> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.getsinbad.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.getsinbad.com/_mcp/server.

# Errors and recovery

> Check the HTTP status and error.code. Use details for recovery decisions and keep requestId when asking Sinbad to trace a failed call.

## Error body

```json
{
  "error": {
    "code": "CONFLICT",
    "numericId": 1004,
    "message": "Action is unavailable for this role or trade state. Refresh the trade.",
    "requestId": "<request id>",
    "details": { "reason": "INVALID_STATE" }
  }
}
```

`details` is optional. Validation errors may identify `details.fields`. Rate limits send `Retry-After` in seconds. The request ID is also returned in `X-Request-Id`. Browser code may not be able to read response headers that are not CORS-exposed; use the body's request ID and rate-limit details when available.

## Codes and next steps

| HTTP      | Code / numeric ID                                                  | What to do                                                                                                                                                                                                                                                                                                                    |
| --------- | ------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 400       | `VALIDATION_ERROR` / 1000                                          | Fix missing or unknown fields, account schema, decimal precision or body size. Default JSON limit is 16 KB; proof and chat-image uploads have a 5 MB JSON limit.                                                                                                                                                              |
| 401       | `UNAUTHENTICATED` / 1001                                           | Check the credential. For an expired or revoked session, ask your backend to mint a new one for the same customer.                                                                                                                                                                                                            |
| 403       | `FORBIDDEN` / 1002                                                 | Check origin, integration, chain, verification trust and consistency headers. Retrying unchanged is not useful.                                                                                                                                                                                                               |
| 404       | `NOT_FOUND` / 1003                                                 | Check the route and ownership. Another customer's resource is hidden as missing.                                                                                                                                                                                                                                              |
| 409       | `CONFLICT` / 1004                                                  | Read the current state again, then check each possible cause: a uniqueness rule, the fixed wallet and chain, a changed offer or fee, an expired deadline, an open trade, a replaced breakdown, the wallet nonce or an idempotency key bound to a different request.                                                           |
| 400       | `INSUFFICIENT_BALANCE` / 4001                                      | Fund the required USDT or POL. A customer SELL with too little USDT fails on `POST /v1/trade-breakdowns`. Not enough POL for the network fee fails on `POST /v1/transactions` with `details.kind: "NATIVE_GAS"`, `details.chainId` and the required, current and missing amounts in wei and POL.                              |
| 502       | `BLOCKCHAIN_TX_FAILED` / 4002                                      | A funding receipt shows an on-chain failure. Inspect the receipt and read the trade again before you try another attempt.                                                                                                                                                                                                     |
| 409       | `INSUFFICIENT_LIQUIDITY` / 4005                                    | On a customer BUY, the merchant can no longer fund the trade. Returned by `POST /v1/trade-breakdowns`, and by `POST /v1/transactions` when you send signed bytes. Search again after recovering any submitted transaction.                                                                                                    |
| 409       | `CONFLICT` with `details.reason: "ACTIVE_TRADE_EXISTS"`            | The customer already has an active trade on that side, or a breakdown whose escrow transaction was reported or is under review. Raised by `POST /v1/trade-breakdowns`. An unsubmitted breakdown does not cause this: a new idempotency key replaces it with a fresh quote. Show the existing trade, or wait for it to finish. |
| 400       | `VALIDATION_ERROR` with `details.reason: "SELF_TRADE_NOT_ALLOWED"` | The only usable match is the customer's own listing ("You cannot trade with your own wallet."). Raised by `POST /v1/offers/best` and `POST /v1/trade-breakdowns`. Offer a different amount or listing.                                                                                                                        |
| 409       | `PAYMENT_METHOD_ALREADY_EXISTS` / 4008                             | Reuse or reactivate the saved account with the same catalog identity.                                                                                                                                                                                                                                                         |
| 429       | `RATE_LIMITED` / 4004                                              | Wait for `Retry-After` or `details.retryAfter`. Do not retry in a tight loop.                                                                                                                                                                                                                                                 |
| 502 / 503 | `UPSTREAM_ERROR` / 1005                                            | Read the message: chain, RPC or pricing may be temporarily unavailable, or the request may need Sinbad's intervention. Retry only when appropriate.                                                                                                                                                                           |

