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

# Hosted checkout

> A payment page on Payra's domain for one order: your server creates it, the customer pays on it, and comes back to you.

A **checkout session** is one order's payment page: a `cs_test_…` / `cs_live_…` object your server
creates with a secret key and a `url` on Payra's domain. Send the customer there; the page carries
your business name and logo, collects a card or a US bank account with [Payra Elements](/elements),
charges it in your name (the payment shows in RevOS like any other), and sends the customer back to
your `success_url`. A session pays once and expires after 24 hours unless you say otherwise.

Use it when you would rather not host the payment step: no fields on your page, no publishable key in
your front end, one redirect out and one back. Use [Elements](/elements) directly when the fields have
to live inside your own checkout.

<Steps>
  <Step title="Create the session on your server">
    ```bash theme={null}
    curl -X POST https://api-dashboard.payra.com/v1/checkout-sessions \
      -H "Authorization: Bearer sk_test_..." \
      -H "Idempotency-Key: 7c1e2b6a-3b0d-4a8e-9a57-1f2b3c4d5e6f" \
      -H "Content-Type: application/json" \
      -d '{
        "amount": 12550,
        "currency": "USD",
        "reference": "1042",
        "description": "Northwind order #1042",
        "success_url": "https://shop.example/thanks?order=1042",
        "cancel_url": "https://shop.example/cart",
        "customer_email": "ana@example.com",
        "payment_method_types": ["card", "us_bank_account"]
      }'
    ```

    ```json Response theme={null}
    {
      "object": "checkout_session",
      "id": "cs_test_7Hs2kQ9mL1pXv4cR8tWzAbCd",
      "url": "https://dashboard.payra.com/checkout/cs_test_7Hs2kQ9mL1pXv4cR8tWzAbCd?key=…",
      "status": "open",
      "amount": 12550,
      "currency": "USD",
      "reference": "1042",
      "description": "Northwind order #1042",
      "success_url": "https://shop.example/thanks?order=1042",
      "cancel_url": "https://shop.example/cart",
      "customer_email": "ana@example.com",
      "customer": null,
      "payment_method_types": ["card", "us_bank_account"],
      "metadata": null,
      "payment": null,
      "livemode": false,
      "expires_at": "2026-09-26T12:00:00.000Z",
      "completed_at": null,
      "created_at": "2026-09-25T12:00:00.000Z"
    }
    ```

    `amount` is in the smallest unit, charged exactly. `success_url` and `cancel_url` must be `https`; a
    sandbox key may also use `http://localhost` while you build.
    `reference` (your order number) and `description` are stored on the payment the page makes, so the
    [`payment.*` webhooks](/webhooks) carry them as they do for any charge. `payment_method_types` omitted
    means a card only; `us_bank_account` needs `USD`. `customer_email` is kept on the session for your records.
    `customer`, one of your customers, files the payment on them, and a card paid on the page is also saved
    to them: the page tells the customer so and asks for the billing ZIP. `metadata` (up to 20 keys) is stored
    and returned, never interpreted. `expires_at` can be at most 7 days out.

    An [`Idempotency-Key`](/idempotency) is required: a retry after a timeout answers the same session and
    the same `url`, not a second page for the order.

    **`url` is in this response only.** It carries the key that opens the page; reading the session back
    never returns it. Store it with the order, or send the customer at once.
  </Step>

  <Step title="Send the customer to the page">
    Redirect in the same tab (`302`, or `window.location`). The page shows your business name and logo
    from RevOS settings, the amount, the description and the reference, and one tab per payment method
    type you allowed. The customer enters the details; a refused card can be tried again on the same page.

    A card is charged at once; a bank debit is accepted and the page says the payment is scheduled. On
    success the customer is sent to `success_url` with `?checkout_session=cs_…` appended, so your page can
    read the session before the webhook lands. **Back to store** goes to `cancel_url`, and so does an
    expired page.
  </Step>

  <Step title="Confirm on your server">
    Listen for [`checkout_session.completed`](/webhooks), whose `data.object` is the session with
    `payment` set to the `pay_…` it made; or for `payment.succeeded` as with any charge (the payment carries
    your `reference`). A page that ran out of time, or that you expired, emits `checkout_session.expired`.

    Reading the session back works too:

    ```bash theme={null}
    curl https://api-dashboard.payra.com/v1/checkout-sessions/cs_test_7Hs2kQ9mL1pXv4cR8tWzAbCd \
      -H "Authorization: Bearer sk_test_..."
    ```

    `status` is `open`, `complete` (with `payment`) or `expired`. A bank debit leaves the session `complete`
    while the payment is still `processing`: follow the payment, as [Payments](/payments) explains, to know
    when it settled.
  </Step>
</Steps>

## The publishable key the page uses

The page mounts Elements with a publishable key of your workspace. If the workspace has none in that
environment, the first session creates one named **Hosted checkout**; it appears in Settings →
Integrations like any other key and can be revoked there. Revoking it while sessions are open makes
those pages unavailable; the next session creates a new one.

## Closing a page early

When the order is canceled on your side, expire the page so the customer cannot pay it:

```bash theme={null}
curl -X POST https://api-dashboard.payra.com/v1/checkout-sessions/cs_test_7Hs2kQ9mL1pXv4cR8tWzAbCd/expire \
  -H "Authorization: Bearer sk_test_..."
```

A session that already completed answers `400 checkout_session_not_open`. While the customer is
paying on the page, the page stays open and the call answers `409 checkout_session_payment_in_flight`:
retrieve the session once that payment has succeeded or failed. If it is `complete`, the order is
paid; if it is still `open`, expire it again.

## Listing

`GET /checkout-sessions` answers your sessions newest first, paged with `limit` and `starting_after`
like every other list; see [pagination](/pagination).

## Errors

| Code                                       | When                                                                                                                                                                                                           |
| ------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `idempotency_key_required` (400)           | The create has no `Idempotency-Key`                                                                                                                                                                            |
| `parameter_invalid` (400)                  | A URL is not `https` (a sandbox key may use `http://localhost`), a bank account was offered in `CAD`, `expires_at` is in the past or more than 7 days out, or `customer` is not yours; `param` names the field |
| `checkout_session_not_open` (400)          | Expiring a session that already completed or expired                                                                                                                                                           |
| `resource_missing` (404)                   | No such session in this workspace                                                                                                                                                                              |
| `checkout_session_payment_in_flight` (409) | Expiring a session while the customer's payment on the page is still being processed                                                                                                                           |
