Skip to main content

Errors

The Flowlix API uses conventional HTTP response codes and returns structured JSON error objects to help you diagnose and handle problems programmatically.

HTTP status codes

CodeMeaning
200OK — The request succeeded.
201Created — A new resource was created (e.g. a payment).
400Bad Request — The request was malformed or had an invalid parameter.
401Unauthorized — The API key is missing or invalid.
403Forbidden — The API key does not have permission for the requested operation.
404Not Found — The requested resource does not exist.
409Conflict — An idempotency key was reused with a different request body, or another request with the same key is currently in flight.
422Unprocessable Entity — The request was well-formed but could not be completed (synchronous card decline, or a refund that violates business rules).
429Too Many Requests — Rate limit exceeded.
500Internal Server Error — Something went wrong on our end.
503Service Unavailable — Flowlix is temporarily unavailable. Retry with exponential backoff.
201 Created does not mean the card was charged. For Direct API payments and HPP sessions, 201 only means Flowlix accepted the request for asynchronous processing. The terminal outcome is reported on the Payment object’s status field on subsequent GET calls — succeeded, failed, expired, or requires_action. A declined card commonly shows up as 201 followed by a GET returning status: "failed" with a decline_code. The 422 card_error shape is only used in the rarer case where the upstream provider rejects the card synchronously.

Error object structure

All errors follow the same shape:
{
  "error": {
    "type": "invalid_request_error",
    "code": "parameter_invalid",
    "message": "amount must be greater than or equal to 1",
    "param": "amount",
    "decline_code": null,
    "doc_url": "https://docs.flowlix.dev/api-reference/errors",
    "request_id": "req_abc123def456"
  }
}

Error fields

FieldTypeDescription
typestringThe category of error. See error types below.
codestring or nullA machine-readable code identifying the error class. For card errors this is always card_declined; the specific decline reason is in decline_code.
messagestringA human-readable explanation of what went wrong. May contain a bracketed sub-code for refund validation failures (see Refunds).
paramstring or nullThe specific request parameter that caused the error, when the API can identify one. May be null for many error classes.
decline_codestring or nullFor card_error only — the reason the card was declined.
doc_urlstring or nullA link to the relevant documentation page. For declined cards this points to the matching /declines/<code> page.
request_idstringThe unique request ID for support reference.

Error types

TypeDescription
api_errorAn unexpected error on Flowlix’s servers. These are rare and should be retried.
authentication_errorThe API key is missing, invalid, or revoked, or the key does not have permission for the requested action.
card_errorThe card was declined synchronously by the upstream provider. Check decline_code for the specific reason.
idempotency_errorAn idempotency key cannot be honored — see the code field.
invalid_request_errorThe request was malformed, missing a required parameter, had an invalid value, or violated a business rule.
rate_limit_errorToo many requests in a short period. Back off and retry.

Common error codes

CodeTypeHTTPDescription
parameter_missinginvalid_request_error400A required header was not provided (the field-level missing case maps to parameter_invalid).
parameter_invalidinvalid_request_error400A request parameter is missing, malformed, or violates a constraint.
invalid_request_bodyinvalid_request_error400The request body could not be parsed as JSON.
invalid_api_keyauthentication_error401The API key is missing, malformed, or not recognized.
not_permittedauthentication_error403The API key does not have permission for this operation.
resource_missinginvalid_request_error404The requested resource (e.g. payment ID) does not exist.
idempotency_key_in_useidempotency_error409The idempotency key was reused with a different body, or another request with the same key is currently being processed.
duplicate_requestidempotency_error409A duplicate of an already-completed request was detected.
card_declinedcard_error422The card was declined synchronously. The specific reason is in decline_code.
action_not_allowedinvalid_request_error422The operation is not allowed in the current state. Used for refund validation failures (the bracketed sub-code in message distinguishes them — see Refunds).
rate_limit_exceededrate_limit_error429Too many requests. Check the Retry-After header.
internal_errorapi_error500An unexpected internal error occurred.
service_unavailableapi_error503Flowlix or an upstream provider is temporarily unavailable. Retry after a short delay.
Authentication errors (invalid_api_key) return one of three messages depending on the issue:
  • "Missing or malformed Authorization header." — no Authorization header or not using Bearer scheme
  • "Invalid API key format." — key does not start with fl_test_sk_ or fl_live_sk_
  • "Invalid API key provided." — key format is correct but the key is not recognized, revoked, or the merchant validation backend is unreachable
Refund validation failures (such as “amount exceeds remaining” or “transaction is not in a refundable state”) all share the top-level code: "action_not_allowed". The internal validator code is embedded inside message, prefixed with Refund validation failed [<code>]:. See Refunds for the full list and matching examples.

Handling errors in code

Here is a recommended pattern for handling Flowlix API errors:
import requests

response = requests.post(
    "https://api.flowlix.dev/v1/payments",
    headers={"Authorization": "Bearer fl_test_sk_abc123"},
    json={"amount": 4999, "currency": "eur", "card": {...}}
)

if response.status_code == 201:
    payment = response.json()
    # Accepted for processing — poll GET /v1/payments/{payment["id"]}
    # until status is succeeded / failed / expired / requires_action
elif response.status_code == 422:
    error = response.json()["error"]
    if error["type"] == "card_error":
        decline_code = error.get("decline_code")
        # Synchronous card decline — show a message to the customer
    else:
        # Business-rule violation — read error["message"]
        pass
elif response.status_code == 401:
    # Authentication failed — check your API key
    pass
elif response.status_code == 403:
    # API key not permitted for this operation
    pass
elif response.status_code == 409:
    # Idempotency conflict — same key with different body, or
    # another request with the same key is still in flight
    pass
elif response.status_code == 429:
    retry_after = int(response.headers.get("Retry-After", 30))
    # Back off and retry after `retry_after` seconds
elif response.status_code >= 500:
    # Flowlix or upstream error — retry with exponential backoff
    pass
else:
    error = response.json()["error"]
    # Other client error — log and investigate

Decline codes

A card_error response carries the specific reason in decline_code. The same decline codes also appear on the Payment object itself when a card is declined asynchronously (status: "failed", decline_code: "...", with the matching decline_message):
Decline codeMeaning
generic_declineThe card was declined for an unspecified reason.
do_not_honorThe issuer declined without a specific reason.
insufficient_fundsThe card does not have enough funds.
expired_cardThe card has expired.
invalid_numberThe card number is not valid.
lost_cardThe card has been reported lost.
stolen_cardThe card has been reported stolen.
card_velocity_exceededThe card has exceeded its transaction limit.
The decline_code is extracted by the gateway from the upstream provider’s response when the message follows the form Card declined: <code>. If the upstream provider returns a non-card or unstructured failure, decline_code may be null even on a card_error response. See the Decline Codes section for details on each code, including customer-facing messages and merchant actions.

Tips

  • Always check error.type to determine how to handle the error.
  • Log request_id from every error response — you will need it if you contact support.
  • Use decline_code for card errors to show appropriate messages to customers.
  • Retry on api_error and 503 with exponential backoff (start at 1 second, max 30 seconds).
  • Respect Retry-After on rate limit errors instead of guessing a wait time.
  • Match on the bracketed sub-code in the message for refund validation failures, since they all share code: "action_not_allowed".