coal
coal

Checkouts API

Checkout sessions are the core of the Coal payment flow. Create a session to generate a unique payment URL, then redirect your customer to complete payment. Coal verifies the on-chain transfer and notifies your server via webhook.

The Checkout Session Object

json
1{
2 "id": "clxxx789",
3 "merchantId": "clmerchant456",
4 "slug": "my-product",
5 "amount": "49.99",
6 "currency": "USDC",
7 "status": "pending",
8 "payoutAddress": "0xabc123...",
9 "txHash": null,
10 "redirectUrl": "https://yoursite.com/success",
11 "productId": "clxxx123",
12 "expiresAt": "2026-03-22T12:15:00.000Z",
13 "confirmedAt": null,
14 "createdAt": "2026-03-22T12:00:00.000Z"
15}
FieldTypeDescription
idstringUnique session ID (CUID)
merchantIdstringID of the owning merchant
slugstringPayment link slug this session was created from
amountstringAmount for this session (decimal string)
currencystringSession settlement currency
statusstringpendingverifyingconfirmed, or failed / expired
payoutAddressstringMerchant's Base wallet address
txHashstring | nullOn-chain transaction hash once confirmed
redirectUrlstring | nullWhere to redirect after payment
payerInfoConfigobject | nullBuyer info fields Coal should collect before payment
payerInfoobject | nullCollected payer details once the buyer fills them in
expiresAtISO 8601Session expiry (24 hours from creation)
confirmedAtISO 8601 | nullWhen on-chain confirmation was verified

Create a Checkout Session

POST/api/checkouts

Creates a new checkout session and returns the session ID and checkout URL. Use the URL to redirect your customer.

Authentication: x-api-key: coal_live_*

Request body:

ParameterTypeRequiredDescription
amountnumberrequiredAmount to charge for the session
currencystringoptionalSettlement currency. Defaults to the merchant's configured currency.
productIdstringoptionalOptional product ID to associate with the session
productNamestringoptionalOptional display name for the checkout
productDescriptionstringoptionalOptional product description shown in checkout
productImagestringoptionalOptional product image URL
redirectUrlstringoptionalWhere to send the buyer after payment
callbackUrlstringoptionalOptional server-side callback endpoint for payment events
splitConfigIdstringoptionalOptional split configuration ID to apply at settlement
payerInfo{ required: boolean; fields: string[] }optionalCollect payer details before checkout. Example fields: fullName, email, phone, company, country.
bash
1curl -X POST https://api.usecoal.xyz/api/checkouts \
2 -H "x-api-key: coal_live_your_key_here" \
3 -H "Content-Type: application/json" \
4 -d '{
5 "amount": 49.99,
6 "productName": "Pro Plan",
7 "redirectUrl": "https://yoursite.com/success",
8 "payerInfo": {
9 "required": true,
10 "fields": ["fullName", "email"]
11 }
12 }'
json
1{
2 "data": {
3 "id": "clxxx789",
4 "url": "https://usecoal.xyz/pay/checkout/clxxx789",
5 "status": "pending",
6 "amount": 49.99,
7 "currency": "USDC",
8 "expiresAt": "2026-03-22T12:15:00.000Z"
9 }
10}

Session Expiry

Sessions expire after 24 hours. If the customer doesn't complete payment in that window, create a new session. The old session will transition to expired status.

Collecting Payer Info

Use payerInfo to ask for buyer details before payment. Coal stores the collected values on the checkout session and includes them in the checkout.session.completed webhook payload.


Get Session Status

GET/api/pay/status/:sessionId

Poll this endpoint or use it in your redirect handler to check whether a payment has been confirmed.

bash
1curl https://api.usecoal.xyz/api/pay/status/clxxx789
json
1{
2 "status": "confirmed",
3 "txHash": "0xdeadbeef...",
4 "redirectUrl": "https://yoursite.com/success"
5}

Status values:

StatusDescription
pendingSession created, awaiting customer payment
verifyingTransaction hash received and on-chain verification is running
confirmedPayment verified on-chain
failedPayment transaction failed or was invalid
expiredSession timed out without payment

Confirm a Payment (Merchant-Initiated)

POST/api/pay/confirm

Called by the checkout widget after the customer's wallet submits the transaction. You do not call this from your server — it's an internal step handled by Coal's frontend.

Warning:

This endpoint is called by the Coal checkout widget, not by your integration. Your server should rely on webhooks or status polling to detect payment confirmation.


List Sessions (Console only)

GET/api/console/checkouts

Returns all checkout sessions for the authenticated merchant, newest first. This is a dashboard route, not the primary public merchant API.

Authentication: Authorization: Bearer <Privy JWT>

Query parameters:

ParamTypeDescription
statusstringFilter by status: pending, confirmed, failed, expired
limitnumberMax results per page (default: 50, max: 100)
cursorstringCursor for pagination (from previous response)
bash
1curl "https://api.usecoal.xyz/api/console/checkouts?status=confirmed&limit=20" \
2 -H "Authorization: Bearer <Privy JWT>"
json
1{
2 "data": {
3 "sessions": [
4 {
5 "id": "clxxx789",
6 "amount": "49.99",
7 "currency": "USDC",
8 "status": "confirmed",
9 "txHash": "0xdeadbeef...",
10 "confirmedAt": "2026-03-22T12:03:41.000Z",
11 "createdAt": "2026-03-22T12:00:00.000Z"
12 }
13 ],
14 "nextCursor": null,
15 "total": 1
16 }
17}

Capture a Payment (Console only)

POST/api/console/checkouts/:id/capture

This is a dashboard route for legacy or internal hold flows. See Authorize & Capture for the legacy walkthrough.

Authentication: Authorization: Bearer <Privy JWT>

bash
1curl -X POST https://api.usecoal.xyz/api/console/checkouts/clxxx789/capture \
2 -H "Authorization: Bearer <Privy JWT>" \
3 -H "Content-Type: application/json" \
4 -d '{ "amount": 49.99 }'
json
1{
2 "data": {
3 "id": "clxxx789",
4 "status": "confirmed",
5 "capturedAt": "2026-03-22T15:00:00.000Z",
6 "amount": "49.99"
7 }
8}

Void an Authorization (Console only)

POST/api/console/checkouts/:id/void

Releases a reserved authorization without capturing funds.

bash
1curl -X POST https://api.usecoal.xyz/api/console/checkouts/clxxx789/void \
2 -H "Authorization: Bearer <Privy JWT>"
json
1{
2 "data": {
3 "id": "clxxx789",
4 "status": "voided",
5 "voidedAt": "2026-03-22T15:00:00.000Z"
6 }
7}

Webhook Events

EventFired when
checkout.confirmedPayment verified on-chain
checkout.failedTransaction failed or invalid
checkout.expiredSession timed out
checkout.authorizedAuth & Capture — funds reserved
checkout.voidedAuthorization released

See Webhooks Overview and Signature Verification for integration details.


Error Codes

CodeHTTPDescription
NOT_FOUND404Session or slug does not exist
SESSION_EXPIRED410Session has passed its 15-minute window
ALREADY_CONFIRMED409Session has already been paid
UNAUTHORIZED401Missing or invalid API key
INVALID_AMOUNT400Capture amount exceeds authorized amount