Admin API
Administrative endpoints for pool, token factory, bridge, and PSP top-up management.
Two trust tiers live under /api/v1/admin/*:
- Platform-operator only (Pool Management, Bridge) — always require
X-Service-Token(or a super_admin/permissioned-AdminUser dashboard session). These touch shared, cross-tenant state — on-chain liquidity pools, the bridge — so a tenant's own credentials never work here, marked 🔒 below. Tenant and asset registry management live outside this page — see Tenants → and Assets →. - Tenant-reachable (Token Factory, PSP Top-up) — a tenant's own
X-API-Key/X-API-Secretworks here too, automatically scoped to that tenant's own tokens/transactions only.X-Service-Tokenand dashboard sessions also still work, for platform-operator cross-tenant access.
The PSP callback is the one exception to both tiers — the PSP itself calls it, verified by signature instead of either credential.
Pool Management
Create Pool
Creates a new Uniswap V2 pair and registers it.
🔒 Admin operation — always requires a Service Token, regardless of the panel above.
curl -X POST "http://localhost:8100/api/v1/admin/pools/create" \
-H "Content-Type: application/json" \
-H "X-Service-Token: YOUR_SERVICE_TOKEN" \
-d '{
"tokenA": "USDT",
"tokenB": "IMC",
"chain": "celo"
}'{
"pair": "0xfE6A7492c21DE2C196B465A814A1FAf9B2564082",
"tokenA": "USDT",
"tokenB": "IMC"
}Fund Pool
Adds liquidity to an existing pool.
🔒 Admin operation — always requires a Service Token, regardless of the panel above.
curl -X POST "http://localhost:8100/api/v1/admin/pools/fund" \
-H "Content-Type: application/json" \
-H "X-Service-Token: YOUR_SERVICE_TOKEN" \
-d '{
"pair": "0xfE6A7492c21DE2C196B465A814A1FAf9B2564082",
"amountA": 500000000,
"amountB": 1000000000,
"chain": "celo"
}'{
"status": "success",
"txHash": "0x...",
"pair": "0xfE6A...4082"
}Reset Pool Ratio
Forces pool reserves to a target ratio.
🔒 Admin operation — always requires a Service Token, regardless of the panel above.
curl -X POST "http://localhost:8100/api/v1/admin/pools/reset" \
-H "Content-Type: application/json" \
-H "X-Service-Token: YOUR_SERVICE_TOKEN" \
-d '{
"pair": "0xfE6A7492c21DE2C196B465A814A1FAf9B2564082",
"reserveA": 1000000000,
"reserveB": 2000000000,
"chain": "celo"
}'{
"status": "success",
"txHash": "0x...",
"pair": "0xfE6A...4082"
}Remove Liquidity
🔒 Admin operation — always requires a Service Token, regardless of the panel above.
curl -X POST "http://localhost:8100/api/v1/admin/pools/remove-liquidity" \
-H "Content-Type: application/json" \
-H "X-Service-Token: YOUR_SERVICE_TOKEN" \
-d '{
"pair": "0xfE6A7492c21DE2C196B465A814A1FAf9B2564082",
"liquidity": 1000000000,
"chain": "celo"
}'{
"status": "success",
"txHash": "0x...",
"pair": "0xfE6A...4082"
}Token Factory
Deploys and manages tenant-owned tokens on-chain, tracked in a MongoDB registry separate from the env-var-configured assets above — use this for tokens a tenant creates at runtime (e.g. a customer's own utility or security token) rather than platform assets like USDT/IMC.
Create Token
Deploys a new token contract via the on-chain factory and registers it.
curl -X POST "http://localhost:8100/api/v1/admin/tokens/create" \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-H "X-API-Secret: YOUR_API_SECRET" \
-d '{
"name": "Acme Rewards",
"symbol": "ACME",
"tokenType": "utility",
"totalSupply": 1000000,
"maxSupply": 10000000,
"chain": "celo"
}'{
"token": {
"id": 4,
"chain": "celo",
"tokenType": "utility",
"contractAddress": "0x...",
"name": "Acme Rewards",
"symbol": "ACME"
},
"onChain": {
"factoryTokenId": 4,
"contract": "0x...",
"chain": "celo"
}
}| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Token name |
symbol | string | Yes | Token symbol |
tokenType | string | Yes | utility or security |
totalSupply | number | No | Initial supply (base units) |
maxSupply | number | No | Hard cap (base units) |
chain | string | No | Defaults to celo. The chain must have a factory contract deployed (FactoryAddress configured), or this returns 400 factory not deployed on this chain. |
backing | string | No | Free-form backing description, e.g. "real-estate" |
valuation | number | No | Off-chain valuation reference |
propertyRef | string | No | Off-chain reference ID (e.g. property/asset record) |
Security token only:
| Field | Type | Description |
|---|---|---|
kycRequired | boolean | Requires KYC before holding the token |
accreditedOnly | boolean | Restricts to accredited holders |
maxHolders | number | Cap on distinct holders |
lockupDays | number | Transfer lockup period |
List Tokens
curl -X GET "http://localhost:8100/api/v1/admin/tokens?chain=celo" \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-H "X-API-Secret: YOUR_API_SECRET"{
"tokens": [
{
"id": 4,
"chain": "celo",
"symbol": "ACME",
"tokenType": "utility"
}
],
"count": 1
}Scoped to the calling tenant. chain is optional — omit it to list across all chains.
Get Token
curl -X GET "http://localhost:8100/api/v1/admin/tokens/4" \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-H "X-API-Secret: YOUR_API_SECRET"{
"token": {
"id": 4,
"chain": "celo",
"symbol": "ACME",
"tokenType": "utility",
"totalSupply": 1000000
}
}Mint Token
curl -X POST "http://localhost:8100/api/v1/admin/tokens/4/mint" \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-H "X-API-Secret: YOUR_API_SECRET" \
-d '{
"to": "0xe3f53fe54f8e1b5e8c86f070e4c9c2c7fa641d0d",
"amount": 5000
}'{
"txHash": "0x...",
"newSupply": 1005000
}Burn Token
curl -X POST "http://localhost:8100/api/v1/admin/tokens/4/burn" \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-H "X-API-Secret: YOUR_API_SECRET" \
-d '{
"from": "0xe3f53fe54f8e1b5e8c86f070e4c9c2c7fa641d0d",
"amount": 5000
}'{
"txHash": "0x...",
"newSupply": 1000000
}Fails with 400 burn amount exceeds total supply if amount is greater than the token's currently tracked supply.
Bridge
Wraps assets from any registered token into a unified YSH representation (1:1, amount in base units in → same amount of YSH out) and back. Requires BridgeAddress configured for the chain (Celo only today) — every bridge endpoint returns 400 bridge not deployed otherwise.
Wrap
curl -X POST "http://localhost:8100/api/v1/admin/bridge/wrap" \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-H "X-API-Secret: YOUR_API_SECRET" \
-d '{
"user": "0xe3f53fe54f8e1b5e8c86f070e4c9c2c7fa641d0d",
"primaryToken": "USDT",
"amount": 5000000
}'{
"txHash": "0x...",
"yshMinted": 5000000
}Unwrap
curl -X POST "http://localhost:8100/api/v1/admin/bridge/unwrap" \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-H "X-API-Secret: YOUR_API_SECRET" \
-d '{
"user": "0xe3f53fe54f8e1b5e8c86f070e4c9c2c7fa641d0d",
"primaryToken": "USDT",
"yshAmount": 5000000
}'{
"txHash": "0x...",
"tokensReleased": 5000000
}Multi-Wrap
Wraps several tokens into YSH in a single transaction — tokens and amounts must be the same length, paired by index.
curl -X POST "http://localhost:8100/api/v1/admin/bridge/multi-wrap" \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-H "X-API-Secret: YOUR_API_SECRET" \
-d '{
"user": "0xe3f53fe54f8e1b5e8c86f070e4c9c2c7fa641d0d",
"tokens": [
"USDT",
"IMC"
],
"amounts": [
3000000,
2000000
]
}'{
"txHash": "0x...",
"totalYSH": 5000000
}Get Wrapped Position
curl -X GET "http://localhost:8100/api/v1/admin/bridge/positions/0xe3f53fe54f8e1b5e8c86f070e4c9c2c7fa641d0d" \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-H "X-API-Secret: YOUR_API_SECRET"{
"user": "0xe3f53fe54f8e1b5e8c86f070e4c9c2c7fa641d0d",
"wrappedYSH": "5000000"
}PSP Top-up
Initiates mobile-money STK push (customer-to-platform) and B2C transfer (platform-to-customer) payments through the configured PSP, and journals the movement automatically. Every endpoint here returns 503 PSP integration not configured unless PSP_API_KEY/PSP_API_SECRET are set — see Configuration →.
STK Push (Deposit)
Prompts the user's phone for a mobile-money PIN and, once paid, credits the tenant's KES treasury.
curl -X POST "http://localhost:8100/api/v1/admin/topup/stk-push" \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-H "X-API-Secret: YOUR_API_SECRET" \
-d '{
"externalUserId": "123",
"phone": "0712345678",
"amount": 500,
"mobileMoneySP": "MPESA",
"externalId": "topup-001",
"displayName": "Acme Wallet"
}'{
"status": "pending",
"message": "Success. Request accepted for processing",
"secureId": "sec_abc123",
"transactionId": "txn_xyz789"
}| Field | Type | Required | Description |
|---|---|---|---|
externalUserId | string | Yes | User the deposit is credited against |
phone | string | Yes | Payer's phone number (local or 254... format) |
amount | number | Yes | Amount in KES, must be > 0 |
mobileMoneySP | string | Yes | Mobile money service provider, e.g. MPESA |
externalId | string | Yes | Your own idempotency key for this transaction |
displayName | string | No | Shown to the payer on the STK prompt |
A journal entry is recorded immediately as pending (debit cash, credit tenant treasury) — the PSP callback updates its status when the PSP confirms.
B2C Transfer (Withdrawal)
Sends money from the tenant's KES treasury out to a phone number. Checks the tenant's ledger balance before calling the PSP — returns 400 insufficient balance rather than attempting a transfer the tenant can't cover.
curl -X POST "http://localhost:8100/api/v1/admin/topup/transfer" \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-H "X-API-Secret: YOUR_API_SECRET" \
-d '{
"externalUserId": "123",
"phone": "0712345678",
"amount": 500,
"mobileMoneySP": "MPESA",
"externalId": "withdraw-001"
}'{
"status": "pending",
"message": "Success. Request accepted for processing",
"secureId": "sec_abc124",
"transactionId": "txn_xyz790"
}Same fields as STK Push (minus displayName).
PSP Callback
The PSP calls this back directly (not through the tenant's own backend) to report the final status of a top-up or transfer. Not X-Service-Token-authenticated like the rest of this page — instead verified via an HMAC signature.
curl -X POST "http://localhost:8100/api/v1/psp/callback?tenant=comet" \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-H "X-API-Secret: YOUR_API_SECRET" \
-d '{
"transactionId": "txn_xyz789",
"externalId": "topup-001",
"status": "completed",
"amount": 500,
"reference": "QGH7XYZ123",
"secureId": "sec_abc123"
}'{
"status": "received"
}| Header | Description |
|---|---|
X-Mamlaka-Signature | HMAC signature over the raw request body, verified against PSP_CALLBACK_SECRET. Requests that fail verification get 401 invalid callback signature. |
The engine looks up the pending transaction by transactionId (falling back to externalId), updates its status (completed/failed/other), and stores the raw callback body for audit. Unrecognized tenants or transactions still return 200 {"status": "ignored", ...} rather than an error — PSPs generally retry non-2xx responses, and there's nothing to retry once the tenant/transaction can't be resolved.
Full worked example
See Examples: Liquidity Pool Setup for a complete walkthrough — register assets, create a pool, fund it, verify reserves.