Checkout sessions
One server-created purchase attempt, scoped to a store and mode.
On this page
Create a checkout session
/v1/profiles/{profileId}/checkout-sessionsAuthentication: Bearer key · Required scope: invoices.write
Returns 201 for a new attempt, 200 for an exact retry. Requires a merchant store with billing details, wallet and accepted tokens ready.
The published Node package does not expose these methods yet. Use HTTP.
| Field | Details |
|---|---|
profileIdpath · required UUID | The profile that owns your API key. |
const response = await fetch(
process.env.FORWEND_API_URL + "/v1/profiles/" +
process.env.FORWEND_PROFILE_ID + "/checkout-sessions",
{
method: "POST",
headers: {
Authorization: "Bearer " + process.env.FORWEND_KEY,
"Content-Type": "application/json",
"Idempotency-Key": "order-1042-attempt-1",
},
body: JSON.stringify({
storeId: process.env.FORWEND_STORE_ID,
amountUsd: "1250.00",
reference: "order-1042",
metadata: { order_id: "1042" },
}),
},
);
if (!response.ok) throw new Error("Checkout could not be created");
const session = await response.json();
// Send only the hosted URL, or the pay token for an embedded checkout, to your browser.
const hostedUrl = session.payment.hostedUrl;
const payToken = session.payment.payToken;Request headers
| Field | Details |
|---|---|
Authorizationheader · required | Bearer followed by your server-only API key. |
Content-Typeheader · required | application/json for a create request. |
Idempotency-Keyheader · required for create | 1–255 UTF-8 bytes. Reuse the same key and normalized body for a retry. |
Create request body
| Field | Details |
|---|---|
storeIdUUID · required | Merchant store owned by this profile and allowed by the key. |
amountUsdstring · required | Exact positive USD decimal. Compute it on your server. |
referencestring · optional | Immutable order reference; up to 100 characters. |
titlestring · optional | Purchase title; up to 200 characters. |
expiresIninteger · optional | 900–31536000 seconds. |
returnUrlstring · optional | HTTPS URL on one of the store’s allowed origins. A redirect is not payment proof. |
customerobject · optional | name (≤200 characters) and email. |
itemsarray · optional | Up to 100 {description, quantity, unitPrice} entries. Description ≤200; positive quantity with ≤2 decimals; USD unitPrice. Totals must match amountUsd. |
metadataobject · optional | Up to 20 single-line string values; see request rules. |
Read a checkout session
/v1/profiles/{profileId}/checkout-sessions/{sessionId}Authentication: Bearer key · Required scope: invoices.read
Returns 200 with canonical session state. No request body. Another store’s or mode’s session is hidden with 404.
| Field | Details |
|---|---|
profileIdpath · required UUID | The profile that owns your API key. |
sessionIdpath · required string | Identifier returned by create. |
List checkout sessions
/v1/profiles/{profileId}/checkout-sessionsAuthentication: Bearer key · Required scope: invoices.read
Returns 200 with {items, nextCursor}. Items are session objects, newest first. Includes attempts without invoice numbers. No request body.
| Field | Details |
|---|---|
profileIdpath · required UUID | The profile that owns your API key. |
limitquery · integer | 1–100; default 50. |
cursorquery · string | The previous response’s nextCursor. Omit for the first page. |
storeIdquery · UUID | Optional store filter, still limited by the key’s store. |
Expire an unpaid attempt
/v1/profiles/{profileId}/checkout-sessions/{sessionId}/expireAuthentication: Bearer key · Required scope: invoices.write
No request body. Returns 200 with the closed session and is idempotent once expired. Payment already seen or confirming returns 409.
Later funds remain held for the payer. Expiration does not erase receive addresses or financial records.
| Field | Details |
|---|---|
profileIdpath · required UUID | The profile that owns your API key. |
sessionIdpath · required string | The session to expire. |
Session response
Changed purchase terms with the same idempotency key return 409 idempotency_conflict. Missing billing details return 409 billing_details_required.
| Field | Details |
|---|---|
sessionIdstring | Purchase attempt identifier. |
statusstring | open, complete or expired. Read paymentStatus to determine whether the order is paid. |
payment.paymentId, reference, storeIdstring / nullable strings | The payment and its original order/store relationship. |
payment.hostedUrlstring | null | Hosted checkout URL to redirect the customer to. |
payment.payTokenstring | null | The public pay token, the URL's last segment, for @forwend/js and @forwend/react. |
payment.chainId, token, amount, symbol, decimalstoken and money fields | Amount is a string in integer base units; identify money by chain and contract. |
payment.paymentStatus, settlementStatus, refundStatusstrings | Separate payment, wallet settlement and held-money processing. |
payment.livemode, confirming, closedbooleans | Mode, pending network confirmation and whether checkout has closed. |
payment.receiptsarray | Per-option chainId, token, symbol, decimals, received, pending and held integer strings. |
payment.amountsobject · optional | Exact receiver, payable, held, settled and refunded balances, with reconciliation information. |
Errors
Responses use {error: {code, message}}. Missing or invalid credentials return 401; missing scope or a different profile returns 403. Objects outside the caller’s store or mode are hidden with 404.
Invalid values return 422 invalid_value. Rate limiting returns 429 rate_limited with Retry-After. A starting network can return 503 network_not_ready; an unexpected server error returns 500 internal.