Assets API
Token operations on any configured chain. These endpoints work for any registered asset — replace :symbol with USDT, USDC, IMC, or any custom token — on any registered chain, EVM or Stellar, via ?chain=.
List Assets
curl -X GET "http://localhost:8100/api/v1/assets" \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-H "X-API-Secret: YOUR_API_SECRET"{
"assets": [
{
"Symbol": "USDT",
"Contract": "0x3C8E...DDDF",
"Decimals": 6,
"chain": "celo"
},
{
"Symbol": "IMC",
"Contract": "0xF150...d5A2",
"Decimals": 6,
"chain": "celo"
}
],
"count": 2
}List Chains
curl -X GET "http://localhost:8100/api/v1/chains" \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-H "X-API-Secret: YOUR_API_SECRET"{ "chains": [{ "name": "celo", "chainId": 11142220, "assets": [...] }, { "name": "stellar", "assets": [...] }], "count": 2 }stellar only appears once STELLAR_RELAYER_SEED is configured — see Setting Up Stellar →. Absent that, this instance only ever returns celo (and whatever other EVM chains are configured).
Token Balance
curl -X GET "http://localhost:8100/api/v1/assets/imc/balance?externalUserId=123&tenantSlug=comet" \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-H "X-API-Secret: YOUR_API_SECRET"{
"symbol": "IMC",
"chain": "celo",
"balance": "447000000",
"decimals": 6,
"humanAmount": 447
}Query Parameters:
| Param | Type | Required | Description |
|---|---|---|---|
externalUserId | string | Yes | Wallet owner (your own user ID) |
tenantSlug | string | No | Tenant (defaults to "comet") |
chain | string | No | Chain (defaults to "celo") — pass stellar for a Stellar-family balance |
Stellar decimals are fixed at 7
Native XLM balances are always 7 decimals — that's a Stellar protocol constant, not a per-asset setting like EVM's decimals config. Registered Stellar assets (non-native) use whatever decimals they were registered with, same as EVM.
Mint Tokens
Mints tokens directly to a user's custodial wallet using the relayer key.
IMC can't be minted here
IMC is backed by airtime and is only minted by booking an airtime top-up (POST /admin/imc/airtime-topup, see Airtime), into your IMC treasury. Every other flow moves IMC that already exists: IMM cycles, distributions, swaps and vault funding. A mint request for IMC on any other endpoint is refused.
curl -X POST "http://localhost:8100/api/v1/assets/ctest/mint" \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-H "X-API-Secret: YOUR_API_SECRET" \
-d '{
"externalUserId": "123",
"amountBase": 10000000,
"externalId": "mint-001"
}'{
"amount": 10,
"chain": "celo",
"symbol": "IMC",
"txHash": "0x..."
}Request Fields:
| Field | Type | Required | Description |
|---|---|---|---|
externalUserId | string | Yes | Target user |
amountBase | number | Yes | Amount in base units (6 decimals: 1000000 = 1.0 token) |
externalId | string | Yes | Idempotency key |
Backing-aware accounting
Mints are journaled with type-aware backing. Airtime-backed assets (type: airtime) debit the airtime inventory account; all other assets debit the tenant treasury.
Stellar issues, it doesn't deploy
On Stellar, minting an asset means the relayer account (which must be that asset's registered issuer, per Stellar's protocol) makes a payment of a newly-issued amount — there's no token contract to call, unlike EVM's mint(). The response shape is identical either way; only what happens on-chain differs.
Send Tokens
Transfers tokens from a user's wallet to a recipient.
curl -X POST "http://localhost:8100/api/v1/assets/imc/send" \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-H "X-API-Secret: YOUR_API_SECRET" \
-d '{
"externalUserId": "123",
"to": "0xe3f53fe54f8e1b5e8c86f070e4c9c2c7fa641d0d",
"amountBase": "5000000"
}'{
"txHash": "0x...",
"chain": "celo",
"symbol": "IMC",
"amount": 5
}Request Fields:
| Field | Type | Required | Description |
|---|---|---|---|
externalUserId | string | Yes | Sender |
to | string | Yes | Recipient address — an EVM 0x... address or a Stellar G... address, matching chain |
amountBase | string | Yes | Amount in base units |
On Stellar, sending to a recipient without an existing trustline for that asset establishes one automatically first (the engine signs for both sides when it custodies the recipient's wallet too).
Approve Spender
Grants an ERC-20 allowance over the user's tokens.
curl -X POST "http://localhost:8100/api/v1/assets/usdt/approve" \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-H "X-API-Secret: YOUR_API_SECRET" \
-d '{
"externalUserId": "123",
"spender": "0xSpenderAddress",
"amountBase": "1000000"
}'{
"txHash": "0x...",
"chain": "celo",
"symbol": "USDT"
}If spender is omitted, defaults to the platform relayer address.
EVM only
Stellar has no allowance/approval concept — ?chain=stellar (or any non-EVM chain) returns 501 asset approval is not yet supported on "stellar" chains. Use Burn (Holder) instead, which needs no prior approval on any chain.
Burn (Relayer)
Burns tokens from a user who has approved the relayer. Used for swap-back flows.
curl -X POST "http://localhost:8100/api/v1/assets/usdt/burn" \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-H "X-API-Secret: YOUR_API_SECRET" \
-d '{
"externalUserId": "123",
"amountBase": "5000000",
"externalId": "burn-001"
}'{
"txHash": "0x...",
"chain": "celo",
"symbol": "USDT",
"amount": 5
}EVM only
Same allowance dependency as Approve above — ?chain=stellar returns 501 burn-from-holder is not yet supported on "stellar" chains — use /assets/usdt/burn-by-holder instead. Use Burn (Holder) on Stellar.
Burn (Holder)
Burns tokens held by the user's own custodial wallet using the user's sealed key — no prior allowance required. Used when redeeming tokens for real-world value (e.g. burning IMC when airtime is delivered).
curl -X POST "http://localhost:8100/api/v1/assets/imc/burn-by-holder" \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-H "X-API-Secret: YOUR_API_SECRET" \
-d '{
"externalUserId": "123",
"amountBase": 3000000,
"externalId": "burn-001"
}'{
"txHash": "0x...",
"chain": "celo",
"symbol": "IMC",
"amount": 3
}Request Fields:
| Field | Type | Required | Description |
|---|---|---|---|
externalUserId | string | Yes | User whose wallet holds the tokens |
amountBase | number | Yes | Amount in base units |
externalId | string | Yes | Idempotency key |
Backing-aware accounting
For airtime-backed assets, the burn credits the tenant's airtime inventory account — keeping on-chain supply pegged to real float.
This is the only burn endpoint that works on both EVM and Stellar chains — the recommended default unless you specifically need relayer-initiated burns on an EVM chain.
Admin Asset Management
Register New Asset
🔒 Admin operation — always requires a Service Token, regardless of the panel above.
curl -X POST "http://localhost:8100/api/v1/admin/assets/IMC/register?chain=celo" \
-H "Content-Type: application/json" \
-H "X-Service-Token: YOUR_SERVICE_TOKEN" \
-d '{
"contract": "0x1D1aA006f66A191fc04e7d1cab8D62B0c81d9221",
"decimals": 6,
"name": "ImpalaCoin",
"type": "airtime",
"backing": "airtime"
}'{
"message": "asset IMC registered on celo",
"asset": {
"symbol": "IMC",
"contract": "0xF150...d5A2"
}
}On Stellar, contract is the asset's issuer account address (G...), not a contract address — Stellar assets are identified by (asset code, issuer), not a deployed contract.
Deregister Asset
🔒 Admin operation — always requires a Service Token, regardless of the panel above.
curl -X DELETE "http://localhost:8100/api/v1/admin/assets/mytoken?chain=celo" \
-H "Content-Type: application/json" \
-H "X-Service-Token: YOUR_SERVICE_TOKEN"{
"message": "asset mytoken deregistered from celo"
}