> ## Documentation Index
> Fetch the complete documentation index at: https://docs.moduluslabs.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Errors

> Error envelopes and codes for the Payment Links and Payment Intents APIs

## Overview

The Payment Links and Payment Intents endpoints use different error envelopes. Always branch on the `code`, not on the wording of the `message`, which can change between releases.

For cross-cutting handling (HTTP status ranges, retry and reconciliation rules), see [Errors](/docs/errors).

## Payment Links errors

The Payment Links API returns a nested envelope:

```json theme={null}
{
  "error": {
    "code": "BAD_REQUEST",
    "message": "amount must equal the sum of line items",
    "correlation_id": "b7e2c1a4-9f3d-4c6b-8a21-5e0f7d9c3b18"
  }
}
```

Application errors include `correlation_id` (also returned as the `x-correlation-id` response header). Platform-generated authentication, authorization, and rate-limit errors use the same shape but might omit `correlation_id`.

| Code                     | HTTP | Cause                                                                                                               | What to do                                                                                      |
| ------------------------ | ---- | ------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
| `BAD_REQUEST`            | 400  | A field is missing or invalid (for example `amount` does not equal the line-item sum).                              | Fix the request body and retry.                                                                 |
| `UNAUTHORIZED`           | 401  | Missing or invalid API key.                                                                                         | Send a valid key in `X-API-Key`.                                                                |
| `FORBIDDEN`              | 403  | The key is valid but is not enabled for this operation.                                                             | Use a key enabled for this operation.                                                           |
| `NOT_FOUND`              | 404  | The link does not exist, or it belongs to a different entity than your key.                                         | Confirm the id, and that the link is within your key's entity scope. See the design note below. |
| `IDEMPOTENCY_MISMATCH`   | 409  | The same `Idempotency-Key` was reused with a different request body.                                                | Use a fresh key for a new operation, or resend the identical body to replay.                    |
| `STILL_PROCESSING`       | 409  | A previous request with the same `Idempotency-Key` is still processing.                                             | Wait a moment and retry with the same key.                                                      |
| `INVALID_STATE`          | 409  | The operation is not valid for the link's current state (for example cancelling a link that is no longer `ACTIVE`). | Re-read the link and act on its current `status`.                                               |
| `REQUEST_TOO_LARGE`      | 413  | The request body exceeds the maximum size.                                                                          | Reduce the payload (for example fewer `line_items`).                                            |
| `UNSUPPORTED_MEDIA_TYPE` | 415  | `Content-Type` is not `application/json`.                                                                           | Set `Content-Type: application/json`.                                                           |
| `NO_PAYMENT_METHOD`      | 422  | No eligible payment methods are configured for this branch.                                                         | Contact support to enable a payment method for the branch.                                      |
| `RATE_LIMITED`           | 429  | Too many requests.                                                                                                  | Back off and retry using the rate-limit response headers.                                       |
| `INTERNAL_ERROR`         | 500  | An unexpected error occurred on the Modulus side.                                                                   | Retry with backoff; if it persists, contact support with the `correlation_id`.                  |
| `SERVICE_UNAVAILABLE`    | 503  | The service is temporarily unavailable.                                                                             | Retry with backoff.                                                                             |

<Note>
  **Design note - why cross-tenant reads return `404`, not `403`.** Requesting a link that belongs to another entity returns `404 NOT_FOUND`, the same as an id that does not exist. A `403` would confirm the link exists, leaking information across tenants. Returning `404` gives no such existence oracle.
</Note>

## Payment Intents errors

The Payment Intents API returns a flat envelope:

```json theme={null}
{
  "code": "INVALID_CURRENCY",
  "message": "currency is not supported",
  "request_id": "b7e2c1a4-9f3d-4c6b-8a21-5e0f7d9c3b18"
}
```

| Code                   | HTTP | Cause                                                      | What to do                                                                 |
| ---------------------- | ---- | ---------------------------------------------------------- | -------------------------------------------------------------------------- |
| `INVALID_AMOUNT`       | 400  | `amount` is outside the allowed range (`1` to `99999999`). | Send a valid centavo amount.                                               |
| `INVALID_CURRENCY`     | 400  | Currency is missing or unsupported.                        | Send a supported ISO 4217 code, for example `PHP`.                         |
| `INVALID_EXPIRY`       | 400  | `expires_in_minutes` is outside `5` to `10080`.            | Send a value in range, or omit it for the 1440-minute default.             |
| `INVALID_REQUEST_BODY` | 400  | The request body could not be parsed.                      | Send valid JSON matching the schema.                                       |
| `UNAUTHORIZED`         | 401  | Missing or invalid secret key.                             | Send `Authorization: Bearer sk_...`.                                       |
| `PAYLOAD_TOO_LARGE`    | 413  | The request body exceeds the maximum size.                 | Reduce the payload.                                                        |
| `INTERNAL_ERROR`       | 500  | An unexpected error occurred on the Modulus side.          | Retry with backoff; if it persists, contact support with the `request_id`. |

## Next Steps

<CardGroup cols={2}>
  <Card title="Payment Links" icon="link" href="/docs/ecom/payment-links">
    Create and manage hosted checkout links
  </Card>

  <Card title="JavaScript SDK" icon="js" href="/docs/ecom/jssdk/introduction">
    Take card payments in the browser
  </Card>

  <Card title="Global errors" icon="triangle-exclamation" href="/docs/errors">
    HTTP status ranges and retry rules
  </Card>

  <Card title="Testing" icon="flask" href="/docs/testing">
    Test cards and error scenarios
  </Card>
</CardGroup>
