Pilot environment: testnet, simulated yield, mocked fiat rails — read the disclosure
Guides

Tokenised deposits, step by step

Run the vault deposit and withdrawal one step at a time — fiat to tcbUSD, tcbUSD into the vault and back out, through the vault's queue when it cannot pay at once.

Concepts: Intents and the money lifecycle · Vault sub-accounts

A vault deposit runs two steps in one call: it mints tcbUSD from your fiat, then puts that tcbUSD into the vault for shares. A vault withdrawal runs the same two steps in reverse. This page gives you each step as a call of its own.

StepCallMovesScope
MintPOST /token-mintsFiat → the same amount of tcbUSDvault:deposit
SubscribePOST /share-subscriptionstcbUSD → vault sharesvault:deposit
Redeem at oncePOST /share-redemptionsVault shares → tcbUSD, paid nowvault:withdraw
Redeem through the queuePOST /share-redemption-requestsVault shares → tcbUSD, paid when the vault canvault:withdraw
Redeem tcbUSDPOST /token-redemptionstcbUSD → the same amount of fiatvault:withdraw

tcbUSD earns nothing on its own. Only vault shares carry the yield, so tcbUSD you hold between a mint and a subscription earns nothing while it waits.

Every route on this page acts as you, never as a sub-client, and refuses x-on-behalf-of with 400 ON_BEHALF_OF_NOT_ALLOWED_HERE.

Choose the steps or the bundled calls

POST /deposits and POST /withdrawals are unchanged, and they remain the simplest way in and out: one call, one idempotency key, one event, and no tcbUSD left booked to your sub-accounts.

Use the steps when you need one of these:

  • To hold tcbUSD between the steps. Mint now and subscribe later, or subscribe part of what you minted.
  • To redeem when the vault cannot pay at once. A bundled withdrawal is refused with 409 INSUFFICIENT_VAULT_LIQUIDITY. A queued redemption waits in the vault's queue and is paid when the vault can.
  • To keep the redeemed money as tcbUSD. A vault redemption pays tcbUSD, not fiat. Turning it into fiat is a separate POST /token-redemptions, which you call when you choose.

Running the steps means one call, one key and one outcome per step. A failure in one step leaves the earlier steps settled: if a subscription fails after a mint settled, you hold the tcbUSD the mint produced, and nothing unwinds it for you.

The shares a subscription books are the same shares a deposit buys. Both appear on GET /positions.

Where your tcbUSD and shares are held

TCB holds your tcbUSD and your vault shares in the one TCB wallet assigned to you. You hold no keys. What each sub-account holds is a book entry against that wallet, and a sub-account can spend only what is booked to it: a step that names a sub-account is authorised against that sub-account alone, never against the whole wallet.

tcbUSD and shares move only through the steps on this page. No route sends tcbUSD or shares to a wallet outside TCB, and no route moves them from one sub-account to another.

Every step response carries custody.mode TCB_SUBACCOUNT and custody.walletAddress, the address of that wallet.

Every step needs an idempotency key, and a key is not reusable

Send it as the Idempotency-Key header or as the idempotencyKey body field, and replay it on every retry of the same step. Never mint a new key on a retry path: a new key is a second mint, a second subscription or a second redemption. A replay returns the original body with replayed: true. A queued redemption's replay returns the request as it stands now, because the vault moves it on after the first answer. The same key with different parameters is refused with 409 IDEMPOTENCY_KEY_REUSED, and a second call while the first is still running answers 409 IDEMPOTENT_REQUEST_IN_PROGRESS. The key is remembered permanently. The full rule is in Conventions.

Every step answers the same shape

All five steps, their fetches and their lists return one object. Read type to know which step it is.

