> For the complete index of the 402pay docs, see [llms.txt](https://developer.402pay.co/llms.txt).

# Create a checkout

Start an attempt at paying a link or payment, and reserve a fresh address for crypto.

`POST https://dash.402pay.co/api/v1/checkouts`

Public, so a page you build can call it from the browser. Requires an `Idempotency-Key`, and allows 30 checkouts per 10 minutes from one IP address.

Each call is one attempt: a crypto checkout reserves a fresh address in the business's wallet and locks the rate, and when `email` is known it creates a `pending` payment right away. Card availability and limits depend on the amount and customer location. Customers review the total and complete any required identity checks on the secure payment page.

Called with your secret key, the response also has `email`, `reporting` and `fee_rate_bps`, as described on [retrieve a checkout](https://developer.402pay.co/api/checkouts/retrieve.md).

### Body

- `payment_code` (string): The code at the end of a payment's `url`. Send this or `link_code`.
- `link_code` (string): The code at the end of a link's `url`.
- `rail` (string): `crypto` or `card`. Defaults to `crypto`.
- `asset` (string): For crypto: `USDC`, `USDT`, `BTC`, `ETH` or `SOL`.
- `network` (string): For crypto: a network the business accepts the asset on, such as `polygon`.
- `email` (string): The customer's email, which you see on the payment. Required for cards, and when the business asks for it.
- `country` (string): The customer's two-letter country code, used to determine card-payment availability.
- `replaces` (string): The ID of the checkout this one replaces, when the customer goes back to choose again. The same choice returns the same quote and address, and a different one carries a link's pending payment over. [Supersede](https://developer.402pay.co/api/checkouts/supersede.md) the old checkout when the customer steps back: it stops being the quote on screen, but funds already on the way still count and can still be claimed.
- `simulate` (string): For testing only: how the transfer arrives, `exact`, `underpaid`, `overpaid` or `wrong_network`. `late` returns 400 here; it's only reachable through [simulating a transfer](https://developer.402pay.co/api/checkouts/mark-sent.md). See [testing](https://developer.402pay.co/testing.md#simulate).

### Errors

- 400 `invalid_request` A field is missing or invalid, such as a `simulate` value it doesn't take.
- 400 `idempotency_key_required` The request has no `Idempotency-Key` header.
- 400 `email_required` The rail or the business needs the customer's email, and none was sent.
- 400 `invalid_email` `email` isn't a valid email address.
- 400 `rail_unavailable` The business doesn't accept this rail.
- 400 `asset_unavailable` The business doesn't accept this coin on this network.
- 400 `amount_out_of_range` The price is outside the available card-payment limits.
- 403 `customer_blocked` The business has blocked the customer with this email.
- 403 `business_suspended` 402pay suspended the business, so it can't take new payments.
- 403 `test_mode_unavailable` A test key can't open a checkout on a live business, so send none, as a customer's browser does, or a live key.
- 404 `link_not_found` No active link has this code.
- 404 `payment_not_found` No payment has this code.
- 409 `not_accepting` The business isn't taking payments yet.
- 409 `payment_paid` Funds already arrived for the payment: it succeeded, or it's `underpaid` or `needs_review`.
- 409 `payment_canceled` The business canceled the payment.
- 410 `payment_expired` The payment passed its `expires_at`.
- 429 `rate_limited` Too many checkouts from one IP address. Wait for Retry-After seconds.
- 503 `temporarily_unavailable` 402pay paused new checkouts, card payments, or payments on this network for now. Try again later.

Any request can also fail on its key or its body. See [errors](https://developer.402pay.co/api/errors.md).

Request, cURL:

```bash
curl -X POST "https://dash.402pay.co/api/v1/checkouts" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "payment_code": "2jrsrcxv7k",
    "rail": "crypto",
    "asset": "USDC",
    "network": "polygon",
    "email": "harper.wilson@example.com"
  }'
```

Request, Node.js:

```js
const response = await fetch("https://dash.402pay.co/api/v1/checkouts", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Idempotency-Key": crypto.randomUUID(),
  },
  body: JSON.stringify({
    payment_code: "2jrsrcxv7k",
    rail: "crypto",
    asset: "USDC",
    network: "polygon",
    email: "harper.wilson@example.com"
  }),
});
const { data } = await response.json();
```

Request, Python:

```python
import uuid

import requests

response = requests.post(
    "https://dash.402pay.co/api/v1/checkouts",
    headers={
        "Idempotency-Key": str(uuid.uuid4()),
    },
    json={
        "payment_code": "2jrsrcxv7k",
        "rail": "crypto",
        "asset": "USDC",
        "network": "polygon",
        "email": "harper.wilson@example.com"
    },
)
data = response.json()["data"]
```

Response, 201 Created:

```json
{
  "data": {
    "id": "chk_C5yhPQvPqpYgdDgj",
    "kind": "checkout",
    "status": "open",
    "rail": "crypto",
    "link_id": null,
    "payment_id": "pmt_QI02vLdJGd48hBbg",
    "amount": 4900,
    "currency": "USD",
    "breakdown": {
      "currency": "USD",
      "price": 4900,
      "processing_fee": null,
      "network_fee": 2,
      "total": 4902
    },
    "crypto": {
      "asset": "USDC",
      "network": "polygon",
      "amount": "49.00",
      "address": "0x9a3c416D4A81669C01f4515bBDEB93C45d3813fe",
      "payment_uri": "ethereum:0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359@137/transfer?address=0x9a3c416D4A81669C01f4515bBDEB93C45d3813fe&uint256=49000000",
      "rate": {
        "amount": 100,
        "currency": "USD"
      },
      "received_amount": "0.00",
      "remaining_amount": null,
      "remaining_payment_uri": null,
      "overpaid_amount": null,
      "detected_network": null,
      "tx_hash": null,
      "explorer_url": null
    },
    "card": null,
    "confirmations": {
      "current": 0,
      "required": 4
    },
    "remainder": false,
    "canceled_by": null,
    "replaced_by": null,
    "failure_code": null,
    "review_reason": null,
    "success_url": "https://example.com/thanks",
    "expires_at": "2026-09-26T21:37:47.047Z",
    "sent_at": null,
    "created_at": "2026-09-26T21:22:47.047Z",
    "updated_at": "2026-09-26T21:22:47.047Z"
  }
}
```
