Home » Free ACP Payments Module » Error Codes

ACP Error Codes Reference: Every Code the Module Returns

Every error the ACP Payment Module returns is one flat JSON object per the ACP spec: a type, a machine-readable code, and a human-readable message. Nothing else, no stack traces, no nested detail objects, no internal identifiers. This page is the complete reference: each code with its HTTP status, what triggers it, what a platform client should do about it, and how to provoke each one deliberately in tests.

The Error Envelope

The type field classifies the failure into the four buckets the spec defines: invalid_request for anything the caller can fix, processing_error for payment and downstream failures, service_unavailable for temporary conditions, and rate_limit_exceeded for throttling. The code is the field to branch on programmatically, it is stable and specific, while message is for logs and, where appropriate, for what the assistant tells the shopper. A typical decline looks like this on the wire:

{
  "type": "processing_error",
  "code": "payment_declined",
  "message": "Card was declined: insufficient_funds"
}

The Full Code Table

HTTP  code                    when it happens                        retry?
401   (unauthorized)          missing or wrong bearer token,         fix credentials
                              or failed request signature
400   missing_api_version     API-Version header absent              fix request
415   (unsupported media)     POST body not application/json         fix request
400   (missing key)           POST without an Idempotency-Key        fix request
422   idempotency_conflict    same key reused with different body    new key
409   idempotency_in_flight   first request with key still running   wait Retry-After
404   not_found               unknown route under the prefix         fix URL
404   missing                 session id does not exist              fix id
405   not_cancelable          cancel called on a completed session   do not retry
400   invalid                 malformed input, frozen session        fix request
                              update, or missing payment token
400   out_of_stock            an item is no longer available         update the cart
400   payment_declined        the charge was declined                shopper action
400   requires_3ds            issuer requires authentication         run 3DS, retry
500   internal_error          unexpected server-side failure         retry, same key

Request-Level Errors

The 401 family is configuration, not code. Every request needs Authorization: Bearer matching the configured token, and the endpoints are closed by default, no token configured means every request is rejected, opening the API is an explicit act. If HMAC signing is configured, a missing or wrong Signature is also a 401, and the check fails closed, omitting the header does not bypass it. The missing_api_version 400 means the API-Version header was absent entirely, the module requires the header but tolerates adjacent spec snapshots while the protocol is in beta. The 415 fires when a POST body is not application/json. All of these are covered end to end in the endpoints reference and the security model.

Idempotency Errors

Every POST requires an Idempotency-Key, and two codes police its use. A 422 idempotency_conflict means the same key arrived with a different body, which is almost always a client bug about to become a double purchase, the correct response is to generate a fresh key for what is genuinely a new request, never to strip the key. A 409 idempotency_in_flight means the first request under that key is still processing, the response carries a Retry-After hint, and the platform should simply wait and resend the identical request. The happy path of the same machinery is invisible: an exact replay returns the stored response with an Idempotent-Replayed: true header and side effects happen exactly once.

One subtlety rewards attention: server-side 5xx responses are not stored against the key. A transient failure does not poison the key, so the correct retry after a 500 is the same request with the same key, which gets a fresh attempt rather than a replayed error.

Session-State Errors

The missing 404 is a session id that does not exist, distinct from not_found, which is a route that does not exist. The invalid 400 covers malformed input and every illegal state transition: updating a session frozen by a live hosted payment, completing with no payment token, completing an empty session. The 405 not_cancelable fires only when canceling a session that already completed, cancel is otherwise allowed from any state. The out_of_stock code appears in two strengths: as a warning message attached to a session when re-pricing dropped an unavailable item, so the platform can tell the shopper what changed, and as a hard 400 when a request depends on an item that cannot be sold. Session states and the re-pricing rule behind these are described in the endpoints reference.

Payment Errors

Two codes carry the entire payment surface. A payment_declined 400 wraps any decline, with Stripe's decline reason preserved in the message, the session stays ready_for_payment so the shopper can try another method, and nothing was charged. A requires_3ds 400 means the issuer demanded authentication that a server-to-server call cannot perform, the platform runs the challenge with the shopper and retries. Both are documented in depth, including the exact Stripe mechanics behind them, in the Shared Payment Tokens guide and payment modes. Anything unexpected from the provider or the module itself becomes a 500 internal_error whose details are logged server-side and never leaked to the caller.

Retry Guidance in One Paragraph

Branch on the code, not the status. Fix-and-resend errors (missing_api_version, invalid, 415, missing key) need a corrected request. idempotency_in_flight means wait and resend identically. idempotency_conflict means mint a new key. internal_error means retry with the same key, the 5xx was not stored. payment_declined and requires_3ds are shopper-level outcomes, not transport failures, surface them in the conversation. And not_cancelable is final, a completed session stays completed.

Testing tip: every code above can be provoked without a Stripe account. The mock provider's token conventions (tok_decline, tok_3ds) produce the two payment errors, a reused key with an edited body produces the 422, two concurrent sends of the same request produce the 409, and an update after a mock hosted payment starts produces the frozen-session invalid. The quickstart gets a local server running for this in under a minute.