FieldMeaning
idThe step id. Fetch it again at the same path plus /{id}.
typeTOKEN_MINT, TOKEN_REDEMPTION, SHARE_SUBSCRIPTION, SHARE_REDEMPTION or SHARE_REDEMPTION_REQUEST.
statusWhere the step stands. See the state ladder.
idempotencyKeyThe key you sent, echoed back.
replayedtrue when this body is the stored result of an earlier call under the same key. Money moved once.
subAccountIdThe sub-account the step is booked to.
currencyThe currency of the step.
amountWhat you asked to move, normalised: a submitted "1000.00" comes back as "1000". Fiat on a mint, tcbUSD on every other step.
tokensymbol, address and chain of the token the step moves.
vaultaddress and chain of the vault on the vault steps. null on a mint and a tcbUSD redemption.
custodymode and walletAddress: where the tcbUSD and the shares are held for you.
fiatAccountIdThe fiat account a mint debited or a tcbUSD redemption credited. null on the vault steps.
tokens.movedThe tcbUSD the step moved — minted, burned, put into the vault or paid out of it. On a queued redemption, what has been paid so far. null until the step has moved anything.
shares.movedThe vault shares the step moved, at the vault's own share scale — received by a subscription, burned by a redemption. null on the token steps, and until a vault step has settled.
queueThe queue position and fills of a queued redemption. null on every other step. See the queue.
onchain.txHashThe step's transaction: the mint, the burn, the vault deposit or the vault withdrawal. null until it lands, and on a queued redemption, whose transactions are in queue.
settledAtWhen the step reached a final state. null until it does.
createdAtWhen you submitted the step.
disclosureThe disclosure printed at the top of this page. It is abbreviated in the examples.

Follow the state ladder

GET on the step's /{id} reports the current state.

StatusWhat it meansWhat to do
PENDINGAccepted. Nothing irreversible has happened.Wait or poll.
EXECUTING_ONCHAINThe step is in flight.Wait or poll.
QUEUEDQueued redemptions only. The shares wait in the vault's queue. Parts the vault has paid are already booked to you as tcbUSD.Wait, or cancel.
SETTLEDTerminal. The step is done. On a queued redemption, the request closed with something paid — all of it, or the part paid before you cancelled.Done.
CANCELLEDTerminal. Queued redemptions only. You cancelled before anything was paid, and the shares are available to the sub-account again.Done.
FAILEDTerminal. Nothing moved. Any tcbUSD or shares the step reserved are available again.Safe to retry under a new key.
FAILED_PENDING_COMPENSATIONMoney may have moved and the outcome is not confirmed.TCB resolves it — automatically where the outcome can be proven, by an operator otherwise. Do not retry.
COMPENSATEDTerminal. TCB resolved the compensation.Read the object for the outcome.

A queued redemption never reaches FAILED_PENDING_COMPENSATION. It goes QUEUED once the vault's queue holds it, or FAILED if it never got there.

Never re-key a compensable failure

Every *_FAILED_PENDING_COMPENSATION and *_SETTLEMENT_NOT_RECORDED code, and LEDGER_ENTRY_NOT_RECORDED, means the same thing for your integration: money may have moved, or did. Retrying with a fresh idempotency key starts a second mint of your fiat, or a second irreversible redemption.

TCB resolves these, and nothing resolves them on a timer. Poll the original key or the step id. Quote the step id when you contact TCB.

Mint tcbUSD from fiat

POST /token-mints · scope vault:deposit · idempotency key required

Debits a fiat account you control and mints the same amount of tcbUSD into the TCB wallet assigned to you, booked to the sub-account you name. The mint charges no fee and reserves nothing. Your first mint creates that wallet before anything is charged.

FieldTypeRequiredNotes
currencystring, 3 lettersyesThe fiat currency. Selects the token: USD mints tcbUSD.
amountdecimal stringyesThe fiat to turn into tcbUSD. Never a JSON number. No more fractional digits than the currency settles at — USD settles at 2.
sourceAccountIdUUIDnoThe fiat account to debit. Omitted, your single active account in that currency is used; once you hold more than one, name it — see naming the right account.
subAccountIdUUIDnoThe vault sub-account to book the tcbUSD to. Omitted, your default sub-account is used.
idempotencyKeystring, up to 100 charsnoAlternative to the Idempotency-Key header.
Request
curl -X POST https://api.threecommabank.com/api/partner/v1/token-mints \
  -H "Authorization: Bearer $TCB_API_KEY" \
  -H "Idempotency-Key: acme-mint-2026-10-12-0001" \
  -H "Content-Type: application/json" \
  -d '{
    "currency": "USD",
    "amount": "1000.00",
    "sourceAccountId": "1b9d4f07-6c23-4a8e-b510-9f2c7d3e5a68",
    "subAccountId": "5d3b8e10-2f47-4c9a-b6e8-0a71d4c2f395"
  }'
