Withdrawals
Request a withdrawal of fiat or crypto to a destination you have already registered, track it, and cancel it while it is still awaiting approval.
A withdrawal request does not move funds. It enters an approval queue, and money leaves only once Zodia Markets Operations has approved and processed the request. Until then you can track the request’s state, and while it is still awaiting approval you can cancel it.
Before you start
Section titled “Before you start”| Base URL | https://trade-uk.sandbox.zodiamarkets.com for the UK sandbox. All paths below are relative to it. Production and AME hosts are on Environments |
| API key and secret | See Signing requests |
| The Move Funds permission on the key | Required by every endpoint here, including the two read-only list endpoints. A key without it gets 403 MOVE FUNDS permission required |
The ROLE_OPERATIONS role on the account group you are transacting for |
See Account groups below |
| A registered destination | An enabled and verified beneficiary bank account for fiat, or an enabled and whitelisted wallet for crypto. Register these before your first withdrawal request; neither can be created as part of one |
The role must be granted as ROLE_OPERATIONS itself. A membership that carries only the individual permissions the role is composed of does not satisfy the check, and owning the account group does not either.
The endpoints
Section titled “The endpoints”| Operation | Method and path |
|---|---|
| Submit a fiat request | POST /api/3/withdrawal/fiat |
| Submit a crypto request | POST /api/3/withdrawal/crypto |
| List fiat requests | POST /api/3/withdrawal/fiat/list |
| List crypto requests | POST /api/3/withdrawal/crypto/list |
| Cancel a fiat request | POST /api/3/withdrawal/fiat/{uuid}/cancel |
| Cancel a crypto request | POST /api/3/withdrawal/crypto/{uuid}/cancel |
All six are POST and all six take a signed JSON body, including the two list endpoints. A read is a POST here because the anti-replay element travels in the signed body, not because it changes anything.
Retrying a submit
Section titled “Retrying a submit”Authentication
Section titled “Authentication”Every endpoint is a POST carrying a signed JSON body, authenticated with two headers:
| Header | Value |
|---|---|
Rest-Key |
Your API key |
Rest-Sign |
Signature over the request, computed with your API secret |
The body always carries an anti-replay element alongside the operation’s own fields: either nonce or tonce. A request with neither is rejected.
noncemust be a whole number and strictly increasing for a given API key. A reused or lower value is rejected. It is tried first when both are present.tonceis the current Unix time in microseconds, and is accepted only within two minutes of server time.
Signature computation is identical to the rest of the v3 API. See Generate Signature. Two points matter especially here:
Authentication failures return 401 with Unauthorized. That covers an unknown key, a bad signature, a missing or rejected nonce, a body that is not parseable JSON, and a throttled caller: rate limiting surfaces as 401 here, not 429.
Account groups
Section titled “Account groups”Every operation is scoped to an account group, the entity whose balances and registered destinations you are acting on.
- To submit or cancel, your key’s user must hold
ROLE_OPERATIONSon theaccountGroupUuidyou name. Otherwise the call returns403 You do not have permission to perform this action, which does not say which of the two gates it failed. - The list endpoints take no account-group parameter, and an
accountGroupUuidsent in the body is ignored. They return requests across every account group where you holdROLE_OPERATIONSand silently omit the rest.
Both lists return requests raised through this API by any member of the account group, not only your own. Requests raised by Zodia Markets Operations on your behalf are not returned.
Request states
Section titled “Request states”The two families report state through different mechanisms and their vocabularies are not the same. Read the one that applies to the request you are looking at.
| State | Meaning | Cancellable |
|---|---|---|
PENDING_APPROVAL |
Submitted, awaiting review by Zodia Markets Operations | Yes |
PENDING |
Approved by Operations, withdrawal in progress | No |
PROCESSED |
Funds sent | No |
CANCELLED |
Cancelled by you, or declined by Operations | No |
Four states, and no FAILED.
Crypto
Section titled “Crypto”| State | Meaning | Cancellable |
|---|---|---|
PENDING_APPROVAL |
Submitted, awaiting acceptance by Zodia Markets Operations | Yes |
PENDING |
Accepted by Operations, withdrawal in progress | No |
PROCESSED |
Funds sent | No |
FAILED |
Defined, but no current processing flow produces it. Tolerate it rather than expect it, and contact your relationship manager if you see it | No |
CANCELLED |
Cancelled by you, or declined by Operations | No |
For both families, PENDING_APPROVAL is the only state a request can be cancelled from, and CANCELLED does not distinguish a cancellation by you from a decision by Operations.
The window closes at different moments. A fiat request needs one operator approval. A crypto request is first accepted into the approval queue and then approved, two operator steps, and the cancel window closes at the first of them.
What Insufficient funds means
Section titled “What Insufficient funds means”A submit is rejected with 422 Insufficient funds when the amount exceeds what is available to withdraw on that currency’s account: your total balance on it, less the withdrawal requests you already have in flight.
- In-flight means requests still awaiting approval or accepted and in progress. Requests that have been processed or cancelled do not count.
- Because in-flight requests are counted, several individually affordable requests can collectively exceed your balance, and the last one is rejected. If you get this unexpectedly, call the matching list endpoint to see what is still outstanding.
- The comparison is not strict against the headroom: an amount exactly equal to it is accepted.
- The figure is the account’s total balance, which includes funds committed elsewhere, not the available balance reported by Get Account Balances.
Errors
Section titled “Errors”Failures return the relevant HTTP status with a two-key body:
{ "success": false, "message": "<reason>" }| Status | Meaning | message examples |
|---|---|---|
401 |
Authentication, signature, nonce/tonce, unparseable body, or throttling |
Unauthorized |
403 |
Invalid request body, missing Move Funds, or no ROLE_OPERATIONS on the account group |
Invalid signed params, MOVE FUNDS permission required, You do not have permission to perform this action |
404 |
Cancel target not found in that account group | Request not found |
409 |
Cancel not allowed in the current state | Request can not be cancelled in current state |
422 |
Business validation failed. See the individual submit pages | Insufficient funds and others |
500 |
Unexpected error. The outcome is undefined, so follow the retry guidance above | Error while processing request |
Validate your fields before sending
Section titled “Validate your fields before sending”A wrong HTTP method is the one failure that does not use this envelope: it returns { "apiVersion": ..., "timestamp": ..., "error": ... } instead. A client that parses only success and message should not assume every error body has them.
Conventions
Section titled “Conventions”Content-Type: application/jsonon every request and response.- Fields with no value are omitted from responses rather than returned as
null. Every field documented on the pages below is therefore optional in the response. - Monetary amounts are returned as strings to preserve exact decimals. Parse them as decimals, not floats.
- Timestamps are ISO-8601 with milliseconds and a zone offset, for example
2026-08-04T10:15:30.451+01:00. - Successful submit and cancel calls return
{ "success": true, "uuid": "..." }. The list endpoints return a bare JSON array, with no envelope. - The
nameandaliasfields insidebeneficiary,bankAccountandwalletare resolved from a separate service. When it is unavailable the list still returns200, but those fields are omitted while theuuidfields remain. A missingnamedoes not mean there is no bank account.
Worked example
Section titled “Worked example”Submit a USD withdrawal, confirm it, then cancel it while it is still awaiting approval.
1. Submit
POST /api/3/withdrawal/fiatRest-Key: <api key>Rest-Sign: <signature over path and body>Content-Type: application/json
{ "tonce": 1770888183656000, "accountGroupUuid": "2073252c-81ed-41be-bf4d-d51b8f2246b8", "ccy": "USD", "amount": 2500.00, "withdrawalMethod": "BANK_WIRE", "bankAccountUuid": "7f3a9c21-4d5e-6f70-8192-a3b4c5d6e7f8", "clientComment": "Monthly settlement"}{ "success": true, "uuid": "e1b2c3d4-5a6b-7c8d-9e0f-1a2b3c4d5e6f" }2. Confirm it is awaiting approval
POST /api/3/withdrawal/fiat/listRest-Key: <api key>Rest-Sign: <signature over path and body>Content-Type: application/json
{ "tonce": 1770888183657000 }[ { "uuid": "e1b2c3d4-5a6b-7c8d-9e0f-1a2b3c4d5e6f", "amount": "2500.00", "ccy": "USD", "state": "PENDING_APPROVAL", "withdrawalMethod": "BANK_WIRE", "bankAccount": { "uuid": "7f3a9c21-4d5e-6f70-8192-a3b4c5d6e7f8", "name": "...", "alias": "..." }, "clientComment": "Monthly settlement", "dateCreated": "2026-08-04T10:15:30.451+01:00", "lastUpdated": "2026-08-04T10:15:30.451+01:00" }]3. Cancel it, with the request uuid in the path and accountGroupUuid in the body:
POST /api/3/withdrawal/fiat/e1b2c3d4-5a6b-7c8d-9e0f-1a2b3c4d5e6f/cancelRest-Key: <api key>Rest-Sign: <signature over path and body>Content-Type: application/json
{ "tonce": 1770888183658000, "accountGroupUuid": "2073252c-81ed-41be-bf4d-d51b8f2246b8" }{ "success": true, "uuid": "e1b2c3d4-5a6b-7c8d-9e0f-1a2b3c4d5e6f" }The request now reports state: CANCELLED.
Related documentation
Section titled “Related documentation”- Get Account Details List - the registered bank accounts and wallets a withdrawal can target
- Get Account Balances - balances per account group
- Get Transaction List - the settled record once a withdrawal has been processed
