Payment endpoints
Reference for the endpoints used when a booking requires online payment. How
they fit together, per provider, is described in Payments.
All of them take the cbUuid of a pending reservation, see
Creating a booking.
Choose payment provider
POST /booking/payment-provider
Tells you how a booking is confirmed for a given provider. Needed before
/booking/pay with PayOnSite or None, since those providers go through the
account's SMS verification. For the other providers it just answers Payment.
Request
| Field | Required | Description |
|---|---|---|
cbUuid | Yes | The reservation |
paymentProvider | Yes | PayOnSite, None, Stripe, Swish or Klarna |
{ "cbUuid": "4674c65b-a64f-43a5-94c7-af3b59053cc6", "paymentProvider": "PayOnSite" }
Response 200
{ "confirmationMethod": "Pin" }
| Value | Meaning |
|---|---|
Pin | A PIN SMS was sent to the customer's phone. Pass the code as pin in /booking/pay |
NoPin | No verification. Call /booking/pay with pin set to null |
Payment | The provider takes online payment, no PIN is involved |
Every call with PayOnSite or None on an account with SMS verification
sends a new PIN and invalidates the previous one, so call it once per choice.
The call does not create anything on the payment provider side and does not
extend the reservation.
Errors
| Status | When |
|---|---|
400, code 3 | The cbUuid has no pending reservation |
Pay and confirm
POST /booking/pay
Completes a booking whose confirmationMethod is Payment. Replaces
/booking/confirm for these bookings.
Extends the reservation by five minutes.
Request
| Field | Required | Description |
|---|---|---|
cbUuid | Yes | The reservation |
paymentProvider | Yes | Stripe, Swish, Klarna, PayOnSite or None |
pin | PayOnSite, None | The PIN from the SMS when payment-provider answered Pin, otherwise null. Ignored for the other providers |
stripePaymentMethodId | Stripe | Payment method id (pm_…) from Stripe.js, first call |
stripePaymentIntentId | Stripe | Payment intent id (pi_…) after 3D Secure, second call. Send one of the two Stripe fields |
swishPayerAlias | No | Swish only. The customer's Swish number in E.164 format without +, for example 46701234567. null lets the customer enter it in the Swish app |
klarnaAuthToken | Klarna | The authorization_token from the Klarna SDK |
captchaResponse | No | See Captcha |
{
"cbUuid": "4674c65b-a64f-43a5-94c7-af3b59053cc6",
"paymentProvider": "Stripe",
"stripePaymentMethodId": "pm_1ABCdefGHIjklMNOpqrsTUVW",
"captchaResponse": null
}
Response 201, the booking is confirmed:
{
"cbUuid": "4674c65b-a64f-43a5-94c7-af3b59053cc6",
"cbRef": "mK0k22O3",
"smsReminderEnabled": true,
"emailConfirmSent": true,
"smsConfirmSent": true,
"paymentStatus": "Paid",
"paymentProvider": "Stripe"
}
Same body as confirm. paymentStatus
and paymentProvider depend on the provider:
| Provider | paymentStatus | paymentProvider |
|---|---|---|
Stripe, Klarna | Paid | Stripe, Klarna |
None | Paid | None |
PayOnSite | Unpaid | None (the chosen provider is not echoed back) |
Swish | Never 201 from this call. The confirmed booking comes from the status endpoint with Paid and Swish |
Response 402, payment not completed. The body has one of three shapes:
Stripe, 3D Secure needed. Run stripe.handleCardAction(clientSecret) and call
again with stripePaymentIntentId:
{
"providerStatus": "requires_action",
"clientSecret": "pi_3ABCdefGHIjklMNO_secret_pqrsTUVWxyz",
"requiresAction": true,
"paymentSucceeded": false,
"chargeId": null,
"source": "Visa **** 4242"
}
Payment failed, for example a declined card. The reservation is still held:
{
"paymentStatus": "Unpaid",
"paymentProvider": "Stripe",
"failureCode": "card_declined",
"failureMessage": "Kortet nekades"
}
Swish, payment request created. Poll the status endpoint
with instructionUuid:
{
"paymentRequestToken": "c28a4061470440d8a27a5c9a7b1f4c7e",
"bookingRef": "mK0k22O3",
"instructionUuid": "B9A4C7D5E2F44A1B8C3D6E7F8A9B0C1D"
}
paymentRequestToken is used for the swish://paymentrequest?token=… link
and is an empty string when Swish did not return one. bookingRef is the
cbRef.
Errors
| Status | When |
|---|---|
400, code 0 | Captcha failed, or no customer details were added to the reservation |
400, code 3 | The cbUuid has no pending reservation: already confirmed, released or expired |
400, code 3000 | The provider is not configured for the account, PayOnSite is not allowed for this booking, or None was used with an amount above zero |
400, code 3002 | Swish rejected the payment request. message holds the Swish error codes, comma separated, for example ACMT03 |
| 400, voucher code | The applied voucher is no longer valid, see the codes under Apply or remove a voucher |
| 402 | Payment not completed, see above |
| 403 | Wrong PIN for PayOnSite or None, or the cbUuid is unknown. Empty body |
| 418 | The request was blocked by the abuse checks and the reservation was deleted. Treat like 403 |
| 429 | Too many calls for this reservation or from this IP, see Rate limits |
The call is not idempotent, see Errors, retries and recovery.
Booking status
POST /booking/{cbUuid}/status
Returns whether a reservation has been confirmed. Used to wait for a Swish
payment, and to recover after a lost /booking/pay response. The cbUuid
goes in the path as is (not Base64 encoded like the
manage booking ref).
Request
A JSON body is required. Both fields may be null.
| Field | Required | Description |
|---|---|---|
paymentProvider | No | Swish makes the server check the Swish payment request and complete the booking if it is paid. Any other value or null only reports the booking state |
instructionUuid | With Swish | The instructionUuid from the 402 response of /booking/pay |
{ "paymentProvider": "Swish", "instructionUuid": "B9A4C7D5E2F44A1B8C3D6E7F8A9B0C1D" }
Response 200
{
"status": "Confirmed",
"body": {
"cbUuid": "4674c65b-a64f-43a5-94c7-af3b59053cc6",
"cbRef": "mK0k22O3",
"smsReminderEnabled": true,
"emailConfirmSent": true,
"smsConfirmSent": true,
"paymentStatus": "Paid",
"paymentProvider": "Swish"
}
}
status | body | Meaning |
|---|---|---|
PendingConfirm | null, or Swish status details | The reservation is still pending. Keep polling, or call /booking/pay (again) |
Confirmed | The confirm response | The booking is confirmed. paymentStatus and paymentProvider reflect the payment that was made |
PaymentError | {"status", "errorCode", "message", "additionalInformation"} | The Swish payment was DECLINED, CANCELLED or ended in ERROR. The reservation is still pending |
Errors
| Status | When |
|---|---|
| 410 | The cbUuid has no reservation and no booking: it expired, was released, or never existed |
400, code 3002 | Swish did not return a payment request for the instructionUuid |
Create a Klarna session
POST /klarna/session
Creates a Klarna Payments session for the reservation. The clientToken
initializes the Klarna SDK in the browser. Extends the reservation by five
minutes. Create one session per reservation and reuse it.
Request
{ "cbUuid": "4674c65b-a64f-43a5-94c7-af3b59053cc6" }
Response 200
{
"clientToken": "eyJhbGciOiJSUzI1NiIs...",
"sessionId": "0b1d9815-165e-42e2-8867-35bc03789e00",
"paymentMethodCategories": ["pay_later", "pay_over_time"],
"saleItems": [
{ "itemType": "Service", "quantity": 1, "amount": 500.0, "vatPct": 25.0, "vatAmount": 100.0, "description": "Dry Haircut" }
]
}
| Field | Description |
|---|---|
clientToken | Pass to Klarna.Payments.init |
sessionId | The Klarna session id, for your logs |
paymentMethodCategories | The Klarna payment method categories available. Offer Klarna only when this is not empty |
saleItems | The line items, including an applied voucher, same shape as in confirmation-options |
Errors
| Status | When |
|---|---|
400, code 3 | The cbUuid has no pending reservation |
400, code 3000 | Klarna is not configured for the account |
Validate a voucher
POST /booking/validate-voucher
Checks a discount or gift card code against the reservation without applying
it. Only available when confirmation-options returned allowDiscountVoucher
true.
Request
{ "cbUuid": "4674c65b-a64f-43a5-94c7-af3b59053cc6", "voucherCode": "GIFT123" }
Response 200
{ "valid": true, "errors": [] }
An invalid code is also 200:
{
"valid": false,
"errors": [
{ "errorCode": "NotValidAfter", "message": "Voucher expired", "data": { "notValidAfter": "2026-06-30T23:59:59" } }
]
}
errorCode | Meaning |
|---|---|
CodeNotFound | No voucher with that code |
MinSpendNotReached | The booking is below the voucher's minimum amount, data.minSpend |
NotValidBefore | The voucher is not valid yet, data.notValidBefore |
NotValidAfter | The voucher has expired, data.notValidAfter |
MaxRedeemsReached | The voucher has been used up |
ServiceIdsNotAllowed | The voucher does not apply to the booked services |
ResourceIdsNotAllowed | The voucher does not apply to the booked resource |
NotValidForPayment | The voucher cannot be used for online payment |
Errors
| Status | When |
|---|---|
400, code 3 | The cbUuid has no pending reservation |
Apply or remove a voucher
POST /booking/voucher
Applies a voucher code to the reservation and returns the new amount to pay.
Send voucherCode as null to remove an applied voucher.
Request
{ "cbUuid": "4674c65b-a64f-43a5-94c7-af3b59053cc6", "voucherCode": "GIFT123" }
Response 200
{
"amount": 300.0,
"saleItems": [
{ "itemType": "Service", "quantity": 1, "amount": 500.0, "vatPct": 25.0, "discountAmount": 0, "description": "Dry Haircut", "...": "..." },
{ "itemType": "Discount", "quantity": 1, "amount": 0, "vatPct": 0, "discountAmount": -200.0, "description": "GIFT123", "...": "..." }
],
"discountVoucher": { "code": "GIFT123" }
}
| Field | Description |
|---|---|
amount | The new amount to pay including VAT. Replaces paymentAmountIncVat. 0 means the booking is completed with paymentProvider None |
saleItems | The line items. A Discount item has amount 0 and the discount as a negative discountAmount. The items carry more internal fields than shown, rely only on itemType, quantity, amount, vatPct, discountAmount and description |
discountVoucher | The applied voucher, or null after removal |
Calling confirmation-options again does not reflect the applied voucher, keep
amount and saleItems from this response. The voucher is validated again
when the booking is paid.
Errors
The voucher rules from validate are enforced here as
400 errors with the same reasons:
| Status | When |
|---|---|
400, code 3 | The cbUuid has no pending reservation |
400, code 1117 | CodeNotFound |
400, code 1110 | NotValidForPayment |
400, code 1118 | MinSpendNotReached |
400, code 1119 | NotValidBefore, details holds notValidBefore |
400, code 1120 | NotValidAfter, details holds notValidAfter |
400, code 1121 | MaxRedeemsReached |
400, code 1126 | ServiceIdsNotAllowed |
400, code 1127 | ResourceIdsNotAllowed |