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

# Checkout Sessions

> Create a checkout for one purchase, send the buyer to payment, and confirm the resulting order.

Create a Checkout Session from your server when you need a payment URL for a specific purchase. Anyway saves the purchase details, opens the configured payment flow, and creates or updates an order when payment results arrive. You do not need to create a Payment Link first.

## Payment Links and Sessions

| | Payment Link | Checkout Session |
| - | - | - |
| Purpose | A reusable purchase entry point | One checkout with its own purchase details and expiry |
| Start | Share an existing product link | Create from your backend, or start checkout from a Payment Link |
| Buyer URL | Use the returned `paymentLinkUrl` | Use the returned `url` |
| Correlation | URL query parameters | Server-side `merchantReference` and `metadata` |

Opening an API-created Session URL reuses that checkout. Card Payment Links keep their existing payment page and return settings.

## Create a Session

Call `POST /v1/checkout-sessions` from your server with `X-API-Key` and an `Idempotency-Key`. This example uses the default card payment method and an inline product and price:

```bash theme={null}
curl -X POST https://merchant-api-prod.anyway.sh/v1/checkout-sessions \
  -H "X-API-Key: $ANYWAY_MERCHANT_API_KEY" \
  -H "Idempotency-Key: purchase-456-v1" \
  -H "Content-Type: application/json" \
  -d '{
    "pricingData": {
      "productData": {"name": "Consultation"},
      "amount": 3500,
      "currency": "USD",
      "pricingType": "ONE_TIME"
    },
    "quantity": 1,
    "customerEmail": "buyer@example.com",
    "merchantReference": "PUR_456",
    "metadata": {"user_id": "USR_123"},
    "successUrl": "https://merchant.example/payment-result",
    "expiresIn": 3600
  }'
```

A successful creation returns `201` with a ready-to-use `url`. Selected response fields:

```json theme={null}
{
  "success": true,
  "message": "Checkout session",
  "data": {
    "id": "CS_EXAMPLE",
    "status": "CREATED",
    "url": "https://app.anyway.sh/checkout/CS_EXAMPLE",
    "amount": 3500,
    "currency": "USD",
    "orderId": null
  }
}
```

Redirect the buyer to the returned `url`. Use it exactly as returned; do not reconstruct the address or embed your API key. Keep the returned `id` with your purchase record.

See [Create a checkout session](/api-reference/endpoint/create-checkout-session) for the complete request and response schema.

## Choose purchase details

Catalog products have a product ID and can be reused across purchases. Inline products are supplied for a specific purchase and saved with the Session without creating a separate catalog product.

Choose an existing price or supply a price for this purchase:

* **Existing price:** provide the top-level `pricingId`. The price already identifies its product, amount, and currency; you do not provide a separate product ID.
* **Price for this purchase:** provide the top-level `pricingData`, including the amount and currency. Inside it, use `productId` to reference an existing product, or `productData` to supply inline product details.

`pricingId` and `pricingData` are mutually exclusive. `productId` is nested inside `pricingData`, not a top-level alternative to `pricingId`.

| Input | Use |
| - | - |
| `pricingId` | An existing price belonging to your organization |
| `pricingData.productId` | An existing product with a price supplied for this purchase |
| `pricingData.productData` | An inline product and price for this purchase |

Inside `pricingData`, choose exactly one of `productId` and `productData`. Inline details are saved with the Session and can be read from its detail response; they do not create separately browsable catalog products or prices. The Session keeps a purchase snapshot so later catalog edits do not change its quoted purchase.

If you use [product access checks](/api-reference/endpoint/check-entitlement), choose `pricingId` or `pricingData.productId`: both retain a catalog product ID, including when the price is supplied for this purchase. Purchases using `pricingData.productData` have no catalog product ID and are not included in entitlement checks, even after successful payment or subscription activation.

For inline products, manage access in your own system using verified order/subscription status and your server-side purchase records. A product name is a display label, not an entitlement identifier; a completed Checkout Session does not establish that a subscription is still active.

`amount` is the unit price in the currency's smallest unit; the Session purchase amount includes `quantity`, which defaults to 1. For USD, `3500` means \$35.00. For USDC and USDT, `1000000` means one token. Customer information is optional when creating a Session. You may omit both `customerId` and `customerEmail`; if you provide customer information, send only one of them. Card checkout also supports subscription pricing; use the billing fields in the generated schema.

For crypto, use `provider: "CRYPTO"` and a supported USDC or USDT price. Configure the organization's receiving wallet first. The hosted checkout provides the configured network and receiver; customers should follow the network shown there. Crypto API Sessions currently support one-time pricing.

```json theme={null}
{
  "provider": "CRYPTO",
  "pricingData": {
    "productData": {"name": "Consultation"},
    "amount": 1000000,
    "currency": "USDT",
    "pricingType": "ONE_TIME"
  },
  "merchantReference": "PUR_457",
  "expiresIn": 3600
}
```

## Input limits

