> ## Documentation Index
> Fetch the complete documentation index at: https://www.dynamic.xyz/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Flow payments API errors

> The HTTP errors each Flow payments endpoint returns, which ones to retry, the 409 codes, and the reasons a quote can fail.

A Flow payments request that the API rejects returns an HTTP error and leaves the flow unchanged. This page lists the errors per endpoint and says which ones are worth retrying. A flow that fails after a request succeeded, for example because a transaction reverted, reports that in its `failure` field instead. [Failures and refunds](https://www.dynamic.xyz/docs/flow/failures-and-refunds.md) covers those.

## Error response

Flow payments endpoints return a JSON body with `error`, a human-readable message, and `code`, a stable identifier, on most errors:

```json theme={"system"}
{
  "error": "Flow cannot be cancelled from state 'broadcasted'",
  "code": "flow_invalid_transition"
}
```

Branch on the status and `code`, not on `error`, because messages can change. These errors have no `code`:

* Validation errors on create and payment-link requests.
* `404` responses from the fee claim endpoints.
* `400` responses for missing or invalid request fields.
* A quote with no route, which returns `quoteFailures` instead, as described in [Quote failures](#quote-failures).

A `401` comes from authentication rather than from the flow, and its body can include other fields.

## Retrying

The safe default is to read the flow, then decide the next request from its current state, as described in [Resume and retry by state](https://www.dynamic.xyz/docs/flow/api.md#resume-and-retry-by-state). Retry the same request only where the tables below say so.

* **`409` responses.** See the [409 codes](#409-codes) below.
* **`5xx` responses.** The request may or may not have taken effect, so read the flow before you retry.
* **The session token.** A `401` on the quote, prepare, broadcast, or cancel endpoints means the session token is missing, revoked, or expired, or that the flow ID is unknown. If the payment has not been sent, attach the source again to get a new token. These endpoints return `401`, not `404`, for a flow ID that does not exist.

## 409 codes

Every `409` from a Flow payments endpoint carries one of these codes:

| `code` | Meaning | Retry |
| - | - | - |
| `flow_invalid_transition` | The flow's current state does not allow this request, for example a cancel after the payment was sent | No. Read the flow and continue from its state |
| `flow_concurrent_update` | Another request changed the flow at the same time | Yes, after reading the flow again |
| `flow_tx_hash_already_recorded` | A flow already recorded this transaction hash | No. Read the flow to see its state |

`flow_tx_hash_already_recorded` does not mean that this flow recorded the hash. Read the flow: if it is `broadcasted` or later, the payment is on its way.

## Server endpoints

### Create a flow or payment link

`POST /server/{environmentId}/flow/{mode}` and `POST /server/{environmentId}/payment-links`

| Status | `code` | Cause | Retry |
| - | - | - | - |
| `400` | None | The mode is not `payment`, `deposit`, or `withdraw`; a required field is missing; `SETTLEMENT_TOKEN` was used outside `payment` mode or with more than one settlement token; or Flow payments is not enabled for the environment | No |
| `401` | None | The API token is missing or invalid, belongs to another environment, or lacks `flow.write` | No. Fix the token |
| `403` | None | A destination address is blocked by [screening](https://www.dynamic.xyz/docs/flow/screening.md). No flow is created | No. Use a different destination |
| `422` | None | Invalid configuration: a fee config that breaks the [fee rules](https://www.dynamic.xyz/docs/flow/fees.md#add-a-fee-config); more than one destination for a chain; a settlement chain without a destination; an unsupported chain or token; the chain name `ETH` instead of `EVM`; or a destination address that is not valid on its chain | No. Fix the request |
| `422` | None | A `uiConfig` URL is not an absolute `http` or `https` URL, or its origin is not a registered CORS origin for the environment | No. Fix the URL or register the origin |
| `422` | None | Payment links only: `baseUrl` is missing, is not an absolute `http` or `https` URL, or its origin is not a registered [CORS origin](https://www.dynamic.xyz/docs/platform/dashboard/security.md#allowed-cors-origin) | No. Fix `baseUrl` or register the origin |

### Read a flow

`GET /server/{environmentId}/flow/{flowId}`

| Status | `code` | Cause | Retry |
| - | - | - | - |
| `401` | None | The API token is missing or invalid, or lacks `flow.write` | No |
| `404` | `not_found` | No flow with this ID exists in the environment | No |

### Cancel a flow

`POST /server/{environmentId}/flow/{flowId}/cancel`

| Status | `code` | Cause | Retry |
| - | - | - | - |
| `401` | None | The API token is missing or invalid, or lacks `flow.write` | No |
| `404` | `not_found` | No flow with this ID exists in the environment | No |
| `409` | `flow_invalid_transition` | The flow is not in `initiated`. The payer cancels later states with the session token | No |
| `409` | `flow_concurrent_update` | Another request changed the flow at the same time | Yes, after reading the flow |

### Fees

The fee balance and claim endpoints return `400` when `recipientAddress` is not a valid EVM address, and `404` without a code when there is nothing to claim or the claim request is unknown. See [Fees API](https://www.dynamic.xyz/docs/flow/fees.md).

## Payer endpoints

### Read a flow and list exchanges

`GET /sdk/{environmentId}/flow/{flowId}` and `GET /sdk/{environmentId}/flow/{flowId}/exchanges`

| Status | `code` | Cause | Retry |
| - | - | - | - |
| `404` | `not_found` | No flow with this ID exists in the environment | No. Check the flow ID and environment ID |

### Attach a source

`POST /sdk/{environmentId}/flow/{flowId}/source`

| Status | `code` | Cause | Retry |
| - | - | - | - |
| `400` | None | A field required for the source type is missing, a field not allowed for it was sent (`fromAddress` on a deposit address, `refundAddress` on a wallet or exchange), or the chain is not supported | No. Fix the request |
| `400` | `exchange_not_supported` | The exchange cannot pay Flow payments | No. Offer another source |
| `400` | `exchange_currency_unsupported` | The exchange does not support this flow's currency | No. Offer another source |
| `403` | `sanctions_blocked` | The wallet is blocked by [screening](https://www.dynamic.xyz/docs/flow/screening.md). The flow's `riskState` is now `blocked` | Only with a different source |
| `403` | `session_token_invalid` | The flow is in `signing`, and the request did not include the current session token | Only with the current session token |
| `404` | `not_found` | No flow with this ID exists in the environment | No |
| `404` | `exchange_not_configured` | The exchange is not enabled for the environment. This is not a missing flow | No. Offer another source |
| `409` | `flow_invalid_transition` | The payment was already sent or a deposit was seen, or the flow is final | No. Read the flow |
| `409` | `flow_concurrent_update` | Another request changed the flow at the same time | Yes, after reading the flow |
| `422` | `exchange_settlement_unsupported` | The flow does not settle in a token and on a network the exchange supports | No. Offer another source |
| `502` | `exchange_unavailable` | The exchange did not respond | Yes, after a short wait |

### Get a quote

`POST /sdk/{environmentId}/flow/{flowId}/quote`

| Status | `code` | Cause | Retry |
| - | - | - | - |
| `400` | `amount_not_editable` | `amount` was sent for a flow that is not in `deposit` mode | No. Remove `amount` |
| `400` | `amount_invalid` | `amount` is malformed, zero, too small, or has more decimals than allowed: two for `USD`, or the token's decimals for `SOURCE_TOKEN` | No. Fix the amount |
| `400` | None | `slippage` is outside `0` to `0.1`, the token cannot be resolved for the chain, or the source has no quote (an exchange source) | No |
| `401` | None | The session token is missing, revoked, or expired, or the flow ID is unknown | After attaching the source again |
| `403` | `risk_blocked` | The flow's `riskState` is `blocked` | Only after attaching a different source |
| `403` | `sanctions_blocked` | In `deposit` mode, the amount changed and the wallet was screened again and blocked | Only after attaching a different source |
| `409` | `flow_invalid_transition` | The flow is not in `source_attached`, `quoted`, or `signing` | No. Read the flow |
| `409` | `flow_concurrent_update` | Another request changed the flow at the same time | Yes, after reading the flow |
| `422` | None | No route can settle the payment. The body includes `quoteFailures` | Depends on the reasons. See [Quote failures](#quote-failures) |
| `422` | None | `disableSwaps` is set and the source does not match a settlement; the token has no price; or deposit addresses are not available for this route, region, or account | No. Offer another token, chain, or source |

### Prepare signing

`POST /sdk/{environmentId}/flow/{flowId}/prepare`

| Status | `code` | Cause | Retry |
| - | - | - | - |
| `401` | None | The session token is missing, revoked, or expired, or the flow ID is unknown | After attaching the source again |
| `409` | `flow_invalid_transition` | The flow is not in `quoted` | No. Read the flow |
| `409` | `flow_concurrent_update` | Another request changed the flow at the same time | Yes, after reading the flow |
| `422` | `quote_expired` | The quote is older than 60 seconds | Yes, after getting a new quote |
| `422` | `insufficient_balance` | The wallet does not hold enough for the payment or for gas | After the payer adds funds or picks another token |
| `422` | None | The flow's `riskState` is `blocked`. It does not change on its own | Only after attaching a different source |

### Report the transaction

`POST /sdk/{environmentId}/flow/{flowId}/broadcast`

| Status | `code` | Cause | Retry |
| - | - | - | - |
| `400` | None | `txHash` is missing | No. Send the hash |
| `401` | None | The session token is missing, revoked, or expired, or the flow ID is unknown | No. Read the flow |
| `409` | `flow_invalid_transition` | The flow is not in `signing`, or its source is a deposit address, which has no broadcast | No. Read the flow |
| `409` | `flow_tx_hash_already_recorded` | A flow already recorded this hash | No. Read the flow |
| `409` | `flow_concurrent_update` | Another request changed the flow at the same time | Yes, after reading the flow |

### Cancel as the payer

`POST /sdk/{environmentId}/flow/{flowId}/cancel`

| Status | `code` | Cause | Retry |
| - | - | - | - |
| `401` | None | The session token is missing, revoked, or expired, or the flow ID is unknown | No |
| `409` | `flow_invalid_transition` | The flow is not in `source_attached`, `quoted`, or `signing`. The payment may already be sent | No. Read the flow |
| `409` | `flow_concurrent_update` | Another request changed the flow at the same time | Yes, after reading the flow |

## Quote failures

When no route can settle the payment, the quote returns `422` with a `quoteFailures` list. Each entry names a settlement token you configured and the reason it could not be quoted:

```json theme={"system"}
{
  "error": "No quotes available for any settlement token.",
  "quoteFailures": [
    { "chainId": "8453", "chainName": "EVM", "symbol": "USDC", "reason": "amount_below_minimum" },
    { "chainId": "728126428", "chainName": "TRON", "symbol": "USDT", "reason": "unknown" }
  ]
}
```

| `reason` | Meaning | What to do |
| - | - | - |
| `amount_below_minimum` | The amount is too small for this route | Use a larger amount or another token |
| `amount_above_maximum` | The amount is too large for this route | Use a smaller amount or another token |
| `insufficient_balance` | The payer's wallet does not hold enough for this route | Pick another token, or add funds |
| `insufficient_liquidity` | The route does not have enough liquidity | Pick another token or chain, or use a smaller amount |
| `unsupported_chain_pair` | No route connects the payer's chain to this settlement | Pick another token or chain |
| `token_not_found` | The token is not recognized | Check the token address |
| `token_price_unavailable` | The token cannot be priced | Pick another token |
| `slippage_too_tight` | The slippage tolerance is too narrow for current conditions | Request the quote again with a higher `slippage` |
| `provider_restricted` | The routing provider will not serve this payment | Pick another token, chain, or source |
| `provider_rate_limited` | The routing provider limited the request rate | Retry after a short wait |
| `provider_unavailable` | The routing provider is temporarily unavailable | Retry after a short wait |
| `unknown` | The provider returned an unclassified error | Try a larger amount or another token |

Treat a reason you do not recognize as `unknown`.

## Related

* [API guide](https://www.dynamic.xyz/docs/flow/api.md) shows each request in context.
* [Failures and refunds](https://www.dynamic.xyz/docs/flow/failures-and-refunds.md) lists the `failure.code` values of a failed flow.
* [Screening](https://www.dynamic.xyz/docs/flow/screening.md) explains blocked results and how the payer recovers.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.