Authorize & Capture
Legacy / internal reference only.
Authorize & Capture is a two-phase payment model that separates the authorization (reserving funds) from the capture (collecting them). This is useful for businesses that need to verify an order before charging the customer.
Note: Coal's live public merchant API focuses on direct-settlement checkout sessions. This page is preserved as legacy/internal reference for the authorize/capture workflow and should not be treated as the primary public merchant integration.
When to Use It
| Use Case | Example |
|---|---|
| Pre-orders | Reserve funds when order placed, capture on ship date |
| Hotel holds | Auth on check-in, capture on check-out |
| Metered billing | Auth a limit, capture actual usage |
| Fraud review | Hold funds while you review the order |
For most use cases - simple product sales, digital downloads, subscriptions - the default direct-settlement mode is sufficient. Use authorize_capture only when you need time between reserving and collecting funds in a legacy or internal workflow.
How It Works
{ sessionId }
{ sessionId }
Authorizations expire according to the session's authExpiresAt value. Check that timestamp before attempting capture or void.
Status Transitions
Creating an Authorize & Capture Session
This workflow is documented for legacy/internal systems only. Coal's current public hosted checkout flow is direct-settlement first, and the public POST /api/checkout/init route does not expose authorize_capture as a new merchant integration surface.
If you are maintaining an older internal integration, keep the authorization logic on the server and use the capture/void routes below once an authorization already exists.
Capturing a Payment
Once authorized, capture the payment from your server:
1curl -X POST https://api.usecoal.xyz/api/console/payments/capture \2 -H "Authorization: Bearer <Privy JWT>" \3 -H "Content-Type: application/json" \4 -d '{ "sessionId": "clxxx" }'
1{2 "data": {3 "sessionId": "clxxx",4 "txHash": "0xabc123..."5 }6}
Voiding an Authorization
To release the hold without capturing:
1curl -X POST https://api.usecoal.xyz/api/console/payments/void \2 -H "Authorization: Bearer <Privy JWT>"
1{2 "data": {3 "sessionId": "clxxx",4 "status": "voided"5 }6}
Webhook Events
| Event | Fired when |
|---|---|
checkout.authorized | Customer successfully authorizes |
checkout.confirmed | Capture completes |
checkout.voided | Authorization released |
checkout.failed | Authorization or capture fails |
Node.js Integration Example
1import { NextRequest, NextResponse } from 'next/server';23// Route: POST /api/orders/:orderId/fulfill4export async function POST(req: NextRequest, { params }: { params: { orderId: string } }) {5 const order = await db.orders.findById(params.orderId);67 if (!order || order.status !== 'pending_review') {8 return NextResponse.json({ error: 'Order not ready for fulfillment' }, { status: 400 });9 }1011 // Capture the authorized payment12 const res = await fetch(`${process.env.COAL_API_URL}/api/console/payments/capture`, {13 method: 'POST',14 headers: {15 'Authorization': 'Bearer <Privy JWT>',16 'Content-Type': 'application/json',17 },18 body: JSON.stringify({ sessionId: order.coalSessionId }),19 });2021 if (!res.ok) {22 const err = await res.json();23 return NextResponse.json({ error: err.error?.message }, { status: 502 });24 }2526 await db.orders.update(params.orderId, { status: 'paid' });27 await fulfillOrder(order);2829 return NextResponse.json({ success: true });30}
