Get started
Quickstart
Get a sandbox API key
Create an account in the BaxPay Business dashboard and, on the
Developers page, create a bx_test_ key. Copy it now — it is shown once. The key is the
mode: everything it touches is sandbox data, and no real money can move.
Create a payment link
One call: an amount and a description. You get back a hosted page URL.
Pay it with a test card
Open the URL and pay with 4242 4242 4242 4242, or watch
4000 0000 0000 0002 get declined.
See the money land
Retrieve the link (now PAID) — or better, receive the
payment_link.paid webhook.
1 · Create a payment link
Charge €47.30 for a haircut. Three things to notice:
Idempotency-Keyis required on every call that creates or moves money. Retrying with the same key can never create a second link.businessWide: truetakes the payment for the whole business. To book it to one location instead, send that location'sstoreId. One of the two is required.amountis in euros (47.30), not cents.
curl -X POST https://sandbox-api.baxpay.com/v1/payment-links \
-H "Authorization: Bearer $BAXPAY_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{ "description": "Haircut — 16 July", "amount": 47.30, "businessWide": true }'const res = await fetch('https://sandbox-api.baxpay.com/v1/payment-links', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.BAXPAY_API_KEY}`,
'Idempotency-Key': crypto.randomUUID(),
'Content-Type': 'application/json',
},
body: JSON.stringify({ description: 'Haircut — 16 July', amount: 47.3, businessWide: true }),
});
const body = await res.json();
if (!res.ok) throw new Error(JSON.stringify(body));
console.log(JSON.stringify(body, null, 2)); // the hosted page is body.data.urlimport json, os, uuid, requests
res = requests.post(
"https://sandbox-api.baxpay.com/v1/payment-links",
headers={
"Authorization": f"Bearer {os.environ['BAXPAY_API_KEY']}",
"Idempotency-Key": str(uuid.uuid4()),
},
json={"description": "Haircut — 16 July", "amount": 47.30, "businessWide": True},
)
res.raise_for_status()
print(json.dumps(res.json(), indent=2)) # the hosted page is ["data"]["url"]Every response is wrapped: the link itself is in data.
{
"success": true,
"data": {
"id": "1c9f4a2e-…",
"token": "plk_4f1f0c4c4e1a…",
"url": "https://pay.baxpay.com/plk_4f1f0c4c4e1a…",
"description": "Haircut — 16 July",
"amount": 47.3,
"feeAmount": 1.47,
"chargeAmount": 47.3,
"netAmount": 45.83,
"currency": "EUR",
"feePayer": "RECEIVER",
"status": "OPEN",
"livemode": false,
"paidAt": null
},
"timestamp": "2026-07-16T14:02:10.000Z",
"path": "/v1/payment-links"
}chargeAmount is what the customer pays and netAmount is what you receive after BaxPay's fee
(feeAmount). With the default feePayer: "RECEIVER", you absorb the fee.
2 · Pay it
Open data.url in a browser. You'll see the hosted page your customers see: your business name,
the description, the amount and a test mode banner. Pay by card with 4242 4242 4242 4242
(any future expiry, any CVC); 4000 0000 0000 0002 is always declined. The page flips to a
receipt.
In sandbox you can also pay from a terminal. This is the call the hosted page makes:
curl -X POST https://sandbox-api.baxpay.com/v1/pay/link/plk_4f1f0c4c4e1a…/card \
-H "Content-Type: application/json" \
-d '{ "number": "4242 4242 4242 4242", "expMonth": 12, "expYear": 2030, "cvc": "123", "holder": "MARIE DUPONT" }'{ "success": true, "data": { "status": "PAID", "maskedInstrument": "•••• 4242" } }3 · Confirm the money landed
If you registered a webhook for payment_link.paid, BaxPay has already pushed this to
your endpoint, signed:
{
"id": "f017738f-…",
"type": "payment_link.paid",
"livemode": false,
"data": {
"paymentLinkId": "1c9f4a2e-…",
"description": "Haircut — 16 July",
"amount": 47.3,
"currency": "EUR",
"method": "CARD_NOT_PRESENT",
"transactionRef": "be294a82-…"
},
"createdAt": "2026-07-16T14:03:21.530Z"
}Or retrieve the link:
curl https://sandbox-api.baxpay.com/v1/payment-links/1c9f4a2e-… \
-H "Authorization: Bearer $BAXPAY_API_KEY"const res = await fetch('https://sandbox-api.baxpay.com/v1/payment-links/1c9f4a2e-…', {
headers: { Authorization: `Bearer ${process.env.BAXPAY_API_KEY}` },
});
console.log(JSON.stringify(await res.json(), null, 2));import json, os, requests
res = requests.get(
"https://sandbox-api.baxpay.com/v1/payment-links/1c9f4a2e-…",
headers={"Authorization": f"Bearer {os.environ['BAXPAY_API_KEY']}"},
)
res.raise_for_status()
print(json.dumps(res.json(), indent=2)){
"success": true,
"data": {
"id": "1c9f4a2e-…",
"status": "PAID",
"paidAt": "2026-07-16T14:03:21.512Z",
"paidMethod": "CARD_NOT_PRESENT",
"paidTransactionId": "be294a82-…"
}
}Polling is fine for a quickstart. In production, subscribe to webhooks instead.
Where to go next
Payment links
Expiry, references, clients, cancellation — the full resource.
E-invoicing (France)
Regulated French e-invoices with the same one-call collection.
In-store
Sessions for tills, pumps and kiosks — BaxPay terminals handle the payment itself.
Errors
Status codes, the error shape, and how to retry safely.