Payment links

Reusable hosted payment pages — one link, unlimited buyers.

Payment links

A payment link is a reusable hosted payment page. Create it once, share the URL with as many buyers as you like — every buyer pays separately and each purchase becomes its own payment.

Links are created in the dashboard (Payment links) or over the API. Both paths share the exact same validation, limits and buyer-data rules.

Base URL: https://api.southbill.com/v1 Auth: Authorization: Bearer sk_live_… (or sk_test_… for sandbox links)

Action Scope
Read (GET) payments:read
Write (POST, PATCH) checkout:write

Writes accept Idempotency-Key. Amounts are integers in minor units, currencies lowercase.

The link address

Addresses are always generated by Southbill: /i/ plus 9 random, case-sensitive characters, e.g.

https://payments.southbill.com/i/sNdnaxXi9

You cannot choose or change the code. Amount and currency are always resolved server-side from the stored link — never from the URL.

Endpoints

Method Path Description
GET /v1/payment_links List links (limit <= 100, starting_after, active, archived)
POST /v1/payment_links Create a link
GET /v1/payment_links/:id Retrieve by id or code
POST or PATCH /v1/payment_links/:id Update a link
POST /v1/payment_links/:id/archive Archive (stops accepting buyers)
POST /v1/payment_links/:id/unarchive Restore an archived link
GET /v1/payment_links/:id/payments Purchases made through the link (limit, status)

Create

curl https://api.southbill.com/v1/payment_links \
  -H "Authorization: Bearer sk_live_…" \
  -H "Idempotency-Key: 4f1c9c0a-…" \
  -H "Content-Type: application/json" \
  -d '{
    "pricing_mode": "product",
    "merchant_price_id": "9f0c…",
    "headline": "Summer workshop ticket",
    "cta_label": "book_now",
    "quantity_mode": "customer",
    "max_quantity": 4,
    "max_uses": 50,
    "expires_at": "2026-12-31T23:59:59Z"
  }'

Fields

Pricing

Field Type Notes
pricing_mode product or custom Default product
merchant_price_id (alias price_id) string Required for product. Must be an active, one-off price you own. Recurring prices need a subscription link.
amount_cents integer Required for custom. Minor units, above the currency minimum, max 100,000,000.
currency string Required for custom (lowercase ISO code). For product it is taken from the price.
allow_custom_amount boolean Buyer chooses the amount (donations, pay-what-you-want)
min_amount_cents / max_amount_cents integer or null Only with allow_custom_amount

Quantity

Field Type Notes
quantity_mode fixed or customer Default fixed
fixed_quantity integer 1–10000, default 1
max_quantity integer 1–10000, default 10 (used with customer)

Payment page

Field Type Notes
headline string, max 120
description string, max 600
custom_message string, max 300 Extra note shown to the buyer
reference string, max 80 Internal reference, carried on the payment
cta_label enum pay_now, pay_amount, buy_now, continue_to_payment, complete_purchase, book_now, donate_now, reserve_now
branding_mode company_name or logo
show_product_image boolean Default true
success_url https URL Redirect after payment

Customer details

Name, email, phone and billing address are always collected. They are the signals fraud screening needs, so a link created over the API can never be weaker than one created in the dashboard. Sending collect_phone: false or collect_address: false returns 400.

Field Type Notes
collect_shipping_address boolean Ask for a separate delivery address
collect_note boolean Free-text field for the buyer
note_label string, max 60 Label for that field

Limits & state

Field Type Notes
max_uses integer or null 1–1,000,000 or null for unlimited. Never accepted below the number of completed purchases.
expires_at ISO-8601 or unix seconds, or null Must be in the future
active boolean Pause/resume. Archived links must be unarchived first.

The payment_link object

{
  "id": "7c2a…",
  "object": "payment_link",
  "livemode": true,
  "code": "sNdnaxXi9",
  "url": "https://payments.southbill.com/i/sNdnaxXi9",
  "active": true,
  "archived": false,
  "pricing_mode": "product",
  "merchant_price_id": "9f0c…",
  "product_name": "Summer workshop ticket",
  "amount": 12000,
  "currency": "eur",
  "allow_custom_amount": false,
  "quantity_mode": "customer",
  "fixed_quantity": 1,
  "max_quantity": 4,
  "cta_label": "book_now",
  "collect_phone": true,
  "collect_address": true,
  "max_uses": 50,
  "used_count": 3,
  "remaining_uses": 47,
  "expires_at": "2026-12-31T23:59:59Z",
  "view_count": 214,
  "stats": { "paid_count": 3, "open_count": 1, "volume": { "eur": 36000 } },
  "created": "2026-09-01T10:00:00Z"
}

Purchases

curl "https://api.southbill.com/v1/payment_links/7c2a…/payments?status=completed" \
  -H "Authorization: Bearer sk_live_…"

Each entry is a payment_link_payment with status, amount, currency, quantity, customer_name, customer_email, payment_intent, created and completed_at.

Slots are reserved when a buyer starts checkout and released automatically when they abandon it, so the last seat is never sold twice. Retrieving a link reconciles those reservations first, so counters are always the truth.

Sandbox

Use an sk_test_… key: links, prices, buyers and the payment page all run in the sandbox against test products and test cards, and come back with livemode: false. Test and live links are strictly separated — a test key never sees live links and vice versa. Test links are removed after 30 days or on a sandbox reset, and they do not appear in the dashboard (the dashboard shows live links only).

SDKs

const link = await southbill.paymentLinks.create({ pricing_mode: "custom", amount_cents: 2500, currency: "eur" });
await southbill.paymentLinks.list({ active: true });
await southbill.paymentLinks.payments(link.id);
await southbill.paymentLinks.archive(link.id);
link = southbill.payment_links.create(pricing_mode="custom", amount_cents=2500, currency="eur")
southbill.payment_links.payments(link["id"])
southbill.payment_links.archive(link["id"])

Apps use the same object under /v1/app/payment_links with scopes payments:read / payments:create; the owner always comes from the installation token.