# Introduction (/en/integrate/errors/introduction)

XPay surfaces failures in two distinct places: the response body of an API call you made, and the lastPaymentError field on a payment that failed. Pick the surface that matches your problem.

XPay surfaces failures in **two distinct places**, and they're handled with two different patterns. Pick the one that matches what just went wrong:

* An **API call your server made returned a non-2xx**. The error is in the HTTP response body. You handle it where the call happens. → [API errors](/integrate/errors/api-errors).
* **A customer's payment failed**. The error sits on the `lastPaymentError` field of the Payment Intent for the rest of its life. You read it after the payment attempt, in your webhook handler or in the dashboard. → [Payment errors](/integrate/errors/payment-errors).

These two surfaces don't share a code list and don't share a documentation URL namespace. The rest of the Errors group is split along this line.

## The two surfaces at a glance [#the-two-surfaces-at-a-glance]

|                                    | **API errors**                                                           | **Payment errors**                                                                                             |
| ---------------------------------- | ------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------- |
| When you encounter it              | Synchronously, as the response to an API call                            | Asynchronously, on the Payment Intent that failed                                                              |
| Where it lives                     | The HTTP response body: `{ error: { type, code, ... }, request_id }`     | The Payment Intent's `lastPaymentError` field                                                                  |
| What caused it                     | Your request was rejected (validation, auth, missing resource, conflict) | The customer's card was declined, or the processor failed                                                      |
| Who's the audience for the message | Your engineers                                                           | Your customer (sometimes), your support team (always)                                                          |
| Code spaces                        | One: `ApiErrorCode`                                                      | Three: `PaymentErrorCode`, `DeclineCode`, raw network code                                                     |
| Handler shape                      | `try / catch`, branch on `error.type` and `error.code`                   | Read `lastPaymentError`, branch on `adviceCode`, pick customer-safe copy                                       |
| Reference                          | [API error codes](/integrate/errors/api-error-codes)                     | [Payment error codes](/integrate/errors/payment-error-codes), [Decline codes](/integrate/errors/decline-codes) |

## Two things every error gives you [#two-things-every-error-gives-you]

### A `request_id` [#a-request_id]

Every API response (whether 2xx or error) includes a `request_id` like `req_3STkwmFGhGHoO0IX13BRo5iU`. On error responses it's at the top level alongside `error`. Quote it in support tickets, log it next to every error your handler catches, and use it to look up the failing call in [Workbench → Logs](/integrate/workbench/logs-panel) where you can see the full request and response.

A failed payment also has a `request_id` on the underlying API call that triggered it (the `POST /checkout/sessions/.../pay` from the customer's browser). The `lastPaymentError.chargeId` plus the time of failure are usually enough to find that log if you need it.

### A `docUrl` [#a-docurl]

Every error code has a deep link to its row on a code reference page in this docs site:

* API error codes → `https://docs.xpay.app/integrate/errors/api-error-codes#<code>`
* Payment error codes → `https://docs.xpay.app/integrate/errors/payment-error-codes#<code>`
* Decline codes → `https://docs.xpay.app/integrate/errors/decline-codes#<code>`

The dashboard surfaces this link in transaction tooltips and in [Workbench → Logs](/integrate/workbench/logs-panel) so you can click straight from a failed event to its explanation. In your own ops UI, render the `docUrl` as a "Learn more" link next to the error message.

## Pick where to go next [#pick-where-to-go-next]

<Cards>
  <Card icon="<TriangleAlert />" title="API errors" href="/integrate/errors/api-errors">
    The error envelope, the three error types, the handling pattern (try/catch + branch on `type`).
  </Card>

  <Card icon="<Hash />" title="API error codes" href="/integrate/errors/api-error-codes">
    The full list of `error.code` values you can receive on a failed API call, grouped by domain.
  </Card>

  <Card icon="<CreditCard />" title="Payment errors" href="/integrate/errors/payment-errors">
    What `lastPaymentError` looks like, the `adviceCode` that decides what to do next, and how to
    split copy between your customer and your team.
  </Card>

  <Card icon="<ListTree />" title="Payment error codes" href="/integrate/errors/payment-error-codes">
    Every value of `lastPaymentError.code`, with the customer-safe and merchant-facing copy XPay
    returns.
  </Card>

  <Card icon="<Ban />" title="Decline codes" href="/integrate/errors/decline-codes">
    The card issuer's reason for the decline, in `lastPaymentError.declineCode`. Plus the raw
    network code that came back from the card brand.
  </Card>

  <Card icon="<Terminal />" title="Logs panel" href="/integrate/workbench/logs-panel">
    Look up any failure by its `request_id` and see the full request and response.
  </Card>
</Cards>