API reference
Checkout sessions
Create a hosted payment page, send the payer to it, and read the result: parameters, the session object, lifecycle and the return URLs.
Updated
A checkout session is a time-limited intent to be paid, plus a page we host to collect the payment. You create the session, send the payer to url, and they come back to your success_url with the payment already made.
This is the shortest integration there is: no card form, no tokenisation, no card data anywhere near your servers. The page runs through the same orchestrator as the API, so a payment made on it is indistinguishable from one you created yourself.
Create a session
POST /v1/checkout-sessions → 201 with a checkout session object.
| Parameter | Required | Notes |
|---|---|---|
amount | yes | Positive integer, minor units. |
currency | yes | Lowercase ISO 4217. |
reference | yes | Yours, 1–64 characters. |
success_url | yes | Absolute https URL. Where the payer lands after paying. |
cancel_url | no | Absolute https URL. Where "back" goes. |
description | no | ≤ 500 characters. Shown on the page. |
capture_method | no | automatic (default) or manual. |
allowed_methods | no | Up to 20 wire method names. Omitted means card only. |
customer_email | no | Pre-fills the receipt field. |
customer | no | An existing cus_ id. |
allowed_origins | no | Up to 10 https origins. Accepted and stored for the embedded mode; v1 is redirect-only. |
expires_in | no | Seconds, 1800 to 604800. Default 86400 (24 hours). |
metadata | no | Same rules as on a payment. |
curl https://cleared-pay.com/api/v1/checkout-sessions \
-H "Authorization: Bearer $CLEARED_SECRET_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"amount": 2000,
"currency": "eur",
"reference": "ORDER-10432",
"description": "Order 10432 — 1200 credits",
"allowed_methods": ["card", "ideal", "blik"],
"customer_email": "buyer@example.com",
"success_url": "https://merchant.example/checkout/done",
"cancel_url": "https://merchant.example/cart",
"expires_in": 3600,
"metadata": { "cart_id": "c_881" }
}'The session object
{
"id": "cs_01JBXQ2M4K7T9V3ZC8HR5N6PWD",
"object": "checkout_session",
"status": "open",
"url": "https://cleared-pay.com/pay/cs_01JBXQ2M4K7T9V3ZC8HR5N6PWD",
"amount": 2000,
"currency": "eur",
"reference": "ORDER-10432",
"description": "Order 10432 — 1200 credits",
"capture_method": "automatic",
"allowed_methods": ["card", "ideal", "blik"],
"customer_email": "buyer@example.com",
"customer": null,
"success_url": "https://merchant.example/checkout/done",
"cancel_url": "https://merchant.example/cart",
"payment": null,
"metadata": { "cart_id": "c_881" },
"livemode": false,
"created": "2026-08-19T10:15:00Z",
"expires_at": "2026-08-19T11:15:00Z",
"completed_at": null
}| Field | Notes |
|---|---|
status | open, completed or expired. |
url | The hosted page. Send the payer here; the id is the capability, so treat the URL as one. |
payment | The pay_ id once a payment has been attempted. null before that. |
completed_at | Set when the session completed. |
Which methods the page offers
The page shows allowed_methods intersected with what is actually routable for your account, minus wallets — Apple Pay and Google Pay need a real device flow, which the sandbox cannot simulate. With no allowed_methods the page offers card only.
A method you listed that is not enabled on your account simply does not appear. If nothing is left, the session cannot be paid — check your routing before you send a payer.
Lifecycle
open— created, unpaid, withinexpires_at.- The payer opens
urland pays. A declined attempt leaves the sessionopenso they can try another card or another method; that is deliberate. completed— the payment reachedcaptured,authorizedorprocessing.checkout_session.completedfires.expired—expires_atpassed without a completing payment.checkout_session.expiredfires. Expiry is applied lazily on read as well as by a scheduled job, so a read is never stale.
The return
The success redirect appends two parameters to your success_url:
https://merchant.example/checkout/done?session_id=cs_01JBXQ2M4K7T9V3ZC8HR5N6PWD&payment=pay_01JBXQ4N7P2R8T5WY9AC3EF6HKDo not treat the redirect as proof of payment. A payer can close the tab, lose connectivity, or edit the URL. Fulfil on the checkout_session.completed or payment.captured webhook, and use the redirect only to show the right screen. If you need to render a result immediately, read the session back:
curl https://cleared-pay.com/api/v1/checkout-sessions/cs_01JBXQ2M4K7T9V3ZC8HR5N6PWD \
-H "Authorization: Bearer $CLEARED_SECRET_KEY"What the page is
Cleared-pay-controlled, CSP-locked, and script-inventoried for PCI DSS 6.4.3 — the inventory is part of the repository and a test fails the build on any third-party script. Card data entered there is tokenised inside a single server action and discarded; nothing card-shaped is persisted or logged.
The page is redirect-only in v1: it sets frame-ancestors 'none' and cannot be embedded. allowed_origins is accepted now so an embedded mode later needs no change to your integration.
TODO(compliance): a live session renders "Live payments are not enabled yet" until a live connector is in place. Everything on this page works today with a test key.
Testing it
Create a session with a test key, open url, and pay with a number from the magic-card table. 4242 4242 4242 4242 captures, 4000 0000 0000 9995 gives you a soft decline and the retry behaviour, and 4000 0000 0000 3220 sends you through the simulated challenge page.