Transfers and states
A transfer is one request to move value. There are three public kinds, plus one internal kind:
- Mint — fiat arrives from the bank, and tokens are delivered to the organisation's wallet.
- Redeem — tokens arrive from a holder, and fiat is paid out to the organisation's registered bank account.
- On-chain — fiat the organisation already holds is delivered to a registered wallet as tokens. No new supply is created.
- Treasury — an internal top-up of the distribution account's buffer. It is never returned by the public API.
Each transfer is one row with one state. States only move forward. An illegal move is refused with state_conflict, and a state that is terminal never changes again.
The state machine
Every kind follows its own chain of states. Read each row as "from this state, the transfer can move to these states".
| From | Allowed moves |
|---|---|
awaiting_funds | funds_received (mint), tokens_received (redeem) |
funds_received | reserve_check |
reserve_check | submitted (mint and treasury) |
tokens_received | burn_submitted |
burn_submitted | burned |
burned | payout_initiated |
payout_initiated | completed |
created | submitted (on-chain) |
submitted | completed |
The full chains are:
| Kind | Chain |
|---|---|
| Mint | awaiting_funds → funds_received → reserve_check → submitted → completed |
| Redeem | awaiting_funds → tokens_received → burn_submitted → burned → payout_initiated → completed |
| On-chain | created → submitted → completed |
| Treasury | reserve_check → submitted → completed |
From any state that isn't terminal, a transfer can also stop with one of the exceptions: returned, failed, canceled or expired. completed is the normal end; the exceptions are the other ends. A receipt placed in suspense never moves a transfer into a state of its own: the suspense item is a separate record, and attribution funds or resumes the waiting transfer through its normal path.
A mint that fails its reserve check stays in reserve_check with the reason insufficient_reserve_headroom. It waits there until headroom appears or someone cancels it.
For a redeem, the payout happens only after the burn is confirmed. Tokens are destroyed first, then fiat leaves.
One hash per transfer
The public transfer carries a single stellar_tx_hash: the delivery for a mint, the burn for a redeem, or the payment for an on-chain transfer. A treasury top-up behind a mint has its own hash, but the public transfer still reports the delivery.
Working with transfers
POST /v1/transferscreates one. The body is a union onsource.type:fiat(mint),stellar(redeem) orbalance(on-chain).GET /v1/transferslists them, andGET /v1/transfers/{id}reads one.POST /v1/transfers/{id}/cancelstops a transfer before tokens move.GET /v1/transfers/{id}/timelineandGET /v1/transfers/{id}/entriesshow its history and its ledger entries.
Reads hide other organisations' transfers, and treasury transfers, as a 404.