React SDK
The coal-react package provides everything you need to integrate Coal payments and agent-discoverable commerce into a React / Next.js application.
Version 0.4.1 — three sub-exports:
| Import from | What it does |
|---|---|
coal-react | Client components: checkout buttons, product grids, catalog publisher, Schema.org SEO, receipt polling |
coal-react/server | Server-only: publish your product catalog to Coal's 0G-backed index |
coal-react/next | Next.js App Router: serve agent-discoverable manifests (A2A Agent Card, llms.txt, x402) |
Installation
1npm install coal-react
Peer dependencies: React 18+ and React DOM 18+ (optional for server-only usage).
Quick start
1. Wrap your app
1// app/layout.tsx2import { CoalProvider } from 'coal-react';34export default function RootLayout({ children }) {5 return (6 <CoalProvider merchantId="your-coal-merchant-id">7 {children}8 </CoalProvider>9 );10}
2. Add a buy button
1import { CoalBuyButton } from 'coal-react';23<CoalBuyButton4 createSession={async () => {5 const res = await fetch('/api/create-checkout', { method: 'POST' });6 return res.json(); // must return { url } or { sessionId }7 }}8>9 Buy for $9.9910</CoalBuyButton>
3. Make your site agent-discoverable (optional)
1// app/.well-known/agent-card.json/route.ts2import { createAgentCardRoute } from 'coal-react/next';3export const GET = createAgentCardRoute({ merchantId: 'your-merchant-id' });
AI agents using A2A (Google), x402 (Coinbase), MCP, or LLM crawlers now find your products automatically.
Client components
All client components require <CoalProvider> as an ancestor.
<CoalProvider>
Context wrapper. Safe to render on the server. Provides merchantId, apiUrl, and baseUrl to all nested Coal components.
| Prop | Type | Default | Description |
|---|---|---|---|
merchantId | string | required | Your Coal merchant ID |
apiUrl | string | 'https://api.usecoal.xyz' | Coal API base URL |
baseUrl | string | 'https://usecoal.xyz' | Coal frontend base URL (for checkout redirects) |
<CoalCheckoutButton>
Redirects to Coal's hosted checkout page. Use when you already have a checkout URL or session ID from your server.
| Prop | Type | Default | Description |
|---|---|---|---|
checkoutUrl | string | — | Full checkout URL. Preferred. |
sessionId | string | — | Alternative — URL is constructed as {baseUrl}/pay/checkout/{id} |
baseUrl | string | Provider's baseUrl | Override the Coal base URL |
target | '_self' | '_blank' | '_self' | Redirect vs new tab |
onBeforeRedirect | () => void | — | Callback for analytics |
children | ReactNode | "Pay with Coal" | Button content |
className | string | — | CSS class |
style | CSSProperties | — | Inline styles |
1<CoalCheckoutButton2 checkoutUrl={url}3 onBeforeRedirect={() => analytics.track('checkout')}4/>
<CoalBuyButton>
Higher-level button that calls your server to create a session, then redirects. The Coal API key stays on your server.
| Prop | Type | Default | Description |
|---|---|---|---|
createSession | () => Promise<{ url?, sessionId?, id? }> | required | Server call that returns a checkout URL |
target | '_self' | '_blank' | '_self' | Redirect vs new tab |
onError | (error: Error) => void | — | Error handler |
children | ReactNode | "Pay with Coal" | Button content |
1<CoalBuyButton2 createSession={async () => {3 const res = await fetch('/api/checkout', { method: 'POST' });4 return res.json();5 }}6 onError={(err) => toast.error(err.message)}7>8 Buy Now — $29.999</CoalBuyButton>
<CoalProducts>
Renders a grid or list of products from your Coal merchant profile. Data is fetched from Coal's agent API automatically.
| Prop | Type | Default | Description |
|---|---|---|---|
merchantId | string | Provider's merchantId | Override merchant |
layout | 'grid' | 'list' | 'grid' | Layout mode |
columns | number | 3 | Grid columns |
renderProduct | (product) => ReactNode | — | Custom render function |
loadingContent | ReactNode | "Loading…" | Loading state |
emptyContent | ReactNode | "No products yet" | Empty state |
1<CoalProducts layout="grid" columns={2} />
<CoalProduct>
Single product card by ID.
1<CoalProduct id="product-id" />
<CoalAgentPublisher>
Publishes your product catalog to Coal for 0G-backed agent discovery. Posts to a merchant-owned proxy route — your server handles the Coal API key so it never reaches the browser.
| Prop | Type | Default | Description |
|---|---|---|---|
products | CoalAgentPublisherProduct[] | required | Your product catalog |
proxyUrl | string | '/api/coal/publish-catalog' | Your server-side proxy route |
mode | 'upsert' | 'replace' | 'upsert' | Upsert: only touch listed products. Replace: also archive unlisted external products. |
headers | Record<string, string> | — | Custom headers (CSRF tokens, shared secrets) |
debounceMs | number | 30000 | Minimum ms between publishes |
showStatus | boolean | false | Show a "Catalog indexed on 0G" badge |
onPublish | (result) => void | — | Success callback |
onError | (error: Error) => void | — | Error callback |
1<CoalAgentPublisher2 products={[3 { externalId: 'sku-1', name: 'Pro Plan', price: 29.99, description: '...' },4 { externalId: 'sku-2', name: 'Enterprise', price: 99, image: 'https://...' },5 ]}6 showStatus7 onPublish={(result) => console.log('Indexed:', result.zeroG.storageUri)}8/>
Each product needs an externalId — your own stable product ID. Coal upserts by (merchantId, externalId) so republishing is idempotent.
What happens under the hood:
- Component POSTs to your proxy route (server-side)
- Your proxy calls
publishCoalCatalog()with your Coal API key - Coal upserts products into its DB
- Coal publishes the merchant profile to 0G Storage (immutable log) + 0G KV (mutable mirror)
- Agents hitting
/api/agent/discoversee your products
<CoalSchemaOrg>
Injects Schema.org JSON-LD Product / Offer markup into the page. ChatGPT, Perplexity, and Google AI Overviews cite pages with this markup. XSS-safe — uses safeJsonForScriptTag() internally.
1<CoalSchemaOrg />
useCoalReceipt(sessionId, options?)
Polls Coal for a payment receipt until the 3-step proof trail is fully verified: Base TX → 0G Storage → 0G Chain.
1const {2 receipt, // CoalReceipt object3 loading, // true while polling4 error, // string | null5 fullyVerified, // true when all 3 steps are green6 verifiedSteps, // 0..37} = useCoalReceipt(sessionId, {8 pollIntervalMs: 2000, // default9 timeoutMs: 60000, // default10});
Use on your success page to show the customer their verifiable receipt in real time.
useCoalCheckout(options)
Low-level hook for custom checkout flows (same as v0.3).
1const { checkoutUrl, redirectToCheckout, openCheckout } = useCoalCheckout({2 sessionId: 'abc123',3});
Server helper
publishCoalCatalog(options)
Import from coal-react/server. This module is server-only — it throws at load time if imported in a browser context to prevent API key leaks.
1import { publishCoalCatalog, CoalPublishError } from 'coal-react/server';23try {4 const result = await publishCoalCatalog({5 merchantId: 'your-merchant-id',6 apiKey: process.env.COAL_API_KEY!,7 products: [8 { externalId: 'sku-1', name: 'Pro Plan', price: 29.99 },9 ],10 mode: 'upsert',11 });1213 console.log(result.zeroG.storageUri); // "0g://log/0x..."14} catch (err) {15 if (err instanceof CoalPublishError) {16 console.error(err.status, err.code, err.message);17 // 401 UNAUTHORIZED, 429 RATE_LIMITED, 400 VALIDATION_ERROR18 }19}
Price validation: The helper validates each product's price before the network call — rejects NaN, Infinity, negative, zero, >1,000,000, and >6 decimal places with clear error messages.
Proxy route pattern
Your <CoalAgentPublisher> component posts to YOUR server. Your server calls publishCoalCatalog() with the API key.
1// app/api/coal/publish-catalog/route.ts2import { publishCoalCatalog } from 'coal-react/server';34export async function POST(request: Request) {5 // ⚠️ Add your own auth here (session check, CSRF token, etc.)6 const body = await request.json();78 const result = await publishCoalCatalog({9 merchantId: process.env.NEXT_PUBLIC_COAL_MERCHANT_ID!,10 apiKey: process.env.COAL_API_KEY!,11 products: body.products,12 mode: body.mode ?? 'upsert',13 });1415 return Response.json(result);16}
Next.js route helpers
Import from coal-react/next. Drop these into your Next.js App Router and your site becomes findable by AI agents across multiple discovery protocols.
createAgentCardRoute(options)
Serves an A2A Agent Card at /.well-known/agent-card.json (Google + Linux Foundation spec).
1// app/.well-known/agent-card.json/route.ts2import { createAgentCardRoute } from 'coal-react/next';34export const GET = createAgentCardRoute({5 merchantId: 'your-merchant-id',6 name: 'My Store', // optional7 description: 'We sell...', // optional8 cacheMaxAge: 300, // Cache-Control max-age in seconds (default: 300)9});
The handler fetches your merchant profile from Coal's public agent API (backed by 0G Storage + KV), transforms it into the A2A Agent Card format, and adds capabilities, payment info, catalog preview, and 0G proof metadata.
createLlmsTxtRoute(options)
Serves /llms.txt (Answer.AI convention). ChatGPT, Perplexity, and Google AI Overviews read this when citing your site.
1// app/llms.txt/route.ts2import { createLlmsTxtRoute } from 'coal-react/next';34export const GET = createLlmsTxtRoute({5 merchantId: 'your-merchant-id',6 title: 'My Store',7 summary: 'We sell digital goods payable in USDC on Base via Coal.',8});
createX402ManifestRoute(options)
Serves /.well-known/x402.json for x402 Bazaar (Coinbase).
1// app/.well-known/x402.json/route.ts2import { createX402ManifestRoute } from 'coal-react/next';34export const GET = createX402ManifestRoute({5 merchantId: 'your-merchant-id',6});
Lists all of your x402 paywall endpoints with verify URLs so agent crawlers and the Coinbase Bazaar indexer can discover and pay for gated content.
TypeScript types
All types are exported from their respective sub-paths:
1// Client components2import type {3 CoalCheckoutButtonProps,4 CoalBuyButtonProps,5 CoalProductsProps,6 CoalAgentPublisherProps,7 CoalSchemaOrgProps,8 CoalProductSummary,9 CoalPaywallSummary,10 CoalMerchantProfile,11 CoalReceipt,12 UseCoalCheckoutOptions,13 UseCoalCheckoutReturn,14 UseCoalReceiptOptions,15 UseCoalReceiptResult,16} from 'coal-react';1718// Server helper19import type {20 CoalCatalogProduct,21 PublishCoalCatalogOptions,22 PublishCoalCatalogResult,23 CoalPublishError,24} from 'coal-react/server';2526// Next.js route helpers27import type {28 AgentCardRouteOptions,29 LlmsTxtRouteOptions,30 X402ManifestRouteOptions,31} from 'coal-react/next';
Backwards-compatible aliases
CoalWidget and CoalCheckout are exported as aliases for CoalCheckoutButton. They are deprecated — use CoalCheckoutButton in new code.
Examples
- demo-store — Full storefront with agent manifests + catalog indexing (source)
- coal-react-checkout — Every Coal feature: checkout, paywalls, splits, webhooks, bring-your-own-catalog (source)
- coal-agent — AI agent sandbox with autonomous purchases
Next steps
- Catalog Indexing — full guide to publishing your catalog for agent discovery
- Agent Discovery — how agents find and pay your store
- Agent Payments — gasless ERC-3009 agent wallets
- Paywalls — x402 protocol for machine-to-machine payments
- Webhooks — confirm payments server-side