Response 201
{
  "success": true,
  "data": {
    "id": "7c1e5a90-2b4d-4f86-9e13-5a0d7b2c4e68",
    "type": "TOKEN_MINT",
    "status": "SETTLED",
    "idempotencyKey": "acme-mint-2026-10-12-0001",
    "replayed": false,
    "subAccountId": "5d3b8e10-2f47-4c9a-b6e8-0a71d4c2f395",
    "currency": "USD",
    "amount": "1000",
    "token": {
      "symbol": "tcbUSD",
      "address": "0x5c9E0a7d4F3b21C68e0A9b7D1f2E34c5A6b7C8d9",
      "chain": "ARBITRUM"
    },
    "vault": null,
    "custody": {
      "mode": "TCB_SUBACCOUNT",
      "walletAddress": "0x3B9aC4f27D18E056b1C4a9F30d2E85c7A6b1F094"
    },
    "fiatAccountId": "1b9d4f07-6c23-4a8e-b510-9f2c7d3e5a68",
    "tokens": { "moved": "1000" },
    "shares": { "moved": null },
    "queue": null,
    "onchain": {
      "txHash": "0x9d20b7e548c1f36a0742e9bd35c108af62d497130ba85e6c2f9d40718ac35be2"
    },
    "settledAt": "2026-10-12T10:15:34.000Z",
    "createdAt": "2026-10-12T10:15:28.000Z",
    "disclosure": { "version": "2026-10-12", "summary": "…", "items": ["…"] }
  },
  "timestamp": "2026-10-12T10:15:34.271Z"
}

When TCB has paused issuance of the token, a mint is refused with 409 TOKEN_ISSUANCE_PAUSED and nothing is charged. details.idempotencyKeySpent is false when it was refused up front (the same key stays usable) and true when the pause landed mid-execution (that mint is terminal FAILED; the next attempt needs a new key). Redemptions are unaffected.

Put tcbUSD into the vault

POST /share-subscriptions · scope vault:deposit · idempotency key required

Moves tcbUSD booked to the sub-account into the currency's vault and books the shares it receives to the same sub-account.

FieldTypeRequiredNotes
currencystring, 3 lettersyesSelects the vault and its token: USD puts tcbUSD into the USD vault.
amountdecimal stringyesThe tcbUSD to put in, up to 18 fractional digits.
subAccountIdUUIDnoThe sub-account whose tcbUSD is used and to which the shares are booked. Omitted, your default sub-account is used.
idempotencyKeystring, up to 100 charsnoAlternative to the Idempotency-Key header.

The subscription is authorised against the sub-account's available tcbUSD alone — held minus reserved, as GET /positions/tokens reports it. The check, the claim on your key and a reserve on that tcbUSD are one atomic step, so two concurrent steps can never spend the same tcbUSD. The reserve is released when the subscription settles or fails. A subscription that ends compensable keeps its reserve held until TCB resolves it, and nothing releases a reserve because time passed.

Request
curl -X POST https://api.threecommabank.com/api/partner/v1/share-subscriptions \
  -H "Authorization: Bearer $TCB_API_KEY" \
  -H "Idempotency-Key: acme-subscription-2026-10-12-0001" \
  -H "Content-Type: application/json" \
  -d '{
    "currency": "USD",
    "amount": "1000",
    "subAccountId": "5d3b8e10-2f47-4c9a-b6e8-0a71d4c2f395"
  }'

A settled subscription answers with type SHARE_SUBSCRIPTION, vault set, fiatAccountId null, tokens.moved the tcbUSD put in and shares.moved the shares received, at the vault's own share scale.

Redeem shares for tcbUSD at once

POST /share-redemptions · scope vault:withdraw · idempotency key required

Redeems vault shares booked to the sub-account for the tcbUSD amount you name. The vault burns the shares that amount is worth and pays the tcbUSD at once into the TCB wallet assigned to you, booked to the same sub-account. It pays tcbUSD, not fiat: to receive fiat, follow it with POST /token-redemptions.

