Payment links
A hosted page that collects a payment without any code.
Contents
What a payment link is
A payment link is a hosted page that collects a payment without you building a checkout. A link can take naira by bank transfer, dollars by USDT or USDC, or both. Bank transfer uses a dedicated virtual account. Crypto settles into a USD balance after the network confirms the transfer.
Use one for an invoice, a donation page, an event ticket, or anywhere you want to take money without shipping code.
Create them from your server, or from the dashboard
payment_links scope — a payment link is a way of opening a collection, so collections:read and collections:write cover it.Creating one per order from your server is what powers the checkout widget: your server sets the amount, hands the browser a slug, and the customer pays in a modal on your own page.
Customers always pay on the hosted page. There is no public checkout API for your server to call — if you want to collect without a hosted page at all, mint a virtual account instead.
Create a payment link
/v1/payment-links- Auth
- Secret key or dashboard session
- Scope
- collections:write
Body
| Field | Type | Description |
|---|---|---|
| titlerequired | string | 1–120 characters. The heading on the hosted page. |
| description | string | Up to 2000 characters, shown under the title. |
| customer | object | Details you already hold: name, email, phone, any subset. Each one is shown on the checkout but not editable, and is what lands on the collection — the payer’s browser cannot substitute another. Use it so a signed-in customer is not asked for an email you just authenticated them with. |
| amountTyperequired | string | fixed — you set the amount. customer_chooses — the payer enters one, optionally bounded. |
| amount | money | Required when amountType is fixed. Minor units in pricingCurrency (kobo or cents). |
| methods | string[] | bank_transfer, crypto, or both. Defaults to bank transfer only. |
| pricingCurrency | string | NGN or USD. The amount you set is in this currency. The other rail is derived from the platform rate and frozen onto the checkout when the payer starts it. |
| cryptoWindowMinutes | number | How long a crypto checkout stays open. Defaults to 60. Maximum 1440. Bank transfer stays 30 minutes. |
| minimumAmount | money | Optional floor for customer_chooses. |
| maximumAmount | money | Optional ceiling for customer_chooses. |
| linkTyperequired | string | one_time closes after the first successful payment. reusable keeps accepting payments until disabled. |
| customerFieldsrequired | object | name, email, phone, each { collect: boolean, required: boolean }. |
| customFields | array | Up to 20 extra inputs, each { label, placeholder?, required }. Answers arrive on the collection. |
| expiresAt | string | null | ISO 8601. After this the page stops accepting payment. |
| successRedirectUrl | string | null | Optional absolute http:// or https:// URL. After a successful payment, the checkout page redirects here after a few seconds. |
| appearancerequired | object | theme, brandColor, logoDataUrl (or null), showAttribution. |
curl -X POST https://api.radokpay.com/v1/payment-links \
-H "Authorization: Bearer $RADOK_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Invoice #1042",
"description": "Design retainer, August 2026",
"customer": { "name": "Ada Lovelace", "email": "ada@example.com" },
"amountType": "fixed",
"amount": { "value": 25000000, "currency": "NGN" },
"linkType": "one_time",
"customerFields": {
"name": { "collect": true, "required": true },
"email": { "collect": true, "required": true },
"phone": { "collect": false, "required": false }
},
"customFields": [
{ "label": "Purchase order number", "required": false }
],
"expiresAt": "2026-09-23T00:00:00.000Z",
"successRedirectUrl": "https://example.com/thank-you",
"appearance": {
"theme": "neutral",
"brandColor": "#1a2a80",
"logoDataUrl": null,
"showAttribution": true
}
}'{
"data": {
"id": "652f1a9c4d7b3e0812345678",
"slug": "invoice-1042-9f4c1b",
"url": "https://pay.radokpay.com/pl/invoice-1042-9f4c1b",
"title": "Invoice #1042",
"amountType": "fixed",
"amount": { "value": 25000000, "currency": "NGN" },
"linkType": "one_time",
"status": "active",
"disabled": false,
"vaStatus": "ready",
"bankName": "Providus Bank",
"accountNumber": "9901234567",
"accountName": "RADOK/INVOICE 1042",
"successRedirectUrl": "https://example.com/thank-you",
"expiresAt": "2026-09-23T00:00:00.000Z",
"createdAt": "2026-08-23T11:30:00.000Z"
}
}Share only when vaStatus is ready
vaStatus: "ready" with bank details in the same response. If minting fails, vaStatus is failed (with vaError) and a background retry may still bring it to ready — re-read the link before sharing it.List payment links
/v1/payment-links- Auth
- Secret key or dashboard session
- Scope
- collections:read
Page-based, unlike the cursor pagination used elsewhere in the API.
Query
| Field | Type | Description |
|---|---|---|
| environmentrequired | string | test or live. |
| page | integer | 1-based. Defaults to 1. |
| pageSize | integer | 1–100. Defaults to 20. |
{
"data": [ { "id": "652f1a9c...", "slug": "invoice-1042-9f4c1b", "status": "active" } ],
"meta": { "total": 37, "page": 1, "pageSize": 20 }
}Retrieve and disable
/v1/payment-links/{id}- Auth
- Secret key or dashboard session
- Scope
- collections:read
/v1/payment-links/{id}/disable- Auth
- Secret key or dashboard session
- Scope
- collections:write
Stops the page accepting payment and cancels its virtual account. Payments already settled are unaffected.
curl -X POST https://api.radokpay.com/v1/payment-links/652f1a9c4d7b3e0812345678/disable \
-H "Authorization: Bearer $RADOK_SECRET_KEY"Payments against a link
/v1/payment-links/{id}/payments- Auth
- Secret key or dashboard session
- Scope
- collections:read
Returns the collections created by this link, newest first, with the same page-based meta as the list endpoint. Every row is a normal collection, so it carries the same amount, platformFee, and netAmount.
Events
- A paid link emits
collection.successfor both bank transfer and crypto. Crypto payloads also includemethod,received,overpaid, andfxRate. The payload carriespaymentLinkIdso you can attribute it. - A bank checkout lasts 30 minutes. A crypto checkout lasts
cryptoWindowMinutes(default 60). If nothing arrives, a bank session emitscollection.failedwithreason: "expired". A crypto session emitscollection.expired. A shortfall that did arrive emitscollection.underpaidand is still credited. - While a crypto transfer is on-chain but under the confirmation threshold, the collection is
confirmingand you receivecrypto.payment.confirming, thencrypto.payment.confirmedonce it finalizes. A transfer after expiry emitscollection.late_payment. A transfer after the collection already settled emitscrypto.unsolicited. Both are credited. - There is no separate
payment_link.*event. Subscribe to collection events and branch on the presence ofpaymentLinkId. - A
one_timelink is marked paid and its account cancelled after the first success, so a second payment against it cannot happen.