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

# Errors

The Fluveo `/v1` API uses conventional HTTP status codes and a Stripe-compatible
error envelope. A `2xx` is success; a `4xx` means something about your request
needs fixing; a `5xx` is a problem on Fluveo's side.

## The error envelope

Every error returns JSON with a top-level `error` object:

```json
{
  "error": {
    "type":    "invalid_request_error",
    "code":    "amount_too_small",
    "message": "Amount must be at least 50",
    "param":   "amount"
  }
}
```

| Field     | Meaning                                                          |
| --------- | ---------------------------------------------------------------- |
| `type`    | Broad category — branch your control flow on this.               |
| `code`    | Specific, machine-readable reason (when applicable).             |
| `message` | Human-readable description. Log it; don't show raw to end users. |
| `param`   | The offending request field, when the error is about one input.  |

Public responses use Stripe field names and **never leak internal
connector/provider/acquirer identifiers**.

## Error types

| `type`                  | HTTP | Meaning                                                                                                                                                                                                                                                                                                                                                | What to do                                                                                                                                                                                                    |
| ----------------------- | ---- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `invalid_request_error` | 4xx  | The request was malformed or missing a field.                                                                                                                                                                                                                                                                                                          | Fix the request; `param` names the field.                                                                                                                                                                     |
| `authentication_error`  | 401  | API key is missing, invalid, or revoked.                                                                                                                                                                                                                                                                                                               | Check your secret key — see [authentication](/authentication). A revoked key may keep working for up to 60s.                                                                                                  |
| `card_error`            | 402  | The customer's card was declined or failed a check.                                                                                                                                                                                                                                                                                                    | Surface the decline to the customer; don't blindly retry.                                                                                                                                                     |
| `idempotency_error`     | 400  | An `Idempotency-Key` was reused with different parameters or on another endpoint, where both operations use the [shared response journal](/cookbooks/idempotent-retries). SetupIntent create is outside that journal and has its own resource key scope: the same key can create one PaymentIntent and one SetupIntent for the same merchant and mode. | Replay the original request unchanged. Use a fresh key only for a genuinely different request inside the shared journal; moving from PaymentIntent create to SetupIntent create does not require a fresh key. |
| `rate_limit_error`      | 429  | Too many requests.                                                                                                                                                                                                                                                                                                                                     | Back off and retry with exponential delay.                                                                                                                                                                    |
| `api_error`             | 5xx  | Internal Fluveo error.                                                                                                                                                                                                                                                                                                                                 | Safe to retry with the **same** `Idempotency-Key`.                                                                                                                                                            |

## Common error codes

These `code` values appear most often on the payment path (see the full schemas
in the [API Reference](/api-reference)):

| HTTP | `code`                               | Cause                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| ---- | ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 400  | `parameter_missing`                  | A required field was absent.                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| 400  | `amount_too_small`                   | Amount is below the connector's minimum.                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| 400  | `amount_too_large`                   | Amount is above the connector's maximum, or a refund `amount` is above the Charge's remaining refundable balance (`param: amount`).                                                                                                                                                                                                                                                                                                                                                      |
| 402  | `card_declined`                      | Card issuer refused authorization. Includes a `decline_code`.                                                                                                                                                                                                                                                                                                                                                                                                                            |
| 402  | `expired_card`                       | Card expired before the request landed.                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| 402  | `incorrect_cvc`                      | CVC failed the issuer's check.                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| 403  | `authentication_required`            | A 3DS step is required and no `return_url` was given.                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| 404  | `resource_missing`                   | The object `id` does not exist (or belongs to another merchant — the two are indistinguishable by design).                                                                                                                                                                                                                                                                                                                                                                               |
| 403  | `product_not_activated`              | The route belongs to a Fluveo product that is not activated on the account in the API key's mode, or that the account's region does not have. Today that is Fraud alerts (`GET /v1/radar/early_fraud_warnings` and `/{id}`). An owner or admin activates it on the Fluveo products page, in each mode. Billing routes are never refused.                                                                                                                                                 |
| 503  | `fraud_alerts_unavailable`           | Fraud alerts is active, but this environment has no fraud alerts data configured. `type` is `api_error`, with no `Stripe-Should-Retry`.                                                                                                                                                                                                                                                                                                                                                  |
| 409  | `early_fraud_warning_owner_conflict` | Fluveo's records disagree on which account owns this early fraud warning, so it is not shown. `type` is `api_error`. Contact Fluveo support with its id.                                                                                                                                                                                                                                                                                                                                 |
| 400  | `idempotency_error`                  | `Idempotency-Key` collided with a non-matching request within its key scope; see the [shared-journal and separate SetupIntent create scope](/cookbooks/idempotent-retries).                                                                                                                                                                                                                                                                                                              |
| 400  | `charge_not_settled`                 | The payment is still being recorded; retry the same refund request with the same `Idempotency-Key` shortly (the response carries `Stripe-Should-Retry: true`). If it persists for more than 20 minutes, stop retrying and contact support.                                                                                                                                                                                                                                               |
| 400  | `reconciliation_mismatch_open`       | US Stripe Connect payout or refund: an open balance reconciliation mismatch holds money movements. Current-state findings clear on the next complete full reconciliation run that no longer detects them; windowed findings and unresolved operations remain until their underlying issue is resolved. A payout's refusal is stored and replayed, so use a new `Idempotency-Key` after it clears; a refund's refusal is not stored (`Stripe-Should-Retry: true`), so retry the same key. |
| 400  | `reconciliation_watermark_missing`   | US Stripe Connect payout or refund: the account's balance has not been reconciled yet. Retry the same request with the same `Idempotency-Key` in a few minutes.                                                                                                                                                                                                                                                                                                                          |
| 400  | `reconciliation_watermark_stale`     | US Stripe Connect payout or refund: the account's balance reconciliation is out of date. Retry the same request with the same `Idempotency-Key` in a few minutes.                                                                                                                                                                                                                                                                                                                        |
| 429  | `rate_limit_error`                   | Too many requests; back off.                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| 500  | `api_error`                          | Internal Fluveo error, including an authentication configuration or integrity failure; retry with the same `Idempotency-Key`.                                                                                                                                                                                                                                                                                                                                                            |
| 503  | `api_error`                          | Authentication dependency unavailable, at capacity, or past its deadline. Retry with backoff using the same API key. This authentication refusal has no `code`, `param` or `Retry-After`, and carries `Stripe-Should-Retry: true`.                                                                                                                                                                                                                                                       |