FieldTypeRequiredNotes
currencystring, 3 lettersyesSelects the vault and its token.
amountdecimal stringyesThe tcbUSD to take out, up to 18 fractional digits. You never name a number of shares.
subAccountIdUUIDnoThe sub-account whose shares are redeemed and to which the tcbUSD is booked. Omitted, your default sub-account is used.
idempotencyKeystring, up to 100 charsnoAlternative to the Idempotency-Key header.

The redemption is authorised against the sub-account's available shares, the same way a vault withdrawal is, and reserves them while it is in flight.

When the vault cannot pay the amount right now it is refused with 409 INSUFFICIENT_VAULT_LIQUIDITY, and nothing moves. Redeem less, or queue it.

Request
curl -X POST https://api.threecommabank.com/api/partner/v1/share-redemptions \
  -H "Authorization: Bearer $TCB_API_KEY" \
  -H "Idempotency-Key: acme-redemption-2026-10-12-0001" \
  -H "Content-Type: application/json" \
  -d '{
    "currency": "USD",
    "amount": "400",
    "subAccountId": "5d3b8e10-2f47-4c9a-b6e8-0a71d4c2f395"
  }'

A settled redemption answers with type SHARE_REDEMPTION, tokens.moved the tcbUSD paid to you and shares.moved the shares the vault burned.

Queue a redemption when the vault cannot pay now

POST /share-redemption-requests · scope vault:withdraw · idempotency key required

Places shares booked to the sub-account in the vault's redemption queue for the tcbUSD amount you name. The body is the same as an instant redemption: currency, amount (tcbUSD, up to 18 fractional digits), subAccountId and idempotencyKey. A queued redemption ends in tcbUSD exactly as an instant one does: each part the vault pays is booked to the sub-account as tcbUSD, and fiat is a separate POST /token-redemptions.

Request
curl -X POST https://api.threecommabank.com/api/partner/v1/share-redemption-requests \
  -H "Authorization: Bearer $TCB_API_KEY" \
  -H "Idempotency-Key: acme-queued-2026-10-12-0001" \
  -H "Content-Type: application/json" \
  -d '{
    "currency": "USD",
    "amount": "400",
    "subAccountId": "5d3b8e10-2f47-4c9a-b6e8-0a71d4c2f395"
  }'

A request read back after the vault has paid part of it:

Response 200
{
  "success": true,
  "data": {
    "id": "e2a94c07-5d18-4b3f-8a60-1c7f9e2d4b53",
    "type": "SHARE_REDEMPTION_REQUEST",
    "status": "QUEUED",
    "idempotencyKey": "acme-queued-2026-10-12-0001",
    "replayed": false,
    "subAccountId": "5d3b8e10-2f47-4c9a-b6e8-0a71d4c2f395",
    "currency": "USD",
    "amount": "400",
    "token": {
      "symbol": "tcbUSD",
      "address": "0x5c9E0a7d4F3b21C68e0A9b7D1f2E34c5A6b7C8d9",
      "chain": "ARBITRUM"
    },
    "vault": {
      "address": "0x7a2fD3c8e91B4A5d06E7c1F2b38A9d4C5e6f0B12",
      "chain": "ARBITRUM"
    },
    "custody": {
      "mode": "TCB_SUBACCOUNT",
      "walletAddress": "0x3B9aC4f27D18E056b1C4a9F30d2E85c7A6b1F094"
    },
    "fiatAccountId": null,
    "tokens": { "moved": "150" },
    "shares": { "moved": null },
    "queue": {
      "onchainRequestId": "42",
      "claimableAt": "2026-10-13T10:20:00.000Z",
      "sharesEscrowed": "398.120000000000000000000000",
      "requestTxHash": "0x2b7e90c4d15a3f86e0721c9bd48a5f3e60d1972c8b4af05e3d6c19a7f8e2b041",
      "cancelTxHash": null,
      "fills": [
        {
          "txHash": "0x8c41f7a20d93b65e1c08f4a2d79e3b5c60a1d84f2b97c3e05a6d1f8b4c2e7a90",
          "tokens": "150",
          "bookedAt": "2026-10-13T11:04:12.000Z"
        }
      ]
    },
    "onchain": { "txHash": null },
    "settledAt": null,
    "createdAt": "2026-10-12T10:20:00.000Z",
    "disclosure": { "version": "2026-10-12", "summary": "…", "items": ["…"] }
  },
  "timestamp": "2026-10-13T11:10:00.118Z"
}
queue fieldMeaning
onchainRequestIdThe request's id in the vault's queue. null until the vault confirms the request.
claimableAtThe earliest time the vault may pay the request: its notice period. null until the vault confirms the request. The vault pays when it has the liquidity, which can be later than this.
sharesEscrowedThe shares held in the vault's queue for this request, at the vault's own share scale.
requestTxHashThe transaction that placed the request in the queue.
cancelTxHashThe transaction that cancelled it, when you cancelled it.
fillsWhat the vault has paid, part by part: txHash, tokens (the tcbUSD that part paid) and bookedAt (when it was booked to your sub-account, null while it is being booked).

