Admin API
Administrative endpoints for tenant, asset, pool, token factory, bridge, and PSP top-up management. All admin endpoints require X-Service-Token authentication (except the PSP callback, which the PSP itself calls and which is verified by signature instead).
WARNING
These endpoints are for platform administration only. Tenant operators should use API Key/Secret authentication.
Tenant Management
Create Tenant
curl -X POST "http://localhost:8100/api/v1/admin/tenants" \
-H "Content-Type: application/json" \
-H "X-Service-Token: YOUR_TOKEN_HERE" \
-d '{"name": "new-tenant", "api_key": "tenant-key-001", "api_secret": "tenant-secret-001"}'{ "tenantId": "tenant-001", "name": "new-tenant", "api_key": "tenant-key-001", "created_at": "2026-08-30T12:00:00Z" }List Tenants
curl -X GET "http://localhost:8100/api/v1/admin/tenants" \
-H "Content-Type: application/json" \
-H "X-Service-Token: YOUR_TOKEN_HERE"{ "tenants": [{ "tenantId": "comet", "name": "FusionFi", "created_at": "2026-08-01T00:00:00Z" }], "count": 1 }Rotate Tenant Secret
curl -X POST "http://localhost:8100/api/v1/admin/tenants/comet/rotate-secret" \
-H "Content-Type: application/json" \
-H "X-Service-Token: YOUR_TOKEN_HERE"{ "tenantId": "comet", "new_api_secret": "new-secret-123", "rotated_at": "2026-08-30T12:00:00Z" }Asset Management
Register Asset
Registers a new token in the engine's asset registry, on any configured chain — EVM or Stellar.
curl -X POST "http://localhost:8100/api/v1/admin/assets/IMC/register?chain=celo" \
-H "Content-Type: application/json" \
-H "X-Service-Token: YOUR_TOKEN_HERE" \
-d '{"contract": "0xF150E30B1C7Ab81938d650567D1454Ee0132d5A2", "decimals": 6, "name": "ImpalaCoin", "type": "airtime", "backing": "airtime"}'{ "message": "asset IMC registered on celo", "asset": { "symbol": "IMC", "contract": "0xF150...d5A2" } }| Field | Type | Required | Description |
|---|---|---|---|
contract | string | Yes | ERC-20 contract address on EVM chains; the asset's issuer account (G...) on Stellar |
decimals | number | Yes | Token decimals (6 for USDT/USDC/IMC; Stellar's native XLM is always 7, but registered Stellar assets use whatever you set here) |
name | string | Yes | Human-readable token name |
type | string | No | airtime, stablecoin, or utility |
backing | string | No | airtime or none |
Full request/response detail, including a Stellar example, is in the Assets API →.
Deregister Asset
curl -X DELETE "http://localhost:8100/api/v1/admin/assets/mytoken?chain=celo" \
-H "Content-Type: application/json" \
-H "X-Service-Token: YOUR_TOKEN_HERE"{ "message": "asset mytoken deregistered from celo" }Pool Management
Create Pool
Creates a new Uniswap V2 pair and registers it.
curl -X POST "http://localhost:8100/api/v1/admin/pools/create" \
-H "Content-Type: application/json" \
-H "X-Service-Token: YOUR_TOKEN_HERE" \
-d '{"tokenA": "USDT", "tokenB": "IMC", "chain": "celo"}'{ "pair": "0xfE6A7492c21DE2C196B465A814A1FAf9B2564082", "tokenA": "USDT", "tokenB": "IMC" }Fund Pool
Adds liquidity to an existing pool.
curl -X POST "http://localhost:8100/api/v1/admin/pools/fund" \
-H "Content-Type: application/json" \
-H "X-Service-Token: YOUR_TOKEN_HERE" \
-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.
curl -X POST "http://localhost:8100/api/v1/admin/pools/reset" \
-H "Content-Type: application/json" \
-H "X-Service-Token: YOUR_TOKEN_HERE" \
-d '{"pair": "0xfE6A7492c21DE2C196B465A814A1FAf9B2564082", "reserveA": 1000000000, "reserveB": 2000000000, "chain": "celo"}'{ "status": "success", "txHash": "0x...", "pair": "0xfE6A...4082" }Remove Liquidity
curl -X POST "http://localhost:8100/api/v1/admin/pools/remove-liquidity" \
-H "Content-Type: application/json" \
-H "X-Service-Token: YOUR_TOKEN_HERE" \
-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/tokens/create" \
-H "Content-Type: application/json" \
-H "X-Service-Token: YOUR_TOKEN_HERE" \
-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/tokens?chain=celo" \
-H "Content-Type: application/json" \
-H "X-Service-Token: YOUR_TOKEN_HERE"{ "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/tokens/4" \
-H "Content-Type: application/json" \
-H "X-Service-Token: YOUR_TOKEN_HERE"{ "token": { "id": 4, "chain": "celo", "symbol": "ACME", "tokenType": "utility", "totalSupply": 1000000 } }Mint Token
curl -X POST "http://localhost:8100/api/v1/tokens/4/mint" \
-H "Content-Type: application/json" \
-H "X-Service-Token: YOUR_TOKEN_HERE" \
-d '{"to": "0xe3f53fe54f8e1b5e8c86f070e4c9c2c7fa641d0d", "amount": 5000}'{ "txHash": "0x...", "newSupply": 1005000 }Burn Token
curl -X POST "http://localhost:8100/api/v1/tokens/4/burn" \
-H "Content-Type: application/json" \
-H "X-Service-Token: YOUR_TOKEN_HERE" \
-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/bridge/wrap" \
-H "Content-Type: application/json" \
-H "X-Service-Token: YOUR_TOKEN_HERE" \
-d '{"user": "0xe3f53fe54f8e1b5e8c86f070e4c9c2c7fa641d0d", "primaryToken": "USDT", "amount": 5000000}'{ "txHash": "0x...", "yshMinted": 5000000 }Unwrap
curl -X POST "http://localhost:8100/api/v1/bridge/unwrap" \
-H "Content-Type: application/json" \
-H "X-Service-Token: YOUR_TOKEN_HERE" \
-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/bridge/multi-wrap" \
-H "Content-Type: application/json" \
-H "X-Service-Token: YOUR_TOKEN_HERE" \
-d '{"user": "0xe3f53fe54f8e1b5e8c86f070e4c9c2c7fa641d0d", "tokens": ["USDT", "IMC"], "amounts": [3000000, 2000000]}'{ "txHash": "0x...", "totalYSH": 5000000 }Get Wrapped Position
curl -X GET "http://localhost:8100/api/v1/bridge/positions/0xe3f53fe54f8e1b5e8c86f070e4c9c2c7fa641d0d" \
-H "Content-Type: application/json" \
-H "X-Service-Token: YOUR_TOKEN_HERE"{ "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-Service-Token: YOUR_TOKEN_HERE" \
-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-Service-Token: YOUR_TOKEN_HERE" \
-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-Service-Token: YOUR_TOKEN_HERE" \
-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.
End-to-End Example
Deploy and Fund a New Pool
# 1. Register both assets
curl -X POST http://localhost:8100/api/v1/admin/assets/USDT/register?chain=celo \
-H "X-Service-Token: YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"contract": "0x3C8EB6c8DF02B43a7bae71314917aCBdd60edDDF", "decimals": 6, "name": "Tether USD"}'
curl -X POST http://localhost:8100/api/v1/admin/assets/IMC/register?chain=celo \
-H "X-Service-Token: YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"contract": "0xF150E30B1C7Ab81938d650567D1454Ee0132d5A2", "decimals": 6, "name": "ImpalaCoin", "type": "airtime", "backing": "airtime"}'
# 2. Create pool
curl -X POST http://localhost:8100/api/v1/admin/pools/create \
-H "X-Service-Token: YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"tokenA": "USDT", "tokenB": "IMC", "chain": "celo"}'
# => {"pair": "0xfE6A...4082"}
# 3. Fund pool with 500 USDT and 1000 IMC
curl -X POST http://localhost:8100/api/v1/admin/pools/fund \
-H "X-Service-Token: YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"pair": "0xfE6A7492c21DE2C196B465A814A1FAf9B2564082", "amountA": 500000000, "amountB": 1000000000, "chain": "celo"}'
# 4. Verify pool exists
curl "http://localhost:8100/api/v1/swap/pools" \
-H "X-Service-Token: YOUR_TOKEN"