Checkout Sessions API
Create checkout sessions from your backend.
Create a checkout session from your backend when your customer is ready to pay.
The response returns a checkoutSessionUrl for hosted checkout, the Stafiel
widget, or a merchant-rendered payment page.
Endpoint
POST /api/v1/checkout/sessions
Request Fields
| Field | Required | Default | Description |
|---|---|---|---|
merchantId |
Yes | None | Must match the merchant bound to the API key. |
chainName |
Yes | None | Chain label used for the checkout session. See Chain Labels. |
tokenSymbol |
Yes | None | Use USDC or USDT, subject to the selected chain and merchant configuration. |
amountUsd |
Yes | None | Dollar-denominated checkout amount as a decimal string. The value may use up to 2 decimal places, and responses do not guarantee trailing zeroes. |
expiresIn |
No | Merchant default | Payment window in seconds. The value must be inside the effective merchant or plan policy range. If it is outside the range, the error response may include error.expiresInAllowRange. |
idempotencyKey |
No | None | Recommended. Use your internal order ID so retries do not create duplicate checkout sessions. |
metadata |
No | None | Your own order reference, customer reference, return URL, or other non-sensitive metadata. See Metadata Reserved Fields and Rules. |
Chain Labels
Use these mainnet chainName values when creating live checkout sessions:
| chainName | Chain ID | Description |
|---|---|---|
eth |
1 |
Ethereum mainnet for USDT and USDC payments. |
base |
8453 |
Base mainnet for lower-cost EVM payments. |
bsc |
56 |
BNB Smart Chain mainnet for EVM payments. |
polygon |
137 |
Polygon mainnet for EVM payments. |
solana |
101 |
Solana mainnet for SPL stablecoin payments. |
tron |
728126428 |
Tron mainnet for TRC20 stablecoin payments. |
Use these testnet chainName values when creating testnet checkout sessions:
| chainName | Chain ID | Description |
|---|---|---|
eth-sepolia |
11155111 |
Ethereum Sepolia for testnet payments. |
base-sepolia |
84532 |
Base Sepolia for testnet payments. |
bsc-testnet |
97 |
BNB Smart Chain Testnet for testnet payments. |
polygon-amoy |
80002 |
Polygon Amoy for testnet payments. |
solana-devnet |
103 |
Solana Devnet for testnet payments. |
tron-nile |
3448148188 |
Tron Nile for testnet payments. |
Available chains and tokens depend on merchant configuration.
Request Example
The example includes the currently supported reserved metadata fields.
curl -X POST https://api.stafiel.com/api/v1/checkout/sessions \
-H "X-API-Key: mk_live_your_api_key" \
-H "X-App-Client: merchant-client" \
-H "Content-Type: application/json" \
-d '{
"merchantId": "mch_your_merchant_id",
"chainName": "base",
"tokenSymbol": "USDC",
"amountUsd": "49.00",
"expiresIn": 1800,
"idempotencyKey": "order_10001",
"metadata": {
"returnToUrl": "https://merchant.example.com/orders/10001",
"productName": "Annual Pro Plan",
"productDescription": "One-year subscription for the merchant workspace.",
"quantity": "1",
"unitLabel": "year",
"customerReference": "CUS-8842"
}
}'
This example returns directly to a merchant order page. You may instead send the customer through your own return handler before showing the final order page.
Success Response
{
"success": true,
"data": {
"orderId": "ord_your_order_id",
"status": "awaiting_payment",
"checkoutSessionUrl": "https://pay.stafiel.com/pay/ps_live_example",
"expiresAt": "2026-05-19T12:00:00.000Z",
"amountUsd": "49.00",
"chainName": "base",
"tokenSymbol": "USDC",
"tokenAddress": "0x..."
}
}
Response Fields
| Field | Type | Description |
|---|---|---|
orderId |
string | Stafiel order ID for later order, payment, refund, settlement, and document lookups. |
status |
string | Initial order status. New checkout sessions return awaiting_payment. |
checkoutSessionUrl |
string | Hosted checkout URL returned for the session. |
expiresAt |
string | ISO 8601 timestamp when the checkout session expires. |
amountUsd |
string | Accepted checkout amount as a decimal string. Trailing zeroes are not guaranteed. |
chainName |
string | Canonical public chain label used for the checkout session. |
tokenSymbol |
string | Token symbol selected for the checkout session. |
tokenAddress |
string | Token contract or mint address used for the selected chain. |
For hosted checkout or Widget integrations, pass the complete
checkoutSessionUrl exactly as returned. Treat the URL and its payment token as
opaque values. Do not alter, rebuild, truncate, length-limit, decode, or log the
complete URL.
For a merchant-rendered payment page, your backend may extract the payment token
from the final path segment. For example,
https://pay.stafiel.com/pay/ps_live_example contains the payment token
ps_live_example.
For the server-side payment data request and response fields, see Custom Payment Page.
Idempotency Key
idempotencyKey provides duplicate protection for checkout session creation
retries. A stable internal order ID is a good key because it maps one
merchant-side order attempt to one checkout creation attempt.
Rules:
- Use 1-128 visible ASCII characters.
- Use the same
idempotencyKeywhen retrying an identical creation request. - After creation succeeds, an identical retry with that key returns the original
checkout session response with HTTP
201. - Reusing the key with a different request payload is rejected.
Note: A replay returns the original creation response. For the order's current status, use the Orders API.
Note: If the creation response is lost or the result is unclear, check the request using the same
merchantIdandidempotencyKey. See Checkout Recovery.
Note: Replaying a checkout session response does not extend the payment window. Use the order's current
expiresAtvalue when determining whether payment is still available.
Metadata Reserved Fields and Rules
metadata is an optional JSON object for merchant business context, such as
your internal order ID or customer reference. Do not put secrets, private keys,
access tokens, full payment credentials, or unnecessary sensitive personal data
in metadata.
Stafiel currently interprets these reserved metadata fields for checkout return behavior, hosted checkout display, and generated documents:
| Field | Required | Default | Description |
|---|---|---|---|
metadata.returnToUrl |
No | None | Complete HTTPS URL used by the hosted checkout Back button, subject to the merchant return URL policy. |
metadata.productName |
No | None | Product or service name for hosted checkout and documents. |
metadata.productDescription |
No | None | Short product or order description for hosted checkout and documents. |
metadata.quantity |
No | None | Product quantity for hosted checkout and documents. |
metadata.unitLabel |
No | None | Unit label after quantity, such as seat, month, or year. |
General metadata rules:
- Use a flat object. Nested objects and arrays are ignored.
- Stafiel stores up to 20 accepted top-level fields, processed in request object order. Invalid fields are ignored and do not count toward the limit.
- Keys must be 1-100 characters and may use ASCII letters, numbers, dots, underscores, and hyphens.
- Values may be strings, finite numbers, booleans, or
null. Objects, arrays, invalid values, and unsupported value types are ignored. - String values may be up to 1,000 characters and may contain letters, numbers, punctuation, spaces, and line breaks.
metadata.returnToUrl rules:
- Send the complete URL, including the
https://scheme and hostname. Relative paths such as/orders/10001are not accepted. - The submitted URL must be 512 characters or fewer.
- Unsafe or blocklisted URLs are rejected.
- If the URL is safe but does not pass the merchant return URL policy, the checkout session can still be created, but the URL is not used.
Note:
metadata.returnToUrlcontrols only the Hosted Checkout Back button. It is not a payment-success callback and is not triggered automatically by payment status changes. Use signed webhooks or authenticated API reads to determine the order outcome.
For policy configuration, see Return URL Settings.
Error Responses
Error responses use this shape:
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid request"
}
}
| HTTP Status | Code | Meaning |
|---|---|---|
400 |
CHECKOUT_SESSION_CREATE_FAILED / ORDER_EXPIRES_IN_OUT_OF_RANGE |
Request is invalid, cannot create a session, or expiresIn is outside the allowed range. |
401 |
AUTH_FAILED |
API key is missing, invalid, or in the wrong mode. |
403 |
NO_MERCHANT_ACCOUNT |
Merchant account is required. |
409 |
CHAIN_NOT_AVAILABLE / TOKEN_NOT_AVAILABLE / CHECKOUT_SESSION_CREATE_FAILED |
Chain or token is unavailable, or the idempotency key is in progress or conflicting. |
429 |
RATE_LIMIT_EXCEEDED |
Too many requests. Retry after the indicated time. |
429 |
CHECKOUT_SESSION_CREATE_FAILED |
Checkout creation failed. Common causes include reaching an order creation quota or live-order limit. |
For 429 CHECKOUT_SESSION_CREATE_FAILED, check the merchant's quotas and
limits in the Merchant Dashboard before retrying. Backoff alone does not
resolve an exhausted quota or live-order limit.
For ORDER_EXPIRES_IN_OUT_OF_RANGE, error.expiresInAllowRange contains the
inclusive minimum and maximum payment window in seconds:
min: Minimum acceptedexpiresIn.max: Maximum acceptedexpiresIn.
The values depend on the effective policy for the merchant.
{
"success": false,
"error": {
"code": "ORDER_EXPIRES_IN_OUT_OF_RANGE",
"message": "Payment window is out of allowed range",
"expiresInAllowRange": {
"min": 300,
"max": 86400
}
}
}
The range above is an example. Use the values returned for the request.

