Skip to navigation

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:

{
"error": {
"type": "invalid_request_error",
"code": "amount_too_small",
"message": "Amount must be at least 50",
"param": "amount"
}
}
FieldMeaning
typeBroad category — branch your control flow on this.
codeSpecific, machine-readable reason (when applicable).
messageHuman-readable description. Log it; don’t show raw to end users.
paramThe 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

typeHTTPMeaningWhat to do
invalid_request_error4xxThe request was malformed or missing a field.Fix the request; param names the field.
authentication_error401API key is missing, invalid, or revoked.Check your secret key — see authentication. A revoked key may keep working for up to 60s.
card_error402The customer’s card was declined or failed a check.Surface the decline to the customer; don’t blindly retry.
idempotency_error400An Idempotency-Key was reused with different parameters or on another endpoint, where both operations use the shared response journal. 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_error429Too many requests.Back off and retry with exponential delay.
api_error5xxInternal 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):

HTTPcodeCause
400parameter_missingA required field was absent.
400amount_too_smallAmount is below the connector’s minimum.
400amount_too_largeAmount is above the connector’s maximum, or a refund amount is above the Charge’s remaining refundable balance (param: amount).
402card_declinedCard issuer refused authorization. Includes a decline_code.
402expired_cardCard expired before the request landed.
402incorrect_cvcCVC failed the issuer’s check.
403authentication_requiredA 3DS step is required and no return_url was given.
404resource_missingThe object id does not exist (or belongs to another merchant — the two are indistinguishable by design).
403product_not_activatedThe 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.
503fraud_alerts_unavailableFraud alerts is active, but this environment has no fraud alerts data configured. type is api_error, with no Stripe-Should-Retry.
409early_fraud_warning_owner_conflictFluveo’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.
400idempotency_errorIdempotency-Key collided with a non-matching request within its key scope; see the shared-journal and separate SetupIntent create scope.
400charge_not_settledThe 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.
400reconciliation_mismatch_openUS 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.
400reconciliation_watermark_missingUS 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.
400reconciliation_watermark_staleUS 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.
429rate_limit_errorToo many requests; back off.
500api_errorInternal Fluveo error, including an authentication configuration or integrity failure; retry with the same Idempotency-Key.
503api_errorAuthentication 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_codeMeaning
generic_declineThe issuer declined without a specific reason.
insufficient_fundsThe account lacked funds.
fraudulentThe 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.

For the canonical envelope reference, see the overview.