Quickstart
Accept your first payment in under 5 minutes. This guide walks you through everything from account setup to receiving a real on-chain payment and integrating the API.
Testnet Setup
Before going live, you can develop and test against Base Sepolia — a free test network where no real funds are used.
1. Get test funds
Use the Base Sepolia faucet to receive test ETH for gas fees on Base Sepolia.
2. Enable testnet mode
In your .env.local file, set:
1NEXT_PUBLIC_CHAIN_ENV=testnet
Restart your dev server after changing this value.
3. Embedded wallets on testnet
If you use Privy embedded wallets, they work automatically on testnet — no extra configuration needed. Privy creates wallets on both Base mainnet and Base Sepolia, and your app will route to the correct chain based on the NEXT_PUBLIC_CHAIN_ENV setting.
Note: Test payments made on Base Sepolia will never appear in your live dashboard. Switch
NEXT_PUBLIC_CHAIN_ENVback tomainnet(or remove the variable) when you're ready for production.
Step 1: Create Your Account
Head to usecoal.xyz/signup and register a merchant account. You'll need an email address. After signing up, confirm your email and you'll land in the Coal Console.
Step 2: Set Your Payout Address
Before you can receive funds, you need to tell Coal where to send payouts.
- In the Console, go to Settings → Payout Address
- Paste your EVM wallet address (any wallet that supports ERC-20 tokens on Base)
- Click Save
Important: This is the address that will receive every payment. Coal is non-custodial — funds go directly to your wallet, not to Coal. Set this before creating any live payment links.
Step 3: Create a Product
Products let you define what you're selling — name, price, and optional image.
- Go to Console → Products → New Product
- Fill in:
- Name — e.g.
Premium Membership - Price — e.g.
25.00 - Description (optional) — shown on the checkout page
- Image (optional) — uploaded via URL or file upload
- Name — e.g.
- Click Create Product
Your product is now available to attach to payment links.
Step 4: Create a Payment Link
Payment links are shareable URLs that open a hosted checkout page.
- Go to Console → Payment Links → New
- Choose a Slug — this becomes the URL path, e.g.
premium-membership - Link it to the product you just created
- Optionally set:
- Redirect URL — where to send the buyer after a successful payment
- Callback URL — your webhook endpoint for server-to-server notifications
- Click Create
Your payment link is now live at usecoal.xyz/pay/premium-membership.
Step 5: Test the Checkout
Open https://usecoal.xyz/pay/premium-membership in your browser (or try it in an incognito window).
You'll see the hosted checkout page showing your product name, price, and merchant info. The flow from the user's perspective:
- User visits the payment link
- They connect their wallet
- They approve the on-chain token transfer
- Coal submits the transaction hash for on-chain verification
- Once confirmed, the user is redirected to your
redirectUrlwith?session_id=<id>appended
Step 6: Integrate via API
For programmatic checkout creation — e.g. in your e-commerce backend — use the merchant API directly. These calls use x-api-key; the dashboard and /api/console/* routes use Privy Bearer tokens and are not the primary public merchant surface.
Get Your API Key
Go to Console → API Keys → Create New Key. Copy the key immediately; it will not be shown again.
API keys use the format coal_live_ followed by a random secret. Pass them in the x-api-key header for merchant API requests:
1x-api-key: coal_live_your_key_here
API Flow Overview
x-api-key header · { amount, productName, redirectUrl }
{ sessionId, txHash }
Initialize a Checkout Session
POST https://api.usecoal.xyz/api/checkouts
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": 25,6 "productName": "Premium Membership",7 "redirectUrl": "https://yoursite.com/success"8 }'
Response:
1{2 "data": {3 "id": "clv9abc123...",4 "url": "https://usecoal.xyz/pay/checkout/clv9abc123...",5 "status": "pending",6 "amount": 25,7 "currency": "USDC",8 "expiresAt": "2026-03-23T14:00:00.000Z"9 }10}
For flexible/donation links (no fixed product), just send the amount you want to charge.
Confirm Payment (Submit Transaction Hash)
After your frontend sends the payment on-chain, submit the transaction hash to Coal for verification.
POST https://api.usecoal.xyz/api/pay/confirm
1curl -X POST https://api.usecoal.xyz/api/pay/confirm \2 -H "Content-Type: application/json" \3 -d '{4 "sessionId": "cm9x4k2j00003lb08n5qz7v1r",5 "txHash": "0x3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b"6 }'
Response:
1{2 "data": {3 "status": "verifying",4 "sessionId": "cm9x4k2j00003lb08n5qz7v1r"5 }6}
The verifying status means Coal has accepted the hash and the background verification job will process it within the next minute.
Poll Payment Status
GET https://api.usecoal.xyz/api/pay/status/:sessionId
1curl https://api.usecoal.xyz/api/pay/status/cm9x4k2j00003lb08n5qz7v1r
Response (confirmed):
1{2 "status": "confirmed",3 "txHash": "0x3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b",4 "redirectUrl": "https://yoursite.com/success"5}
Possible status values: pending | verifying | confirmed | failed | expired
Node.js Integration Example
Here is a complete example of initializing a checkout and polling for confirmation in a Node.js backend:
1const COAL_API = 'https://api.usecoal.xyz/api';23// Step 1: Initialize a checkout session4async function initCheckout(amount: number) {5 const res = await fetch(`${COAL_API}/checkouts`, {6 method: 'POST',7 headers: {8 'Content-Type': 'application/json',9 'x-api-key': process.env.COAL_API_KEY!,10 },11 body: JSON.stringify({12 amount,13 productName: 'Premium Membership',14 redirectUrl: 'https://yoursite.com/success',15 }),16 });1718 if (!res.ok) {19 const err = await res.json();20 throw new Error(`Checkout init failed: ${JSON.stringify(err)}`);21 }2223 const { data } = await res.json();24 return data; // { id, url, status, amount, currency, expiresAt }25}2627// Step 2: Submit tx hash after on-chain transfer28async function confirmPayment(sessionId: string, txHash: string) {29 const res = await fetch(`${COAL_API}/pay/confirm`, {30 method: 'POST',31 headers: { 'Content-Type': 'application/json' },32 body: JSON.stringify({ sessionId, txHash }),33 });3435 const { data } = await res.json();36 return data; // { status: "verifying", sessionId }37}3839// Step 3: Poll for final status40async function waitForConfirmation(sessionId: string, timeoutMs = 120_000) {41 const deadline = Date.now() + timeoutMs;4243 while (Date.now() < deadline) {44 const res = await fetch(`${COAL_API}/pay/status/${sessionId}`);45 const { status, txHash, redirectUrl } = await res.json();4647 if (status === 'confirmed') return { status, txHash, redirectUrl };48 if (status === 'failed' || status === 'expired') {49 throw new Error(`Payment ${status}`);50 }5152 // Not yet confirmed — wait 5 seconds and retry53 await new Promise(r => setTimeout(r, 5_000));54 }5556 throw new Error('Confirmation timed out');57}5859// Example usage60const session = await initCheckout('premium-membership');61console.log(`Send ${session.amount} ${session.currency} to ${session.merchant.payoutAddress}`);6263// ... user sends the transfer and you receive the txHash from your frontend ...6465await confirmPayment(session.sessionId, '0xabc123...');66const result = await waitForConfirmation(session.sessionId);67console.log('Payment confirmed:', result.txHash);
Next Steps
- Authentication — understand API keys and securing your requests
- Payment Flow — deep dive into the full end-to-end flow
- Webhooks — receive server push notifications on payment confirmation
- Products API — manage your product catalog programmatically
