Diagram
A request that is refused before it is created never enters this machine and locks nothing: it gets a 4xx error, or a201 body with
status: "rejected" and a code (compliance refusal, for example
wallet_banned or aml_blocked), or status: "REJECTED" with an
empty id and the reason in reason (for example insufficient
balance).
States
The status enum also contains
REVERTED. No current transition
produces it; if you see it, treat it as terminal and refunded.
Cancellation rules
Users can cancel while status isPENDING, MLRO_REVIEW, or
CO_SIGNED:
vault:write even though
POST /api/withdrawals/request also accepts the narrower
withdraw:write. A key you create in your account settings with the
Withdraw permission carries only withdraw:write, so it can request a
withdrawal but cannot cancel one.
After SUBMITTED the tx is already in the mempool — cancel is not
possible. Response (409, standard error envelope):
Audit trail
Every status transition is recorded in an append-only event log.GET /api/me/withdrawals/{id}/events (portfolio:read) returns
it, oldest first:
kind is one of created, auto_approved,
flagged_for_review:<reason>, mlro_approved, mlro_rejected,
broadcast, rebroadcast:… / rebroadcast_unstamped:… (status
unchanged, another txHash for the same withdrawal), confirmed,
cancelled, expired:<reason>, broadcast_failed:<error>,
broadcast_blocked:<reason>. Match on the part before the first :.
actor is the user’s wallet (lowercase) for created and cancelled,
system for automatic transitions, mlro:<reviewer> for compliance
decisions, or chain for the on-chain confirmation that flips
SUBMITTED → CONFIRMED. Use this endpoint
for any compliance audit trail — GET /api/me/withdrawals/{id} only
returns the head state.