Checkout Sessions
When the amount isn't fixed in advance — a cart total, metered usage, a custom quote — your server creates a Checkout Session with a secret key, and the embed opens it. It's the only safe way to charge an arbitrary amount.
Fixed prices don't need this — use a Price and skip your backend entirely. Reach for Sessions only when the amount is computed at request time.
Create a session
Request
| Field | Notes | |
|---|---|---|
amountCents | required | Positive integer, USD cents. 1575 = $15.75. |
successUrl | optional | Where to send the buyer after they pay. |
metadata | optional | String key/value, echoed on this payment's webhooks. |
curl https://api.hashpay.dev/v1/checkout/sessions \
-H "Authorization: Bearer sk_live_2kfPqR…" \
-H "Content-Type: application/json" \
-d '{
"amountCents": 1575,
"successUrl": "https://shop.example.com/thanks",
"metadata": { "cartId": "cart_9" }
}'
Response · 201
{
"sessionId": "cs_abc123…",
"mode": "live",
"amountCents": 1575,
"expiresAt": "2026-06-29T12:00:00Z"
}
Both the session's mode and the store it belongs to (branding + webhook routing) come from the key — an sk_test_ key makes a test session for its store. Sessions expire 24 hours after creation.
Open it in the browser
Send the sessionId to your front-end and hand it to the embed. The buyer picks chain + token and pays the session's amount:
HashPay.checkout({ sessionId: 'cs_abc123…' })
Or declaratively: <button data-hashpay-session="cs_abc123…">Pay</button>. See the Embed SDK.
Errors
| Code | HTTP | When |
|---|---|---|
unauthorized | 401 | Missing or invalid secret key. |
account_disabled | 403 | Account is banned. |
bad_amount | 400 | amountCents not a positive integer. |
bad_request | 400 | Malformed body, or metadata over the limits (≤20 keys, ≤2 KB). |