`UPSTREAM_ERROR` defaults to `502`. Some handlers override it to `503`, including unexpected service failures. Do not assume this code always has one HTTP status.

## Common situations

| Situation                                                                                                                 | Response and next step                                                                                                                                                                                                  |
| ------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| A partner secret call includes an `Origin` header                                                                         | `403 FORBIDDEN`. Make the call from your server.                                                                                                                                                                        |
| The browser origin is not registered, the consistency headers do not match, or the chain is not allowed                   | `403 FORBIDDEN`. Check the integration settings.                                                                                                                                                                        |
| The first session request is missing the name, a verified email address, a wallet or a chain                              | `400 VALIDATION_ERROR`.                                                                                                                                                                                                 |
| A session request reuses another active customer's wallet, email address or phone number                                  | `409 CONFLICT`, with the conflicting fields in `details.fields`.                                                                                                                                                        |
| Your partner account setup is not finished, and you register a new customer or change contact details                     | `502 UPSTREAM_ERROR`. Contact Sinbad and quote the `X-Request-Id`. Do not change customer IDs to work around it.                                                                                                        |
| A later session request changes the registered wallet or chain                                                            | `409 CONFLICT`.                                                                                                                                                                                                         |
| A session request uses a deleted customer ID                                                                              | `401 UNAUTHENTICATED`.                                                                                                                                                                                                  |
| The customer has no active saved account matching the offer's catalog method                                              | `400 VALIDATION_ERROR`, on both BUY and SELL.                                                                                                                                                                           |
| No inventory matches an offer search                                                                                      | `200` with empty `instant` and `alternatives` arrays. A missing or stale currency or an invalid method can instead return `400`, and unavailable pricing can return `UPSTREAM_ERROR`.                                   |
| You modify `breakdownInput`, fees or availability change, the signing window expires, or a newer breakdown replaces yours | `409 CONFLICT`. A selection that no longer exists can return `404`. Search again or create a new breakdown.                                                                                                             |
| A dispute is prepared too early                                                                                           | `409 CONFLICT` with `details.reason: "TOO_EARLY"` and `details.availableAt`. Wait until that time and try again. Other role or state conflicts use `INVALID_STATE`.                                                     |
| Proof already exists, its revision changed, or the trade is closed                                                        | `409 CONFLICT`. Read the trade and the proof again. To change or remove proof, send its current ID as `expectedProofId`. Do not retry different contents against a stale revision. See [Payment proof](/payment-proof). |
| A SELL customer tries to upload or edit proof                                                                             | `403 FORBIDDEN`. Only the fiat payer uploads.                                                                                                                                                                           |
| The current proof or its stored file does not exist                                                                       | `404 NOT_FOUND` on a read.                                                                                                                                                                                              |
| A wallet hash is not yet visible to the network                                                                           | `UPSTREAM_ERROR`. Retry reporting the same hash. Do not send a new transaction.                                                                                                                                         |

New proof and chat files must be PNG or JPEG files of up to 3 MiB. Those limits do not restrict how Sinbad reads merchant files that already exist.

Customer verification is the partner's responsibility, and there is no separate screening request. Location enforcement is not active yet, so an unknown location by itself does not produce an empty offer response.

## Transaction recovery

HTTP `202` means accepted, not confirmed. A timeout or `BROADCAST_UNKNOWN` can still mean a transaction was sent. Keep tracking the same `preparationId` and hash, including after the signing deadline. Follow the recovery steps in [Wallets and gas](/wallets-and-gas) before you create another attempt.

Transaction outcomes such as `REVERTED` or `REVIEW_REQUIRED` are returned in a successful status-read body. Check `transaction.status` as well as the HTTP result. Keep request IDs and transaction IDs for support; never send a private key or partner secret.