## Decline codes (`decline_code`)

On a `card_declined` response, `decline_code` carries the machine-readable
reason. The common values:

| `decline_code`       | Meaning                                                                 |
| -------------------- | ----------------------------------------------------------------------- |
| `generic_decline`    | The issuer declined without a specific reason.                          |
| `insufficient_funds` | The account lacked funds.                                               |
| `fraudulent`         | The payment was refused by pre-payment antifraud screening (see below). |

When the card issuer declines while a PaymentIntent is being confirmed (create
with `confirm=true`, an update with `confirm=true`, or `/confirm`), the `error`
object also carries `payment_intent`: the PaymentIntent as a retrieve returns
it. Usually its status is `requires_payment_method` and `last_payment_error` is
set. If a held card failed its security-code check and the hold is not yet
released, the status is `processing` and `last_payment_error` is null.

These `402` declines do not carry `payment_intent`: an antifraud refusal, a
card brand the merchant's acquirer does not accept, a decline reported as an
error by the payment processor instead of by the issuer, and the rare case
where the PaymentIntent could not be read back at the time of the decline.
Retrieve the PaymentIntent to see its state.

### Antifraud screening

Card payments are screened for fraud **before** they reach the card network.
If screening refuses a payment, the confirm call returns `402` with
`code: "card_declined"` and `decline_code: "fraudulent"` — the same shape as
any other decline, and the payment never reaches the issuer (no authorization,
no charge). A refusal is **indistinguishable** from a network decline by
design; do not surface "fraud" wording to the shopper.

Notes:

* Retrying the **same** card on the **same** PaymentIntent replays the refusal.
  A shopper may try a **different** card, which is screened fresh.
* The intent stays in `requires_confirmation` after a screening refusal (it is
  not moved to `requires_payment_method`); read the PaymentIntent if you need
  its current state. This is a documented deviation from Stripe's post-decline
  state.
* In the **sandbox**, screening runs against the provider sandbox; refusals are
  deterministic for the provider's designated test cards so you can exercise
  the `402` path end to end.

## Handling errors

A practical pattern:

1. **Branch on `error.type`**, not on `message` text (messages may change).
2. For `card_error`, show the customer a clear, non-technical decline message;
   the `decline_code` tells you *why* but is for your logs, not the cardholder.
3. For `invalid_request_error`, fix the field named in `param` — these are
   programming errors, not runtime conditions.
4. For `rate_limit_error` (429), back off exponentially before retrying.
5. For `api_error` (5xx), retry the **same** request with the **same**
   `Idempotency-Key` so you never double-charge. Idempotent retries are the
   safe way to recover from transient failures — see the
   [quickstart](/quickstart#safe-retries).

For the canonical envelope reference, see the [overview](/overview#errors).