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

# Create a payment

Create a payment for an exact amount and get a hosted checkout URL for it.

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

Returns 201 with the new payment and its hosted checkout at `url`. When `reference` already names a live payment for the same amount and currency, it returns 200 with that payment instead, so a retried create never makes a second one.

### Body

- `amount` (integer, required): The price in minor units of `currency`: `4900` with `USD` is $49.00. From the equivalent of $1.00 up to 1,000,000.00 in the currency.
- `currency` (string): USD, EUR, GBP, CAD, AUD. Defaults to USD.
- `reference` (string): Your ID for the payment, such as an order number, up to 64 characters. It names one live payment: one that isn't canceled or expired.
- `description` (string): Shown in the checkout's details and on the receipt, up to 300 characters.
- `customer_email` (string): Links the payment to the customer with this email, adding one if needed. Without it, `customer_id` stays `null` until checkout collects an email.
- `success_url` (string): Where checkout sends the customer after paying, with `payment_id` added to its query. Use `https://`; `http://` works only for `localhost` and `127.0.0.1`. Defaults to the redirect in your checkout settings, then the receipt.
- `cancel_url` (string): Where to send the customer if they leave without paying. Use `https://`; `http://` works only for `localhost` and `127.0.0.1`.
- `expires_at` (timestamp): From 15 minutes to 30 days after 402pay receives the request, so give a 15-minute expiry a few seconds' margin. Defaults to 24 hours.
- `metadata` (object): Up to 20 string pairs of your own. Keys are up to 40 letters, digits, `_`, `.` or `-`. See [metadata](https://developer.402pay.co/api/metadata.md).
- `fee_payer` (string): Who pays 402pay's fee on this payment: `business` or `customer`. Defaults to your checkout settings. With `customer`, checkout adds the fee for the method the customer picks to their total, as [passing fees on](https://developer.402pay.co/guides/reconciliation.md#passing-fees-on) describes.

### Errors

- 400 `invalid_request` A field is missing or out of range. `field` names it.
- 400 `invalid_email` `customer_email` isn't a valid email address.
- 409 `reference_in_use` The reference names a live payment for another amount or currency, or one that already received funds.
- 403 `business_suspended` 402pay suspended your business, so it can't take new payments.
- 403 `test_mode_unavailable` A test key can't create payments on a live business, so use a live key.
- 409 `not_accepting` Your business has no wallet yet, so it can't take payments.

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/payments" \
  -H "Authorization: Bearer $PAY402_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 4900,
    "currency": "USD",
    "reference": "order_1042",
    "description": "Pro plan, monthly",
    "success_url": "https://example.com/thanks",
    "cancel_url": "https://example.com/cart",
    "metadata": {
      "order_id": "1042"
    }
  }'
```

Request, Node.js:

```js
const response = await fetch("https://dash.402pay.co/api/v1/payments", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.PAY402_SECRET_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    amount: 4900,
    currency: "USD",
    reference: "order_1042",
    description: "Pro plan, monthly",
    success_url: "https://example.com/thanks",
    cancel_url: "https://example.com/cart",
    metadata: {
      order_id: "1042"
    }
  }),
});
const { data } = await response.json();
```

Request, Python:

```python
import os

import requests

response = requests.post(
    "https://dash.402pay.co/api/v1/payments",
    headers={
        "Authorization": f"Bearer {os.environ['PAY402_SECRET_KEY']}",
    },
    json={
        "amount": 4900,
        "currency": "USD",
        "reference": "order_1042",
        "description": "Pro plan, monthly",
        "success_url": "https://example.com/thanks",
        "cancel_url": "https://example.com/cart",
        "metadata": {
            "order_id": "1042"
        }
    },
)
data = response.json()["data"]
```

Response, 201 Created:

```json
{
  "data": {
    "id": "pmt_QI02vLdJGd48hBbg",
    "kind": "payment",
    "status": "pending",
    "amount": 4900,
    "currency": "USD",
    "fee_payer": "business",
    "customer_fee": 0,
    "amount_received": 0,
    "reporting": {
      "currency": "USD",
      "amount": 4900,
      "fee": 0,
      "transaction_fee": 0,
      "customer_fee": 0,
      "net": 0
    },
    "fee_rate_bps": 0,
    "method": {
      "rail": "crypto",
      "asset": null,
      "network": null,
      "amount": null,
      "expected_amount": null,
      "overpaid_amount": null,
      "from_address": null,
      "tx_hash": null,
      "explorer_url": null
    },
    "settlement": {
      "destination": "wallet",
      "asset": "USDC",
      "network": "polygon",
      "amount": "0.00",
      "wallet_id": null,
      "address": null,
      "tx_hash": null,
      "explorer_url": null
    },
    "customer_id": null,
    "link_id": null,
    "url": "https://checkout.402pay.co/checkout/2jrsrcxv7k",
    "reference": "order_1042",
    "metadata": {
      "order_id": "1042"
    },
    "success_url": "https://example.com/thanks",
    "cancel_url": "https://example.com/cart",
    "expires_at": "2026-09-27T21:22:47.038Z",
    "canceled_at": null,
    "checkout_id": null,
    "description": "Pro plan, monthly",
    "country": "",
    "failure_code": null,
    "failure_message": null,
    "confirmed_at": null,
    "created_at": "2026-09-26T21:22:47.038Z",
    "updated_at": "2026-09-26T21:22:47.038Z",
    "events": [
      {
        "type": "created",
        "created_at": "2026-09-26T21:22:47.038Z",
        "data": null
      }
    ]
  }
}
```