How a request moves:

  • While it is QUEUED, the shares it covers stay reserved on the sub-account, so GET /positions shows them under reserved and no other step can spend them. Each part the vault pays is booked to the sub-account as tcbUSD by a TCB process that runs every few minutes, so a paid part appears in fills and on GET /positions/tokens within minutes of being paid. tokens.moved is what has been paid so far.
  • It ends SETTLED once the vault has paid all of it, or once you cancel after part of it was paid. The paid part leaves the sub-account's shares, and any unpaid shares are released back to it.
  • It ends CANCELLED when you cancel before anything was paid. The reserve is released and the shares are available again.
  • It ends FAILED when the request never reached the vault's queue. Nothing moved and the shares are available again. The create call answers 500 SHARE_REDEMPTION_REQUEST_FAILED; a new key is safe.

A request waits in QUEUED for as long as the vault takes. Nothing closes it because time passed.

Cancel a queued redemption

POST /share-redemption-requests/{id}/cancel · scope vault:withdraw · no body, no idempotency key

Takes the request out of the vault's queue. Shares not yet paid return to your sub-account; anything already paid stays paid. It answers 200 with the request in its new state: CANCELLED when nothing had been paid, SETTLED for the part that was. If the vault has taken the cancel but the request is not closed yet, the answer is QUEUED with queue.cancelTxHash set. The request closes on its own within minutes, and calling cancel again in the meantime gives the same answer.

Request
curl -X POST https://api.threecommabank.com/api/partner/v1/share-redemption-requests/e2a94c07-5d18-4b3f-8a60-1c7f9e2d4b53/cancel \
  -H "Authorization: Bearer $TCB_API_KEY"
StatusCodeWhat to do
404SHARE_REDEMPTION_REQUEST_NOT_FOUNDNo queued redemption with that id in your scope.
409SHARE_REDEMPTION_REQUEST_NOT_CONFIRMEDThe request is not on the vault's queue yet. Nothing was cancelled. Retry shortly.
409SHARE_REDEMPTION_REQUEST_NOT_CANCELLABLEThe request is no longer waiting: it was paid, cancelled or failed. Read it for its outcome.
503GAS_PREFLIGHT_FAILEDNothing was cancelled. Retry.

Redeem tcbUSD to fiat

POST /token-redemptions · scope vault:withdraw · idempotency key required

Burns tcbUSD booked to the sub-account you name and credits the same amount of fiat to a fiat account you control.

FieldTypeRequiredNotes
currencystring, 3 lettersyesThe fiat currency to credit. Selects the token.
amountdecimal stringyesThe tcbUSD to burn; the same amount of fiat is credited. No more fractional digits than the currency settles at — USD settles at 2 — because an account cannot be credited a fraction of a cent.
destinationAccountIdUUIDnoThe fiat account to credit. Omitted, your single active account in that currency is used; once you hold more than one, name it.
subAccountIdUUIDnoThe sub-account whose tcbUSD is burned. Omitted, your default sub-account is used.
idempotencyKeystring, up to 100 charsnoAlternative to the Idempotency-Key header.

