coal
coal

Products

Products are the catalog items that back your payment links. Each product has a fixed price, optional image, and optional SKU for inventory tracking. When you attach a product to a Payment Link, Coal automatically pulls in the name, image, and price on the checkout page.

Products are optional. If you want buyers to enter a custom amount, create a flexible Payment Link with no product attached. See Payment Links.


Product Object

json
1{
2 "id": "clz9prod123",
3 "name": "Pro Plan",
4 "description": "Unlimited projects and priority support.",
5 "price": "49.00",
6 "image": "https://cdn.example.com/pro-plan.png",
7 "sku": "PLAN-PRO-MONTHLY",
8 "active": true,
9 "sales": 142,
10 "createdAt": "2026-03-01T09:00:00.000Z",
11 "updatedAt": "2026-03-20T14:30:00.000Z"
12}

Field reference

FieldTypeDescription
idstringUnique identifier (CUID).
namestringProduct display name shown on the checkout page.
descriptionstring?Optional description shown below the name.
pricestringFixed price as a decimal string in the merchant's settlement currency.
imagestring?URL of the product image. Displayed as a square thumbnail at checkout.
skustring?Your internal SKU. Not shown to buyers; useful for inventory webhooks.
activebooleanfalse means the product is archived and cannot be linked to new links.
salesnumberCount of confirmed checkout sessions for this product.
createdAtstringISO 8601 timestamp.
updatedAtstringISO 8601 timestamp of last modification.

Creating a Product

Via the Console

  1. Go to Console → Products → New Product.
  2. Enter a name and price.
  3. Optionally add a description, upload an image, and set a SKU.
  4. Click Create Product. It appears in your catalog immediately.

Via the API

POST /api/console/products

This is a console-managed surface. The public merchant API uses x-api-key; /api/console/* routes use Privy Bearer tokens and are dashboard-only.

text
1Authorization: Bearer <Privy JWT>
2Content-Type: application/json

Request body

FieldTypeRequiredDescription
namestringYesDisplay name.
pricenumberYesPrice in the merchant's settlement currency.
descriptionstringNoShort description.
imagestringNoPublicly accessible image URL.
skustringNoYour internal SKU or inventory code.

cURL

bash
1curl -X POST https://api.usecoal.xyz/api/console/products \
2 -H "Content-Type: application/json" \
3 -H "Authorization: Bearer <Privy JWT>" \
4 -d '{
5 "name": "Pro Plan",
6 "description": "Unlimited projects and priority support.",
7 "price": 49,
8 "image": "https://cdn.example.com/pro-plan.png",
9 "sku": "PLAN-PRO-MONTHLY"
10 }'

Returns 201 Created with the full product object.

Node.js

typescript
1const res = await fetch('https://api.usecoal.xyz/api/console/products', {
2 method: 'POST',
3 headers: {
4 'Content-Type': 'application/json',
5 'Authorization': 'Bearer <Privy JWT>',
6 },
7 body: JSON.stringify({
8 name: 'Pro Plan',
9 description: 'Unlimited projects and priority support.',
10 price: 49,
11 image: 'https://cdn.example.com/pro-plan.png',
12 sku: 'PLAN-PRO-MONTHLY',
13 }),
14});
15
16const product = await res.json();
17console.log(product.id); // clz9prod123

Listing Products

GET /api/console/products

Returns all active products for your account, sorted newest first. Each product includes a sales count of confirmed payments.

bash
1curl https://api.usecoal.xyz/api/console/products \
2 -H "Authorization: Bearer <Privy JWT>"
json
1{
2 "products": [
3 {
4 "id": "clz9prod123",
5 "name": "Pro Plan",
6 "description": "Unlimited projects and priority support.",
7 "price": "49.00",
8 "image": "https://cdn.example.com/pro-plan.png",
9 "sales": 142
10 },
11 {
12 "id": "clz9prod456",
13 "name": "Starter Pack",
14 "description": null,
15 "price": "9.00",
16 "image": null,
17 "sales": 38
18 }
19 ]
20}

Updating a Product

PUT /api/console/products/:id

All fields are optional — only send what you want to change.

bash
1curl -X PUT https://api.usecoal.xyz/api/console/products/clz9prod123 \
2 -H "Content-Type: application/json" \
3 -H "Authorization: Bearer <Privy JWT>" \
4 -d '{ "price": 59, "description": "Now includes AI features." }'

Price changes are not retroactive. Existing confirmed sessions keep the amount they were created with. Only new sessions created after the price change will use the updated price.


Deleting a Product

DELETE /api/console/products/:id

Products are soft-deleted — active is set to false. The product disappears from your catalog, and any Payment Links that reference it stop resolving.

bash
1curl -X DELETE https://api.usecoal.xyz/api/console/products/clz9prod123 \
2 -H "Authorization: Bearer <Privy JWT>"

Product Examples

Digital product

json
1{
2 "name": "eBook: Crypto Payments Explained",
3 "description": "A 60-page guide to building on Coal.",
4 "price": 15,
5 "image": "https://cdn.example.com/ebook-cover.png",
6 "sku": "EBOOK-CRYPTO-V1"
7}

After purchase, use a webhook (checkout.session.completed) to send the buyer a download link.

Physical product

json
1{
2 "name": "Coal Merch Hoodie — Black L",
3 "description": "100% organic cotton. Ships in 3–5 business days.",
4 "price": 65,
5 "image": "https://cdn.example.com/hoodie-black-l.jpg",
6 "sku": "MERCH-HOODIE-BLK-L"
7}

Use the sku field to match the confirmed payment to your inventory management system.

Subscription (recurring billing pattern)

Coal now supports recurring products natively:

  1. Create a product with billingType: "subscription".
  2. Set billingInterval and billingIntervalCount, for example:
json
1{
2 "name": "Pro Plan",
3 "price": 49,
4 "billingType": "subscription",
5 "billingInterval": "month",
6 "billingIntervalCount": 1
7}

On the first paid cycle, Coal stores the buyer's billing mandate and creates the subscription record. Future renewal checkout sessions are generated automatically on the configured cadence, and merchants can manage the billing state from the dashboard.


Best Practices

Use SKUs for inventory sync. The sku field is echoed back in webhook payloads. If your warehouse or fulfillment system uses SKUs, set the product SKU to match so you can look up the order automatically on confirmation.

Use images for branding. Products with a high-quality image convert better. Host images on a CDN (see Managing Products) and use HTTPS URLs. Coal does not resize images — aim for square images at 400×400 px or larger.

Do not delete products with active links. Deleting a product deactivates any Payment Links that reference it. Archive it only when you are certain no buyer will visit those links.