Where errors show up

MCP responses come back as JSON-RPC result objects with a content array. When a tool call fails, the result still includes structured error info: usually as a JSON object inside a text content block.

{
  "content": [
    {
      "type": "text",
      "text": "{\"error\":{\"type\":\"invalid_request_error\",\"code\":\"resource_not_found\",\"message\":\"Customer cus_xxx not found.\",\"request_id\":\"req_...\"}}"
    }
  ],
  "isError": true
}

The inner error object follows EPD’s standard REST error shape: same fields, same codes. Read REST errors for the full reference; everything there applies here.

Common error codes by category

CategoryCodes
Authenticationmissing_api_key, invalid_api_key, expired_api_key, revoked_api_key
Authorizationinsufficient_permissions, ip_not_allowed, environment_mismatch
Validationvalidation_error, required_field, invalid_value, invalid_format
Stateresource_not_found, invalid_state_transition, already_canceled
Idempotencyrequest_in_progress, idempotency_key_conflict
Rate limitingrate_limit_exceeded, global_rate_limit_exceeded
Paymentgateway_error, customer_not_configured, merchant_not_configured
MCP-specificnot_implemented (e.g. pause_subscription returns this until the feature ships)

A declined charge is not an error. create_order, create_subscription, and a charge retry (retry_order / retry_failed_charge) return a status: "failed" order carrying a normalized failure_code (for example processor_declined, insufficient_funds, expired_card, incorrect_cvv). Branch on failure_code, not on an error code. (retry_order also retries a failed refund, which comes back as a refund_failed order rather than a charge failure.) The full set is in Decline reason codes.

How an agent should react

Validation errors: surface to user

validation_error and friends mean the tool call was malformed. Return the message + field_errors to the user; do not retry.

Authentication / authorization: stop

invalid_api_key, insufficient_permissions, ip_not_allowed are configuration problems. Stop and notify the operator; retrying will not fix them.

Rate limit: back off

rate_limit_exceeded or global_rate_limit_exceeded: pause for a few seconds with jitter, then retry the same call.

Gateway / internal: retry idempotently

gateway_error, internal_error, service_unavailable are transient. Retry with the same idempotency_key.

Card declined: let the user choose

A declined charge comes back as a failed order with a failure_code (processor_declined, insufficient_funds, expired_card, and so on). It needs a human decision: collect a different card or wait. Do not silently retry.

Where to find request_id

Every error includes a request_id. When opening a support ticket about an MCP call, paste it; it lets support trace your exact call instantly.

"request_id": "req_a1b2c3d4e5f67890abcdef0123456789"