The redemption is authorised against the sub-account's available tcbUSD alone, and the tcbUSD is reserved while it is in flight. If the burn fails with nothing burned, the reserve is released and the tcbUSD is available again. If its outcome is not proven, the tcbUSD stays reserved until TCB resolves it.

Request
curl -X POST https://api.threecommabank.com/api/partner/v1/token-redemptions \
  -H "Authorization: Bearer $TCB_API_KEY" \
  -H "Idempotency-Key: acme-token-redemption-2026-10-12-0001" \
  -H "Content-Type: application/json" \
  -d '{
    "currency": "USD",
    "amount": "250.00",
    "destinationAccountId": "1b9d4f07-6c23-4a8e-b510-9f2c7d3e5a68",
    "subAccountId": "5d3b8e10-2f47-4c9a-b6e8-0a71d4c2f395"
  }'

A settled redemption answers with type TOKEN_REDEMPTION, vault null, fiatAccountId the account credited and tokens.moved the tcbUSD burned.

Fetch and list the steps

Scope transaction:read on every route below.

Fetch oneList
GET /token-mints/{id}GET /token-mints
GET /token-redemptions/{id}GET /token-redemptions
GET /share-subscriptions/{id}GET /share-subscriptions
GET /share-redemptions/{id}GET /share-redemptions
GET /share-redemption-requests/{id}GET /share-redemption-requests

A fetch returns the step in its current state, including a terminal failure, which is reported as the object rather than as an error. It is the poll surface the compensable errors point at, and the fetch half of the thin webhook payloads. A queued redemption is read with its place in the queue and what has been paid, from the vault. An id that is not yours, or that belongs to a different step, answers 404 INTENT_NOT_FOUND — 404 SHARE_REDEMPTION_REQUEST_NOT_FOUND on the queued-redemption routes.

A list returns your steps of that kind, newest first, one page at a time, each row in the same shape as a fetch.

QueryRequiredNotes
statusnoOnly steps in this status: PENDING, EXECUTING_ONCHAIN, QUEUED, SETTLED, CANCELLED, FAILED, FAILED_PENDING_COMPENSATION or COMPENSATED.
cursornoThe nextCursor from the previous page. Omit it for the first page. Treat it as opaque.
limitnoRows per page, 1-100. Defaults to 25.
Request
curl -G https://api.threecommabank.com/api/partner/v1/share-redemption-requests \
  -H "Authorization: Bearer $TCB_API_KEY" \
  --data-urlencode "status=QUEUED" \
  --data-urlencode "limit=25"

The page is items and nextCursor, which is null on the last page. A malformed cursor, limit or status is refused with 400 INVALID_AMOUNT.

Read your tcbUSD

GET /positions/tokens · scope position:read

The tcbUSD booked to each sub-account — held, reserved by steps in flight, and available — with the totals and the balance of the TCB wallet assigned to you. The totals can show walletTokens above held: a part of a queued redemption the vault has paid but that is not yet booked, and fractions of a cent left by a fiat withdrawal, sit in the wallet until they are booked. Every response carries yieldNote: tcbUSD earns nothing on its own, and only vault shares carry the yield. The fields and an example are in Positions. Your vault shares are on GET /positions.

Money caps

TCB may set ceilings on your account's money movement, per currency and per flow, the same four kinds as on vault deposits and withdrawals. The steps count against them like this:

StepCounted againstRefused with
POST /token-mintsIts own tcbUSD-mint cap400 TOKEN_MINT_CAP_EXCEEDED
POST /token-redemptionsIts own tcbUSD-redemption cap400 TOKEN_REDEMPTION_CAP_EXCEEDED
POST /share-subscriptionsYour deposit cap400 DEPOSIT_CAP_EXCEEDED
POST /share-redemptionsYour withdrawal cap400 WITHDRAWAL_CAP_EXCEEDED
POST /share-redemption-requestsYour withdrawal cap400 WITHDRAWAL_CAP_EXCEEDED

A vault subscription moves money into the vault exactly as a deposit does, and a vault redemption takes it out exactly as a withdrawal does, so they share those caps: a deposit cap bounds POST /deposits and POST /share-subscriptions together. The maxOutstanding ceiling counts subscriptions as money in and settled vault redemptions as money out.

