Payment Links
Payment Links are shareable URLs that let you accept crypto payments without writing a single line of code. Send one in an email, post it on social media, or drop it in a Notion page — anyone who clicks it lands on a hosted Coal checkout page branded with your merchant profile.
This is a console-managed surface. The public merchant API uses x-api-key; these /api/console/* routes use Privy Bearer tokens.
Note: Payment Links are ideal for direct sales, content creators, and no-code use cases. If you need programmatic control over the checkout flow, see Checkout Sessions.
Types of Payment Links
Product-Linked
A product-linked link is tied to a specific item in your product catalog. The checkout page automatically displays the product name, image, description, and fixed price. The buyer cannot change the amount.
Use this for: fixed-price digital goods, SaaS plan purchases, event tickets.
Flexible (Custom Amount)
A flexible link has no product attached. The buyer enters any amount they choose before paying. You can provide a title and description to give context.
Use this for: donations, tips, pay-what-you-want offers, open-source sponsorships.
Creating a Payment Link in the Console
- Go to the Console and navigate to Payment Links in the sidebar.
- Click Create Link.
- Choose a type:
- Select a product from the dropdown to create a product-linked link.
- Leave the product field empty to create a flexible link.
- Set a slug (optional). The slug becomes the last segment of your URL:
usecoal.xyz/pay/{slug}. If you leave it blank, Coal generates a random one for you. - For flexible links, add a Title and Description so buyers know what they are paying for.
- Click Create — your link is live instantly.
Creating a Payment Link via API
POST /api/console/links
Requires a valid session (authenticated merchant). All requests must include:
1Authorization: Bearer <Privy JWT>
Product-linked link
1curl -X POST https://api.usecoal.xyz/api/console/links \2 -H "Content-Type: application/json" \3 -H "Authorization: Bearer <Privy JWT>" \4 -d '{5 "productId": "clxxx",6 "slug": "pro-plan"7 }'
Flexible link
1curl -X POST https://api.usecoal.xyz/api/console/links \2 -H "Content-Type: application/json" \3 -H "Authorization: Bearer <Privy JWT>" \4 -d '{5 "title": "Donation",6 "description": "Support our open-source project",7 "slug": "donate"8 }'
Request body parameters
| Field | Type | Required | Description |
|---|---|---|---|
productId | string | No | ID of a product from your catalog. Mutually exclusive with title. |
slug | string | No | Custom URL-safe slug. Auto-generated if omitted. |
title | string | No | Display title for flexible links. |
description | string | No | Description shown on the checkout page. |
Response
1{2 "id": "clz9abc123",3 "slug": "pro-plan",4 "url": "https://usecoal.xyz/pay/pro-plan",5 "active": true,6 "productId": "clxxx",7 "title": null,8 "description": null,9 "createdAt": "2026-03-22T10:00:00.000Z"10}
URL Structure
Every payment link resolves to:
1https://usecoal.xyz/pay/{slug}
The slug must be URL-safe (letters, numbers, hyphens). Slugs are globally unique across Coal — if a slug is already taken, the API returns a 409 SLUG_TAKEN error.
Listing Payment Links
GET /api/console/links
Returns all active links for your account, including the resolved product name and price.
1curl https://api.usecoal.xyz/api/console/links \2 -H "Authorization: Bearer <Privy JWT>"
1{2 "links": [3 {4 "id": "clz9abc123",5 "slug": "pro-plan",6 "url": "https://usecoal.xyz/pay/pro-plan",7 "productName": "Pro Plan",8 "productImage": "https://cdn.example.com/pro.png",9 "price": "49.00",10 "active": true,11 "createdAt": "2026-03-22T10:00:00.000Z"12 },13 {14 "id": "clz9def456",15 "slug": "donate",16 "url": "https://usecoal.xyz/pay/donate",17 "productName": "Donation",18 "productImage": null,19 "price": "Flexible",20 "active": true,21 "createdAt": "2026-03-20T08:30:00.000Z"22 }23 ]24}
Deleting a Payment Link
DELETE /api/console/links/:id
Links are soft-deleted — the record is kept in the database but active is set to false. The URL immediately stops resolving for buyers.
1curl -X DELETE https://api.usecoal.xyz/api/console/links/clz9abc123 \2 -H "Authorization: Bearer <Privy JWT>"
Returns the updated link object with active: false.
Node.js Examples
Create a product-linked link
1const response = await fetch('https://api.usecoal.xyz/api/console/links', {2 method: 'POST',3 headers: {4 'Content-Type': 'application/json',5 'Authorization': 'Bearer <Privy JWT>',6 },7 body: JSON.stringify({8 productId: 'clxxx',9 slug: 'pro-plan',10 }),11});1213const link = await response.json();14console.log(link.url); // https://usecoal.xyz/pay/pro-plan
Create a flexible donation link
1const response = await fetch('https://api.usecoal.xyz/api/console/links', {2 method: 'POST',3 headers: {4 'Content-Type': 'application/json',5 'Authorization': 'Bearer <Privy JWT>',6 },7 body: JSON.stringify({8 title: 'Support Us',9 description: 'Every payment helps keep the servers on.',10 slug: 'support',11 }),12});1314const link = await response.json();15console.log(link.url); // https://usecoal.xyz/pay/support
List and delete links
1// List2const res = await fetch('https://api.usecoal.xyz/api/console/links', {3 headers: { 'Authorization': 'Bearer <Privy JWT>' },4});5const { links } = await res.json();67// Delete the first link8const deleteRes = await fetch(9 `https://api.usecoal.xyz/api/console/links/${links[0].id}`,10 {11 method: 'DELETE',12 headers: { 'Authorization': 'Bearer <Privy JWT>' },13 }14);
Embedding Payment Links
Payment links are plain URLs — you can embed them anywhere a hyperlink works.
Email campaigns — Link a button to https://usecoal.xyz/pay/your-slug. No redirects, no tracking pixels required.
Social media — Post the link directly. The checkout page has Open Graph meta tags so it renders a preview card on Twitter/X, LinkedIn, and Discord.
Notion / Confluence — Paste the URL as a linked button block. Notion automatically unfurls it as a bookmark card.
QR codes — Use any QR generator (e.g. qrcode.react) to encode the URL for print materials or event badges.
Direct messages — Drop the URL in Telegram, Slack, or iMessage. Coal handles the rest.
