Statuses & Lifecycles
Every state machine in one place: what each status means, which are terminal, and which webhook fires on each transition.
Status values are stable API contract, and the same vocabulary is used
in GET responses, in webhook payloads, and throughout these docs. Resources
move forward through their chains. The endorsement is the exception, noted
below: it returns to pending when a change is resubmitted after
issues, to incomplete when something only you can supply is owed, and
it can move from approved back to issues when the review asks for more.
A status marked reserved is part of the vocabulary but not produced today. Handle it in your code so a later release needs no change.
Endorsement
The public verification reading: the review of the customer for one currency, which is the verification. See Verification & Endorsements.
| Status | Terminal | Meaning | Webhook on entry |
|---|---|---|---|
incomplete | no | Not requested, not yet submitted, or something it needs is outstanding. The missing bucket lists what you owe | endorsement.request_for_information when the review hands it back to you; none on creation |
pending | no | Requested and under review. Nothing is owed right now | none |
issues | no | The review asked for more, or something received is not usable. The buckets name it, and a PATCH with the fix resubmits | endorsement.request_for_information, except when the only code is unavailable |
approved | no | The customer can use this currency's features | endorsement.approved |
rejected | yes | Finally refused for this currency | endorsement.rejected |
approved is not permanent. The review's word stands in both directions,
and it can ask for more on an endorsement it approved, which moves it to
issues.
issues with the single code unavailable means we could not reach the
review. No webhook fires, we retry automatically, and you need not act.
Verification round
One submission to one endorsement's review, listed at
GET /api/customers/{customer_id}/verifications. A submit, or a PATCH that
resubmits, opens one round per endorsement it sends, and each round names
its endorsement.
| Status | Terminal | Meaning |
|---|---|---|
submitted | no | Sent for review |
under_review | no | Under review |
approved | yes | This round passed |
request_for_information | yes | This round asked for something. rejection_reasons names it |
rejected | yes | This round refused |
expired | yes | A round that will never be decided: an old one, or one that never reached the review. The retry opens a new round |
Rounds carry no webhooks of their own. Decisions surface on the customer's endorsements, which is the reading to build on.
Customer
| Status | Terminal | Meaning |
|---|---|---|
pending | no | Created and not yet approved |
active | no | Normal |
rejected | yes | Refused |
frozen | no | Set by Rasto. Contact support |
Deposit
| Status | Terminal | Meaning | Webhook |
|---|---|---|---|
detected | no | The bank credit was seen | none |
confirmed | no | Reserved | none |
orchestrating | no | Conversion in progress. Also where a deposit waits when corridor inventory is momentarily short | deposit.received |
completed | yes | Converted and delivered | none |
returned | yes | Sent back to the sender | deposit.returned |
failed | yes | Reserved | none |
on_hold | no | Compliance hold, needs review before it proceeds | deposit.on_hold |
Today returned and on_hold are produced in sandbox, through the
simulation amounts. In live, a credit the bank reverses or holds is
resolved by our team, who contact you.
Conversion
| Status | Terminal | Meaning | Webhook |
|---|---|---|---|
created | no | Opened for a deposit | conversion.created |
routing | no | Route and rate being fixed | none |
executing | no | Executing the conversion | none |
settling | no | Delivery in flight | none |
completed | yes | Fees and FX settled, delivered_amount final | conversion.completed |
failed | yes | Reserved | none |
refunding | no | Reserved | none |
refunded | yes | Reserved | none |
requires_attention | no | Parked for our team's review, for example a delivery that failed on chain. Nothing more moves until it is resolved | none |
Payout
| Status | Terminal | Meaning | Webhook |
|---|---|---|---|
created | no | Delivery prepared | none |
submitted | no | Reserved | none |
completed | yes | Confirmed on chain. tx_hash is the proof. conversion.completed fires alongside it | payout.completed |
failed | yes | Reserved | none |
returned | yes | Reserved | none |
Virtual account
| Status | Terminal | Meaning | Webhook |
|---|---|---|---|
pending | no | Provisioning at the banking rail | none |
active | no | Live. Deposit instructions are valid | virtual_account.activated |
frozen | no | Reserved | none |
closed | yes | Permanently closed | none |
Wallet
| Status | Terminal | Meaning |
|---|---|---|
active | no | Live: receives and sends |
frozen | no | Reserved |
closed | yes | Permanently closed |
A send against a non-active wallet returns 400 wallet_not_active.
Wallet operation
The lifecycle of every send from a wallet, the only way funds leave one.
| Status | Terminal | Meaning | Webhook on entry |
|---|---|---|---|
authorization_required | no | Built, waiting for your signature. Nothing has moved. The authorization and signing objects ride the response only in this state | none |
authorized | no | Your signature was accepted. Execution begins | none |
signing | no | Obtaining the signature from the signing network | none |
signed | no | Signature obtained | none |
submitting | no | Assembling and broadcasting | none |
submitted | no | On chain, awaiting confirmation | none |
completed | yes | Landed. tx_hash is the proof | wallet_operation.completed |
failed | yes | Nothing moved. Creating a replacement is safe | wallet_operation.failed |
expired | yes | Authorize was called after the 30-second window. Nothing moved | none |
returned | yes | Reserved: moved, then reversed on chain. Reconcile before re-sending | none |
An operation does not expire on its own. Past expires_at it still reads
authorization_required, and it moves to expired only when you call
authorize, which returns 400 operation_expired. Treat any operation past
its expires_at as dead and create a new one.
failed and returned are not interchangeable. failed means the money
never moved, so a retry is safe. returned means it moved and came back,
so retrying blindly can send twice.
Not every status has a webhook. A conversion parked in
requires_attention shows only on a GET, so poll any resource that stays
in an intermediate status longer than you expect.