No ceiling applies unless TCB has configured one for your account; there is no API to read or change them. A refused request moved nothing: details.cap names the ceiling, details.limit its value, and details.attempted what the figure would have become. The idempotency key stays usable — resubmit the same key when the window rolls over, or with a smaller amount. The daily and monthly figures count requests as soon as they are accepted; only a step that failed or was cancelled with nothing moved stops counting.

Handle the errors

Nothing has moved on any 4xx, or on a 503, unless the row says otherwise.

StatusCodeStepsWhat to do
400IDEMPOTENCY_KEY_REQUIRED / IDEMPOTENCY_KEY_INVALIDallSend a key, in one place, within the allowed characters and length.
400INVALID_AMOUNTallamount was not a positive decimal string, or a list query was malformed.
400AMOUNT_PRECISION_EXCEEDEDmint, tcbUSD redemptionRound to details.allowedDecimals and resubmit.
400INSUFFICIENT_BALANCEmintThe source fiat account does not hold the amount.
400INSUFFICIENT_SUB_ACCOUNT_TOKENStcbUSD redemption, subscriptionThe named sub-account's available tcbUSD does not cover it. Name the sub-account that holds it, wait for the in-flight step, or move less.
400INSUFFICIENT_SUB_ACCOUNT_POSITIONboth vault redemptionsThe named sub-account's available shares do not cover it.
400INSUFFICIENT_VAULT_POSITIONboth vault redemptionsYour wallet does not hold enough shares to redeem that amount.
400TOKEN_MINT_CAP_EXCEEDED / TOKEN_REDEMPTION_CAP_EXCEEDED / DEPOSIT_CAP_EXCEEDED / WITHDRAWAL_CAP_EXCEEDEDsee Money capsResubmit the same key when it clears, or send less.
400 or 403SOURCE_ACCOUNT_NOT_USABLE / DESTINATION_ACCOUNT_NOT_USABLEmint, tcbUSD redemptionThe named account is not a fiat account in that currency (400) or is not active (403). Name another account.
403WALLET_NOT_ALLOWLISTEDsubscriptionYour wallet is not yet cleared for this vault. Contact TCB.
403INSUFFICIENT_SCOPEallThe key lacks vault:deposit, vault:withdraw, transaction:read or position:read.
404VAULT_NOT_AVAILABLEvault stepsNo active vault for that currency.
404SOURCE_ACCOUNT_NOT_FOUND / DESTINATION_ACCOUNT_NOT_FOUNDmint, tcbUSD redemptionThe named account is not one of yours.
404SUB_ACCOUNT_NOT_FOUNDallThe subAccountId is not one of yours.
404INTENT_NOT_FOUND / SHARE_REDEMPTION_REQUEST_NOT_FOUNDfetches, cancelNo step with that id in your scope.
409SOURCE_ACCOUNT_AMBIGUOUS / DESTINATION_ACCOUNT_AMBIGUOUSmint, tcbUSD redemptionName the account explicitly.
409FIAT_ACCOUNT_NOT_PROVISIONEDmint, tcbUSD redemptionThe named (or implied) fiat account is not yet set up to be debited or credited by this step. Name another account, or contact TCB.
409TOKEN_CONFIG_NOT_AVAILABLEallThe currency is not provisioned for this flow. Contact TCB.
409TOKEN_ISSUANCE_PAUSEDmintTCB has paused issuance of the token. Nothing was charged. Read details.idempotencyKeySpent — see the mint.
409VAULT_SUBSCRIPTIONS_CLOSEDsubscriptionThe vault is not accepting subscriptions right now.
409VAULT_TOKEN_MISMATCHvault stepsThe vault is built on a different token than the active tcbUSD. Contact TCB.
409INSUFFICIENT_VAULT_LIQUIDITYinstant vault redemptionThe vault cannot pay that much now. Redeem less, or queue it.
409STEP_AUTHORIZATION_CONFLICTredemptions, subscriptionAnother step on the same sub-account was authorised at the same moment. Nothing moved — retry with the same key.
409PARTNER_RAIL_FROZENtcbUSD redemption, vault stepsReconciliation found an unexplained difference on your tcbUSD or your vault position, and the steps that draw on it are suspended. A tcbUSD freeze does not suspend payouts. Nothing moved. A person lifts it; contact TCB.
409IDEMPOTENCY_KEY_REUSEDallSame key, different parameters. Use a new key.
409IDEMPOTENT_REQUEST_IN_PROGRESSallThe first call under this key is still running. Retry the same key.
409SHARE_REDEMPTION_REQUEST_NOT_CONFIRMED / SHARE_REDEMPTION_REQUEST_NOT_CANCELLABLEcancelSee Cancel a queued redemption.
500TOKEN_MINT_FAILEDmintNothing was charged. Safe to retry under a new key.
500TOKEN_REDEMPTION_FAILEDtcbUSD redemptionNothing was burned and the tcbUSD is available again. Safe to retry under a new key.
500SHARE_SUBSCRIPTION_FAILED / SHARE_REDEMPTION_FAILEDsubscription, instant redemptionThe vault call was refused before anything reached the vault. The tcbUSD or shares are available again. Safe to retry under a new key.
500SHARE_REDEMPTION_REQUEST_FAILEDqueued redemptionThe request never reached the vault's queue and the shares are available again. Safe to retry under a new key.
500TOKEN_MINT_FAILED_PENDING_COMPENSATION / TOKEN_REDEMPTION_FAILED_PENDING_COMPENSATION / SHARE_SUBSCRIPTION_FAILED_PENDING_COMPENSATION / SHARE_REDEMPTION_FAILED_PENDING_COMPENSATIONthe matching stepMoney may have moved. Any reserve stays held until TCB resolves it. Do not re-key — see the callout above.
500TOKEN_MINT_SETTLEMENT_NOT_RECORDED / TOKEN_REDEMPTION_SETTLEMENT_NOT_RECORDED / SHARE_SUBSCRIPTION_SETTLEMENT_NOT_RECORDED / SHARE_REDEMPTION_SETTLEMENT_NOT_RECORDEDthe matching stepThe step completed and only its record failed. Do not re-key.
500LEDGER_ENTRY_NOT_RECORDEDsubscription, instant vault redemptionThe money completed but the exact share movement could not be read back, so no ledger entry was posted. Do not re-key; TCB retries the read and posts the entry, or an operator resolves it.
503CHAIN_UNAVAILABLE / GAS_PREFLIGHT_FAILED / TOKEN_CONFIG_CHECK_FAILEDallNothing moved. Retry.
503PARTNER_WALLET_PROVISIONING_FAILEDmintYour first mint creates your custody wallet, and that could not complete just now. Nothing was charged and your idempotency key was not claimed: resend with the same key or a new one, with the same or changed parameters.

