API
Payment links API
Create, list and retrieve hosted payment links, and find the payment references made through them.
On this page
A payment link is a CentraPoint-hosted page (https://app.centrapoint.co.za/pay/…) where a payer enters their details, chooses a payment method from your enabled gateways and pays. Create links from your system, send the url to the payer, and use the list/get endpoints to find the payments made through each link.
| Endpoint | Purpose |
|---|---|
POST /api/v1/payment-links | Create a link |
GET /api/v1/payment-links | List links, optionally by externalReference, with their payments |
GET /api/v1/payment-links/{id} | Get one link with its payments |
Create a payment link#
/api/v1/payment-linksRequires an API key on a plan that includes the REST API and payment links, and an account that is not read-only. Returns 201 Created with the payment link object (without payments, since a new link has none).
Request body#
JSON object. Unknown fields are ignored.
| Field | Type | Description |
|---|---|---|
titlerequired | string | Shown to the payer. 1–100 characters after trimming whitespace. |
amountrequired | number | Amount in major units (rand, not cents), greater than 0 and at most 10 000 000. Rounded to 2 decimal places. Must be a JSON number. |
currencyoptional | string | ISO 4217 code, e.g. ZAR, KES, NGN. Upper-cased by the server. Default ZAR. Must be accepted by one of your enabled checkout gateways. See Currency and amount. |
externalReferenceoptional | string | Your own ID for this link, e.g. an order number. 1–100 characters after trimming. See External reference. |
descriptionoptional | string | Up to 255 characters, shown under the title. |
returnUrloptional | string | https URL to send the payer back to afterwards. Its origin must be allow-listed. See Return URL. |
metadataoptional | object | Your key/value data, echoed on transactions and webhooks. See Metadata. |
recurringoptional | object | { frequency, amount? } to start a card subscription. See Recurring links. |
customerIdoptional | string | Attach an existing customer by ID (from the Customers API). Takes precedence over customerEmail. |
customerEmailoptional | string (email) | Attach the link to a customer. See Customers. |
customerFirstNameoptional | string | Up to 100 characters. Only used when a new customer is created. |
customerLastNameoptional | string | Up to 100 characters. Only used when a new customer is created. |
productIdoptional | string | ID of a product in your organisation. See Products and invoices. |
invoiceIdoptional | string | ID of an invoice in your organisation. |
singleUseoptional | boolean | Default true. A single-use link is deactivated after its first successful payment. Set false for a reusable link (e.g. a donation or a fixed-price product page). |
expiresAtoptional | string (date-time) | ISO 8601 timestamp with seconds and either Z or a UTC offset, e.g. 2026-10-31T23:59:59+02:00 or 2026-10-31T21:59:59Z. Must be in the future and no more than 1 year ahead. After this moment the link cannot be paid. Omit for no expiry. |
External reference#
Set externalReference to the ID your system already uses for the thing being paid (order, booking, account statement). CentraPoint stores it on the link, returns it on the link and on every transaction made through it, and lets you look the link up by it:
GET /api/v1/payment-links?externalReference=ORDER-1001This is the recommended way to discover the payment references for your order. External references are not required to be unique; if you create several links with the same value, the list returns all of them, newest first.
Return URL#
By default the payer ends on CentraPoint's payment result page. Pass returnUrl to send them back to your site once the payment has a final outcome:
- An administrator allow-lists your origin (e.g.
https://shop.example.co.za) under Settings → API keys → Allowed return URLs. Only https origins are accepted. - Create the link with
returnUrlon that origin (up to 1000 characters; any#fragmentis dropped). Otherwise the request fails with400, e.g. returnUrl origin … is not allowed. - After the payment, the result page redirects the payer to your URL with
referenceandstatus(complete,failedorcancelled) added as query parameters, e.g.https://shop.example.co.za/checkout/done?reference=CP-20260925-9F3A1C7B&status=complete. From the cancel page, the payer gets a "Return to …" button withstatus=cancelled.
Metadata#
metadata stores your own key/value data on the link (up to 50 string values). It is returned on the link, on every transaction made through it, on payment.* webhooks and on subscriptions started by it. See the metadata rules.
Recurring links#
Add "recurring": { "frequency": "monthly" } to start a gateway-managed card subscription when the link is paid. frequency is monthly, quarterly, biannually or annually; optional amount is what each later collection charges (default: the link amount).
- Requires a plan with recurring billing and an enabled gateway that supports it: PayFast or Netcash Pay Now.
- Only recurring-capable gateways are offered to the payer on the link.
- The resulting subscription can be read and cancelled with the Subscriptions API. See the Recurring billing guide.
Bank transfer (EFT) at checkout#
If you have a Manual EFT gateway, once-off links in a currency it supports also offer Bank transfer (EFT). The payer gets an EFT order and its payer page for uploading proof; when your staff approve it, the link is paid and payment.complete carries the link's externalReference and metadata. Recurring links don't offer it. A once-off link can be created even if Manual EFT is your only gateway. See EFT at hosted checkout.
Customers#
Pass customerId to attach a customer you created with the Customers API. Otherwise, when you pass customerEmail, CentraPoint looks for a customer in your organisation with that email address (case-insensitive). If one exists, the link is attached to it and customerFirstName/customerLastName are ignored. Otherwise a new customer is created, with the first name defaulting to the part of the email before @. Creating a customer counts towards your plan's customer limit and can return 403 plan_limit.
When a link has a customer, the payment page pre-fills the payer's details. Without one, the payer enters their details and CentraPoint matches or creates the customer at checkout.
Currency and amount#
- The currency must be a valid ISO 4217 code and accepted by at least one of your enabled checkout gateways (or, for once-off links, by your Manual EFT gateway). Otherwise the request fails with
400 invalid_request, e.g. None of your enabled payment gateways accept USD. Enable a gateway that supports USD first. Check each gateway's currencies in the Gateways reference. - The payer only sees payment methods whose gateway supports the link's currency.
- Gateways can impose their own rules at checkout: for example M-Pesa only accepts whole Kenyan shilling amounts, and PayFast only accepts ZAR.
- The amount is fixed; the payer cannot change it.
Products and invoices#
productIdlinks the payment to a product. If the product is a recurring product, checkout starts a gateway-managed subscription, which needs a gateway with recurring support (PayFast) and a plan that includes recurring billing.invoiceIdapplies successful payments to that invoice; the invoice becomespaidonce fully paid. Links for paid or cancelled invoices cannot be paid.- IDs must belong to your organisation, otherwise the request fails with
400 invalid_requestand Product not found / Invoice not found. Product and invoice IDs are available in the dashboard; v1 has no endpoints to list them.
Code samples#
curl -X POST "https://app.centrapoint.co.za/api/v1/payment-links" \
-H "Authorization: Bearer $CENTRAPOINT_API_KEY" \
-H "Idempotency-Key: 3f6c2a4e-8d1b-4f7a-9c2e-5b1d0a7e6f31" \
-H "Content-Type: application/json" \
-d '{
"title": "Website hosting - October",
"description": "Monthly hosting for example.co.za",
"amount": 499,
"currency": "ZAR",
"externalReference": "ORDER-1001",
"customerEmail": "[email protected]",
"customerFirstName": "Thandi",
"customerLastName": "Nkosi",
"returnUrl": "https://shop.example.co.za/checkout/done",
"metadata": {
"cartId": "c_8841"
},
"singleUse": true,
"expiresAt": "2026-10-31T23:59:59+02:00"
}'{
"id": "cmg1k2x3y0001abcd1234efgh",
"url": "https://app.centrapoint.co.za/pay/Xb7kP2qR9sLm4TvWz8Yc",
"title": "Website hosting - October",
"description": "Monthly hosting for example.co.za",
"amount": 499,
"currency": "ZAR",
"externalReference": "ORDER-1001",
"returnUrl": "https://shop.example.co.za/checkout/done",
"metadata": {
"cartId": "c_8841"
},
"recurring": null,
"singleUse": true,
"active": true,
"expiresAt": "2026-10-31T21:59:59.000Z",
"paidCount": 0,
"createdAt": "2026-09-25T08:10:02.114Z"
}Send an Idempotency-Key header so a retried request returns the same link instead of creating another.
The payment link object#
| Field | Type | Description |
|---|---|---|
idrequired | string | Payment link ID. |
urlrequired | string | Hosted payment page. Send this to the payer. |
titlerequired | string | Title shown to the payer. |
descriptionrequired | string | null | Description shown to the payer. |
amountrequired | number | Amount after rounding. |
currencyrequired | string | Upper-case currency code. |
externalReferencerequired | string | null | Your ID, as supplied on creation. |
returnUrlrequired | string | null | Where the payer is sent afterwards. |
metadatarequired | object | Your key/value data; {} when empty. |
recurringrequired | object | null | { frequency, amount } for recurring links. |
subscriptionoptional | object | null | List and get responses: { id, status } of the latest subscription started by the link, or null. |
singleUserequired | boolean | Whether the link deactivates after the first successful payment. |
activerequired | boolean | False once deactivated in the dashboard or after a single-use link is paid. |
expiresAtrequired | string | null | Expiry as an ISO 8601 UTC timestamp, or null. |
paidCountrequired | integer | Number of successful payments through the link. |
createdAtrequired | string | ISO 8601 UTC creation time. |
paymentsoptional | object[] | List and get responses only. Checkout attempts through the link, newest first, each with reference, status, amount, currency, paidAt and createdAt. Every attempt (including cancelled or failed ones) has its own reference. |
List payment links#
/api/v1/payment-linksReturns your organisation's links, newest first, as { "data": [ … ] }. Each link includes up to 50 of its most recent payments.
| Field | Type | Description |
|---|---|---|
externalReferenceoptional | string | Only links with exactly this external reference (surrounding whitespace ignored). |
limitoptional | integer | 1–100, default 20. Out-of-range values are clamped; invalid values fall back to 20. |
curl -X GET "https://app.centrapoint.co.za/api/v1/payment-links?externalReference=ORDER-1001" \
-H "Authorization: Bearer $CENTRAPOINT_API_KEY"{
"data": [
{
"id": "cmg1k2x3y0001abcd1234efgh",
"url": "https://app.centrapoint.co.za/pay/Xb7kP2qR9sLm4TvWz8Yc",
"title": "Website hosting - October",
"description": "Monthly hosting for example.co.za",
"amount": 499,
"currency": "ZAR",
"externalReference": "ORDER-1001",
"returnUrl": "https://shop.example.co.za/checkout/done",
"metadata": {
"cartId": "c_8841"
},
"recurring": null,
"singleUse": true,
"active": false,
"expiresAt": "2026-10-31T21:59:59.000Z",
"paidCount": 1,
"createdAt": "2026-09-25T08:10:02.114Z",
"subscription": null,
"payments": [
{
"reference": "CP-20260925-9F3A1C7B",
"status": "complete",
"amount": 499,
"currency": "ZAR",
"paidAt": "2026-09-25T08:14:03.512Z",
"createdAt": "2026-09-25T08:12:47.020Z"
},
{
"reference": "CP-20260925-41D07E2A",
"status": "cancelled",
"amount": 499,
"currency": "ZAR",
"paidAt": null,
"createdAt": "2026-09-25T08:11:15.873Z"
}
]
}
]
}There is no pagination cursor in v1; narrow results with externalReference.
Get a payment link#
/api/v1/payment-links/{id}Returns one link with up to 100 of its most recent payments. Links in other organisations return 404 not_found (Payment link not found). Like the list, it includes subscription.
curl -X GET "https://app.centrapoint.co.za/api/v1/payment-links/cmg1k2x3y0001abcd1234efgh" \
-H "Authorization: Bearer $CENTRAPOINT_API_KEY"{
"id": "cmg1k2x3y0001abcd1234efgh",
"url": "https://app.centrapoint.co.za/pay/Xb7kP2qR9sLm4TvWz8Yc",
"title": "Website hosting - October",
"description": "Monthly hosting for example.co.za",
"amount": 499,
"currency": "ZAR",
"externalReference": "ORDER-1001",
"returnUrl": "https://shop.example.co.za/checkout/done",
"metadata": {
"cartId": "c_8841"
},
"recurring": null,
"singleUse": true,
"active": false,
"expiresAt": "2026-10-31T21:59:59.000Z",
"paidCount": 1,
"createdAt": "2026-09-25T08:10:02.114Z",
"subscription": null,
"payments": [
{
"reference": "CP-20260925-9F3A1C7B",
"status": "complete",
"amount": 499,
"currency": "ZAR",
"paidAt": "2026-09-25T08:14:03.512Z",
"createdAt": "2026-09-25T08:12:47.020Z"
},
{
"reference": "CP-20260925-41D07E2A",
"status": "cancelled",
"amount": 499,
"currency": "ZAR",
"paidAt": null,
"createdAt": "2026-09-25T08:11:15.873Z"
}
]
}Errors#
| Status | error | When |
|---|---|---|
| 400 | invalid_request | Create only: body is not valid JSON or fails validation (issues included); unknown customerId/productId/invoiceId; returnUrl not https or origin not allowed; invalid metadata; recurring not available; invalid currency or one none of your gateways accept; expiresAt not in the future or more than 1 year ahead; amount rounds to 0. |
| 401 | unauthorized | Missing, malformed, unknown or revoked API key. |
| 403 | plan_restricted | Plan does not include the REST API, or (create) payment links. |
| 403 | plan_limit | Create only: creating the customer would exceed your plan's customer limit. |
| 403 | account_restricted | Create only: account is suspended, cancelled or its trial has ended. |
| 409 | idempotency_conflict | Create only: Idempotency-Key reused with a different body. |
| 413 | invalid_request | Create only: request body larger than 100 000 characters. |
| 404 | not_found | Get only: no link with this ID in your organisation. |
| 429 | rate_limited | More than 120 requests per minute with this API key. Honour Retry-After. |
| 500 | internal_error | Unexpected server error. Safe to retry reads; see Errors before retrying a create. |
See Errors & rate limits for the error body format.
Link lifecycle#
A link can be paid while all of these are true:
- It is active (not deactivated in the dashboard, and not a single-use link that has been paid).
expiresAthas not passed.- Its invoice, if any, is not paid or cancelled.
- Your account is not read-only, and you have not reached your plan's monthly transaction limit.
Otherwise the payer sees a "Payment unavailable" message. v1 has no endpoints to update or deactivate links; do that in the dashboard under Payment links.