What this is and who needs it

When something goes wrong, EPD returns a JSON body in a consistent shape with a code your code can switch on. Reading this page once will save you a lot of guessing later.

This page covers server-side REST errors: what your backend gets back from api.epd.com. The EPD Elements browser SDK raises its own client-side errors (card_invalid, card_declined, …) in JavaScript before any REST call is made; those are documented in the Elements guide’s Errors section.

Error response shape

{
  "error": {
    "type": "invalid_request_error",
    "code": "validation_error",
    "message": "One or more fields are invalid.",
    "request_id": "req_a1b2c3d4e5f67890abcdef0123456789",
    "param": "amount",
    "field_errors": [
      { "field": "amount", "code": "invalid_value", "message": "amount must be a positive integer" },
      { "field": "currency", "code": "required_field", "message": "currency is required" }
    ]
  }
}
FieldDescription
typeCoarse category (see the table below).
codeSpecific machine-readable code. Stable across versions; switch on this in code.
messageHuman-readable explanation. May change between versions; do not parse it.
request_idTrace id. Include in any support ticket; it lets us find your call instantly.
paramThe single field most responsible for the error (when applicable).
field_errorsPer-field breakdown for validation errors. Surface these directly in your UI when possible.

Error types

typeStatusWhat it means
invalid_request_error400Malformed request, missing field, invalid value, business rule broken.
authentication_error401Missing, malformed, expired, or revoked key.
authorization_error403Key valid but not allowed to perform this action.
idempotency_error409 / 422Idempotency key reused incorrectly or in flight.
rate_limit_error429Too many requests for the current window.
processing_error4xx / 5xxGateway or internal processing failure. (A card decline is not this: it returns a failed order with a failure_code, see Decline reason codes.)
webhook_errorvariesWebhook delivery, signature, or endpoint configuration error.

Codes you will hit most often

Validation

  • validation_error: top-level catch-all; check field_errors[] for details.
  • required_field: a field was omitted.
  • invalid_value: a field had the wrong type, range, or shape.
  • invalid_format: string did not match the required format (e.g. email).
  • contains_html: a free-text field contained HTML and was rejected for safety.
  • invalid_request with param: card_token: the card_token from EPD Elements is invalid, expired (they live 15 minutes), or already used. A token is single-use: capture a fresh card and attach it again; never resend a spent token. The same code and param are returned when the captured card cannot be read or verified.
  • invalid_request with param: billing_details: the processor requires a billing address before it will vault the card. Resend Add Payment Method with billing_details (or, on Inbound Card Capture, billing_details.address).

Resource state

  • resource_not_found: the id does not exist (or you do not have access to it).
  • resource_already_exists: unique constraint clash.
  • resource_in_use: cannot delete because something still references it.
  • email_already_exists, phone_already_exists, sku_already_exists: narrow variants of resource_already_exists.
  • invalid_state_transition: e.g. canceling an already-canceled subscription.
  • customer_has_active_subscriptions, product_in_active_plans, subscription_not_modifiable, already_canceled.

Authentication & authorization

  • missing_api_key, invalid_api_key, invalid_api_key_format, expired_api_key, revoked_api_key.
  • insufficient_permissions: restricted key lacks the right scope.
  • ip_not_allowed: request came from an IP off the allowlist.
  • environment_mismatch: sandbox-only data with a live key, or vice versa.
  • invalid_api_version, api_version_sunset: bad or retired epd-version header.

Idempotency

  • missing_idempotency_key: endpoint required one and you sent none.
  • invalid_idempotency_key: empty, too long, or wrong characters.
  • request_in_progress: same key still in flight; retry after a short delay.
  • idempotency_key_conflict: same key, different payload. Use a fresh key.

Rate limiting

  • rate_limit_exceeded: per-category limit hit.
  • global_rate_limit_exceeded: global per-merchant limit hit.

Processing & payments

A card decline is not returned as a top-level error code. The charge endpoint returns a failed order carrying a normalized failure_code (processor_declined, insufficient_funds, expired_card, and so on): see Decline reason codes below. The codes here are genuine request/processing errors:

  • gateway_error: temporary downstream failure; safe to retry with the same idempotency key.
  • gateway_delete_failed: could not remove a card from the gateway vault.
  • replacement_required: the customer’s card needs to be re-collected.
  • internal_error: unexpected EPD failure. Safe to retry.
  • service_unavailable: EPD or a dependency is temporarily down.
  • not_implemented: endpoint exists but is not yet enabled (e.g. subscription pause).
  • merchant_not_configured: your account is missing required setup; contact support.
  • customer_not_configured: the customer has no saved payment method to charge. Returned (with param: customer_id) when you create an order or subscription for a customer that has no card on file. Add one first: see Card Vaulting, EPD Elements, or Inbound Card Capture.

Decline reason codes

When a charge is declined, it does not come back as a REST error. The order (or transaction) is returned with status: "failed" and a normalized failure_code, and the POST /v1/orders response is still 201 Created. Branch on failure_code, not on a top-level error code; the accompanying message is a safe human-readable default. (A declined charge is distinct from a validation or vault failure, which does use the error envelope above.) The reasons group by how you should respond:

  • Soft (a later retry may succeed): insufficient_funds, do_not_honor, card_limit_exceeded, processor_declined, issuer_unavailable, unknown
  • Card data (re-collect the card): expired_card, incorrect_cvv, invalid_account
  • Account status (the customer must contact their bank): blocked_card, closed_card, contact_bank, transaction_not_allowed
  • Fraud / security (do not retry): lost_stolen_card, fraud_suspected
  • Configuration / system (not retryable; fix the config or payload): duplicate_transaction, invalid_merchant_configuration, validation_error (on a decline this means the processor rejected a submitted field, distinct from the top-level request-validation validation_error above)

Webhooks

  • webhook_delivery_failed, webhook_endpoint_invalid, webhook_signature_failed.

How to react

On 4xx: fix and re-send

These mean your request was wrong. Surface the message (or the field-level field_errors) to the user; do not blindly retry.

On 429: back off

Honor the Retry-After header. Add jitter. Avoid thundering-herd retries from many workers at once.

On 5xx: retry with the same idempotency key

EPD treats these as transient. Use exponential backoff (e.g. 1s, 2s, 4s, 8s, capped). Reusing the same X-EPD-Idempotency-Key makes the retry safe.

Always log request_id. With it, EPD support can find your exact call in seconds; without it, debugging is much slower.