Payment links
Create, list, read and cancel one-off payment links from your own code. The same links you make in the dashboard, with the same rules, each one payment for one payer.
On this page
The payment link object
A payment link asks one payer for one amount: an invoice, a deposit, a one-off order. It's the same link you can make under Payment links in the dashboard, and it shows up there. A link is one payment, so its id is the payment's id: read its settlement with Retrieve a payment. For a price many buyers can pay, use a checkout instead.
| Field | Type | Description |
|---|---|---|
id | string | The link's id, pay_…. The same id identifies its payment |
object | string | "payment_link" |
url | string | The payer's page. Send this to them |
tracking_url | string or null | A page that shows only the link's status, without the payer's details. Safe to share with anyone who should follow it |
status | string | open, pending, paid, held, failed or expired. See statuses |
livemode | boolean | false for Test mode, true for Live mode |
amount | string | The amount asked for: "250.00" |
currency | string | USD, EUR, GBP, CAD or AUD |
description | string | What the payment is for, shown to the payer |
reference | string or null | Your own id, such as an invoice number |
customer_name | string or null | Who the link is for |
customer_email | string or null | Their email, filled in on their page, lowercased |
memo | string or null | Your note to the payer |
success_url | string or null | Where the payer lands once paid |
collect | object or null | What the payer's page asks for besides their email, or null for nothing more. See The collect field |
order_id | string or null | The order the link belongs to, ord_… |
created | integer | When it was created, in Unix seconds |
expires_at | integer | When it stops accepting new payments |
started_at | integer or null | When the payer started paying |
paid_at | integer or null | When it was paid |
canceled_at | integer or null | When you canceled it, in Unix seconds |
{
"id": "pay_Lk4mQ8vR2xTn7WcZs1Pb",
"object": "payment_link",
"url": "https://pay.railbed.io/p/pay_Lk4mQ8vR2xTn7WcZs1Pb",
"tracking_url": "https://pay.railbed.io/t/Tq3nV8xK2mRz7LpW4cYb5sDh",
"status": "open",
"livemode": false,
"amount": "250.00",
"currency": "USD",
"description": "Consulting, September",
"reference": "INV-0042",
"customer_name": "Ada Lovelace",
"customer_email": "ada@example.com",
"memo": "Thanks for your business.",
"success_url": null,
"collect": null,
"order_id": "ord_Hn6pW2zQ8rKt3VmXc9La",
"created": 1790380525,
"expires_at": 1791590125,
"started_at": null,
"paid_at": null,
"canceled_at": null
}Create a payment link
POST/v1/payment_links
Creates a link in the key's mode. Nothing is charged and no provider is contacted until the payer starts paying, so a long expiry costs nothing.
| Parameter | Type | Description |
|---|---|---|
amount | string Required | A decimal string from "1.00" to "100000.00", at most two decimals |
currency | string Required | USD, EUR, GBP, CAD or AUD, in any letter case |
description | string Required | What it's for, up to 120 characters |
customer_name | string | Who it's for, up to 80 characters |
customer_email | string | Their email, filled in on their page |
customer_phone | string | Their phone number, up to 40 characters, kept on the order |
shipping_address, billing_address | object | Their addresses, if you have them: line1, line2, city, region, postal_code and country (two letters). Kept on the order and filled in for the payer, under customer_name. region is a code for the US, Canada and Australia (NY) |
reference | string | Your own id, up to 64 characters |
memo | string | A note to the payer, up to 500 characters |
expires_in_days | integer | 1–30. Default 7 |
success_url | string | Where the payer lands once paid. Live mode needs https:// |
collect | object | What the payer's page asks for besides their email, as on a checkout session |
curl https://pay.railbed.io/v1/payment_links \
-H "Authorization: Bearer $RAILBED_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"amount": "250.00",
"currency": "USD",
"description": "Consulting, September",
"customer_name": "Ada Lovelace",
"customer_email": "ada@example.com",
"reference": "INV-0042",
"memo": "Thanks for your business.",
"expires_in_days": 14
}'const link = await fetch('https://pay.railbed.io/v1/payment_links', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.RAILBED_SECRET_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
amount: '250.00',
currency: 'USD',
description: 'Consulting, September',
customer_name: 'Ada Lovelace',
customer_email: 'ada@example.com',
reference: 'INV-0042',
expires_in_days: 14,
}),
}).then((r) => r.json());
// Send link.url to the payer.link = requests.post(
"https://pay.railbed.io/v1/payment_links",
headers={"Authorization": f"Bearer {os.environ['RAILBED_SECRET_KEY']}"},
json={
"amount": "250.00",
"currency": "USD",
"description": "Consulting, September",
"customer_name": "Ada Lovelace",
"customer_email": "ada@example.com",
"reference": "INV-0042",
"expires_in_days": 14,
},
timeout=15,
).json()
# Send link["url"] to the payer.{
"id": "pay_Lk4mQ8vR2xTn7WcZs1Pb",
"object": "payment_link",
"url": "https://pay.railbed.io/p/pay_Lk4mQ8vR2xTn7WcZs1Pb",
"tracking_url": "https://pay.railbed.io/t/Tq3nV8xK2mRz7LpW4cYb5sDh",
"status": "open",
"livemode": false,
"amount": "250.00",
"currency": "USD",
"description": "Consulting, September",
"reference": "INV-0042",
"customer_name": "Ada Lovelace",
"customer_email": "ada@example.com",
"memo": "Thanks for your business.",
"success_url": null,
"collect": null,
"order_id": "ord_Hn6pW2zQ8rKt3VmXc9La",
"created": 1790380525,
"expires_at": 1791590125,
"started_at": null,
"paid_at": null,
"canceled_at": null
}The link follows the dashboard's rules, and an error's field names the parameter you sent, such as expires_in_days or shipping_address.postal_code. This endpoint ignores Idempotency-Key: after a timeout, list your links by reference before creating another.
| Error | Cause |
|---|---|
400 invalid_amount | amount isn't a decimal string from "1.00" to "100000.00" |
400 invalid_currency | currency isn't one of the five |
400 missing_description, 400 too_long | description is empty, or a text field is over its limit |
400 invalid_customer_name, 400 invalid_customer_phone, 400 invalid_reference, 400 invalid_memo | That field isn't a string |
400 invalid_email | customer_email isn't an email address (send null, or leave it out, for none) |
400 invalid_address | An address is missing a line, city, region or postal code its country needs. field names it |
400 invalid_expiry | expires_in_days isn't a whole number from 1 to 30 (a JSON number, not a string) |
400 invalid_url | success_url isn't a full address, has a username or password, or isn't https:// in Live mode |
400 invalid_collect | collect asks for something it can't. field names the part |
409 no_payout_wallet | Live mode, and the account has no payout wallet yet |
409 commerce_paused | The account has paused taking payments |
403 integration_disabled | Payment links, or the API, are turned off for the account |
403 merchant_read_only | The account is suspended or archived |
Retrieve a payment link
GET/v1/payment_links/:id
Returns one of your links in the key's mode. Another account's, one in the other mode, or a payment that isn't a link is 404 not_found. A link past its expires_at reads as expired.
curl https://pay.railbed.io/v1/payment_links/pay_Lk4mQ8vR2xTn7WcZs1Pb \
-H "Authorization: Bearer $RAILBED_SECRET_KEY"List payment links
GET/v1/payment_links
Returns your links in the key's mode, newest first. The parameters are those of List payments.
| Parameter | Type | Description |
|---|---|---|
limit | integer | 1–100. Default 20 |
starting_after | string | A link id from the previous page's next_cursor |
status | string | open, pending, paid, held, failed or expired (a canceled link is expired) |
reference | string | Only links with exactly this reference |
created_gte, created_lte | integer | Only links created in this range, in Unix seconds, inclusive |
curl "https://pay.railbed.io/v1/payment_links?status=open" \
-H "Authorization: Bearer $RAILBED_SECRET_KEY"{
"data": [
{
"id": "pay_Lk4mQ8vR2xTn7WcZs1Pb",
"object": "payment_link",
"url": "https://pay.railbed.io/p/pay_Lk4mQ8vR2xTn7WcZs1Pb",
"tracking_url": "https://pay.railbed.io/t/Tq3nV8xK2mRz7LpW4cYb5sDh",
"status": "open",
"livemode": false,
"amount": "250.00",
"currency": "USD",
"description": "Consulting, September",
"reference": "INV-0042",
"customer_name": "Ada Lovelace",
"customer_email": "ada@example.com",
"memo": "Thanks for your business.",
"success_url": null,
"collect": null,
"order_id": "ord_Hn6pW2zQ8rKt3VmXc9La",
"created": 1790380525,
"expires_at": 1791590125,
"started_at": null,
"paid_at": null,
"canceled_at": null
}
],
"has_more": false,
"next_cursor": null
}Cancel a payment link
POST/v1/payment_links/:id/cancel
Closes a link nobody has started paying, as Cancel link does in the dashboard. No body is needed.
curl -X POST https://pay.railbed.io/v1/payment_links/pay_Lk4mQ8vR2xTn7WcZs1Pb/cancel \
-H "Authorization: Bearer $RAILBED_SECRET_KEY"{
"id": "pay_Lk4mQ8vR2xTn7WcZs1Pb",
"object": "payment_link",
"url": "https://pay.railbed.io/p/pay_Lk4mQ8vR2xTn7WcZs1Pb",
"tracking_url": "https://pay.railbed.io/t/Tq3nV8xK2mRz7LpW4cYb5sDh",
"status": "expired",
"livemode": false,
"amount": "250.00",
"currency": "USD",
"description": "Consulting, September",
"reference": "INV-0042",
"customer_name": "Ada Lovelace",
"customer_email": "ada@example.com",
"memo": "Thanks for your business.",
"success_url": null,
"collect": null,
"order_id": "ord_Hn6pW2zQ8rKt3VmXc9La",
"created": 1790380525,
"expires_at": 1791590125,
"started_at": null,
"paid_at": null,
"canceled_at": 1790384125
}It follows the rules of Cancel a checkout session: the link becomes expired with canceled_at set and sends payment.canceled; canceling again returns the same link; once the payer has gone to a card provider it answers 409 not_cancelable, because their payment may be on its way. Money that arrives after a cancel still settles the link.
Access
Retrieving and listing need the payments:read scope, which Read only keys have. Creating and canceling need checkouts:write, which Full access keys and agents with full access have.