The full catalogue is in Errors.

Receive the events

Subscribe at POST /webhooks to any of these. Each payload is thin: it carries the event type and a resource pointer, and you fetch the step to read its state.

EventFires whenFetch
token_mint.settled / token_mint.failedA mint settled, or failed with nothing chargedGET /token-mints/{id}
token_redemption.settled / token_redemption.failedA tcbUSD redemption settled, or failed with nothing burnedGET /token-redemptions/{id}
share_subscription.settled / share_subscription.failedA subscription settled, or failed with nothing sent to the vaultGET /share-subscriptions/{id}
share_redemption.settled / share_redemption.failedAn instant vault redemption settled, or failed with nothing redeemedGET /share-redemptions/{id}
share_redemption_request.queuedA queued redemption is in the vault's queueGET /share-redemption-requests/{id}
share_redemption_request.settledA queued redemption closed with something paidGET /share-redemption-requests/{id}
share_redemption_request.cancelledA queued redemption was cancelled before anything was paidGET /share-redemption-requests/{id}
share_redemption_request.failedA queued redemption never reached the vault's queueGET /share-redemption-requests/{id}

resource.type is token_mint, token_redemption, share_subscription, share_redemption or share_redemption_request. A part the vault pays on a queued redemption fires no event of its own; read queue.fills. A compensable outcome fires no event — see which webhook fires when.

The machine-readable specification for these endpoints is openapi.partner.v1.json. Where it and this page disagree, the specification is authoritative.

Pilot disclosure

On this page