| Input | Constraint |
| - | - |
| `Idempotency-Key` | Required; 1–255 ASCII characters from `0x21` to `0x7E` (no spaces or control characters) |
| `quantity` | Integer from 1 to 999999; defaults to 1 |
| `merchantReference` | At most 255 bytes |
| `metadata` | At most 50 string key-value pairs; keys must contain 1–40 characters without `[` or `]`; values may contain at most 500 characters |
| `pricingData.productData.name` | Required for an inline product; 1–255 characters after trimming surrounding whitespace |
| `customerEmail` | Valid email address, at most 255 bytes |
| Request body | One JSON object of at most 64 KiB; unknown fields, including nested fields such as `pricingData.flexibleAmount`, return `400` |

## Idempotency and retries

* Generate one `Idempotency-Key` for each creation request. Retry the same request with the same key and unchanged parameters after a timeout or `503`.
* Reusing a key with different parameters returns `409`. A retry whose existing checkout can no longer be paid can also return `409`; use a new key for an intentional new checkout.
* `merchantReference` is a business correlation value, not an idempotency key or a uniqueness lock. Different creation keys can create separate Sessions for the same reference.
* Invalid input returns `400`. Failed creation does not return a usable payment URL.
* Creation returns `403` if the organization is frozen, Card/USD verification is incomplete, or the card payment account is missing or not enabled to receive payments. Complete the relevant setup before retrying.

Send `metadata` as a map of strings. It is returned with the Session and carried to the order as `merchantMetadata`; `merchantReference` remains a separate field.

## Query and confirm payment

Use `GET /v1/checkout-sessions/{id}` to retrieve the Session in your organization:

```bash theme={null}
curl https://merchant-api-prod.anyway.sh/v1/checkout-sessions/CS_EXAMPLE \
  -H "X-API-Key: $ANYWAY_MERCHANT_API_KEY"
```

| Field | Meaning |
| - | - |
| `status` | Session lifecycle: `CREATED`, `COMPLETE`, `EXPIRED`, or `FAILED` |
| `orderId` | The associated order, when available |
| `orderStatus` | The associated order's payment state, when available |
| `url` | The payable Session URL; null when the Session is no longer payable |
| `lastError` | The last recorded channel operation error; its presence alone does not mean the Session is `FAILED` |

`COMPLETE` does not guarantee that funds have arrived: an asynchronous payment can still be pending. A fully refunded order can have `orderStatus: "REFUNDED"` while its Session remains `COMPLETE`; a refund does not reopen checkout. Partial refunds may leave the order `PAID`, so use order details for refund information.

Confirm payment through the [Orders API](/api-reference/orders-guide) or a verified [order webhook](/api-reference/endpoint/webhooks), not a browser return or the Session lifecycle alone. Orders associated with a Session include `checkoutSessionId`. Fulfill idempotently by order ID. Subscription renewals are separate orders linked to the subscription and initial order; they do not complete the initial Session again.

The Session URL covers both payment and the resulting receipt. Reopening a receipt does not create a new purchase.

## Expiry and return URLs

`expiresIn` is in seconds and defaults to 3600. Card checkout accepts 1800–86400; crypto accepts 60–86400. To stop an unpaid checkout, call `POST /v1/checkout-sessions/{id}/expire`:

```bash theme={null}
curl -X POST https://merchant-api-prod.anyway.sh/v1/checkout-sessions/CS_EXAMPLE/expire \
  -H "X-API-Key: $ANYWAY_MERCHANT_API_KEY"
```

For a Payment Link Session without a known provider Session, expiration closes only the platform entry. It does not deactivate the reusable Payment Link or close a provider page the buyer has already opened.

A successful expiration returns `EXPIRED`; expiring an already `EXPIRED` Session also succeeds. Sessions in `COMPLETE` or `FAILED` return `409`. If expiration fails, query the Session before assuming it is closed. For crypto, expiry closes the payment entry but cannot revoke a receiving address or undo a transfer already sent.

`successUrl` and `cancelUrl` must be absolute HTTPS URLs. Payment success can return the buyer to your configured URL with the signed result; leaving via the cancel link does not by itself cancel or fail the Session. Always confirm the order on your server.

## Find Sessions in Business

Open **Business → Sales → Checkout Sessions**. The list defaults to API-created Sessions. Use **Source** to select **API sessions**, **Payment Link sessions**, or **All sources**. Search using the full Session ID or merchant reference, and open a row to inspect purchase details and the related order. A payable Session offers **Open checkout**.

<CardGroup cols={3}>
  <Card title="Create" icon="plus" href="/api-reference/endpoint/create-checkout-session">Create a checkout for a purchase.</Card>
  <Card title="Retrieve" icon="magnifying-glass" href="/api-reference/endpoint/get-checkout-session">Read its lifecycle and order relationship.</Card>
  <Card title="Expire" icon="clock" href="/api-reference/endpoint/expire-checkout-session">Stop an unpaid checkout.</Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.