Skip to main content

Payment links

A page we host, at a URL you send to whoever is paying. No code, no embedding, no checkout page of your own.

A link is not a factory that mints a session per visitor. It owns an address and a running total, which is why it has no countdown: a link closes on a threshold or a date, not on a per-visitor timer.

Two modes

fixed — one amount, one payer

A preconfigured amount and a unique address generated for the page. On payment the page becomes unavailable.

curl -X POST $CHAINOS/v1/payments/links \
-H "X-API-Key: $CHAINOS_PAYMENT_SECRET" \
-H "Content-Type: application/json" \
-d '{
"mode": "fixed",
"chain": "base",
"asset": "USDC",
"amount": "1250.00",
"title": "Invoice 4418",
"description": "Consulting, August",
"collectEmail": true
}'

The URL is generated and never chosen. A guessable slug on an invoice publishes what one customer was charged, so fixed links always get a random one.

donation — many payers, accumulating

Arbitrary amounts from many payers, accumulating until a threshold is reached or a closing date passes, after which the page becomes unavailable.

curl -X POST $CHAINOS/v1/payments/links \
-H "X-API-Key: $CHAINOS_PAYMENT_SECRET" \
-H "Content-Type: application/json" \
-d '{
"mode": "donation",
"addressMode": "per_payer",
"chain": "polygon",
"asset": "USDT",
"thresholdAmount": "50000",
"closesAt": "2026-12-31T23:59:59Z",
"minAmount": "1",
"slug": "harvest-appeal",
"title": "Harvest appeal",
"collectEmail": true
}'

A donation link must have a threshold, a closing date, or both. One with neither is refused: an open-ended link is an address nobody ever stops watching.

Custom slugs are honoured on donation links only, for the same reason fixed links do not get them.

addressMode decides whether donors get their names on receipts

This is the decision worth making deliberately.

per_payer (default)shared
AddressOne derived per donorOne for the life of the link
The donorEnters an email, gets their own addressSends to the address on the page
ReceiptsAttributable, with a nameAnonymous unless volunteered
Concurrent donorsTold apart exactlyBest effort
Printable, postableNoYes — that is the point
InvoicesIssuedNone

A shared address is what you want on a poster, a printed QR or a page you link from social media. The cost is real and stated: two donors paying at the same moment cannot be told apart well enough to put a name on a receipt, so no invoice is issued at all. It is the same shared-address-plus-memo model ChainOS deliberately rejected for XRP and Stellar, and it matters less here only because nothing ships.

Every payment still accumulates toward the link's total in both modes.

fixed links are always effectively per_payer with a single payer.

What comes back

{
"id": "0b1f…",
"slug": "harvest-appeal",
"url": "https://pay.your-company.example/pay/harvest-appeal",
"mode": "donation",
"addressMode": "per_payer",
"amountRaised": "0",
"paymentCount": 0,
"thresholdAmount": "50000",
"closesAt": "2026-12-31T23:59:59Z",
"address": null,
"status": "active"
}

address is present only in shared mode, where the link itself holds it.

amountRaised is confirmed money only. A reorg can still undo a detected payment, and a total that counted unconfirmed deposits would go backwards on a public page.

Closing

A link closes by itself when its condition is met, and closedReason says which:

closedReasonMeaning
paidA fixed link took its payment
thresholdA donation link reached its target
dateThe closing date passed
manualSomebody closed it

Closing is irreversible; the total is frozen at what it raised. To take a page down temporarily, pause it instead — the total is kept and resuming puts it back.

curl -X POST $CHAINOS/v1/payments/links/$ID/pause -H "X-API-Key: $KEY"
curl -X POST $CHAINOS/v1/payments/links/$ID/resume -H "X-API-Key: $KEY"
curl -X POST $CHAINOS/v1/payments/links/$ID/close -H "X-API-Key: $KEY"

A closed page does not mean money sent to it is lost. The address keeps being monitored until monitorUntil and late funds are credited, exactly as they are for a checkout session — see Late payments.

Webhooks

Payments through a link fire the same payment_* events as any other, with linkId set and createdVia: "link". payment_link_created and payment_link_closed cover the link itself.

For a donation campaign, payment_link_closed carries amountRaised and paymentCount — the two numbers you want for the announcement.

Editing

PATCH /v1/payments/links/{id} changes presentation, bounds and closing conditions: title, description, successUrl, collectEmail, minAmount, maxAmount, thresholdAmount, closesAt, maxUses, metadata.

It does not change the chain, the asset, the mode or a fixed link's amount. Those are what the address was derived for and what donors have already paid against; changing them would rewrite the meaning of money already received.

In the portal

Payment links in the sidebar. Mode, raised-against-target, what will close it and when, and copy-URL, pause and close on each row. Closing is behind a confirmation because it cannot be undone.