Skip to main content

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

FieldRequiredDescription
cbUuidYesThe reservation
paymentProviderYesPayOnSite, None, Stripe, Swish or Klarna
{ "cbUuid": "4674c65b-a64f-43a5-94c7-af3b59053cc6", "paymentProvider": "PayOnSite" }

Response 200

{ "confirmationMethod": "Pin" }
ValueMeaning
PinA PIN SMS was sent to the customer's phone. Pass the code as pin in /booking/pay
NoPinNo verification. Call /booking/pay with pin set to null
PaymentThe 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

StatusWhen
400, code 3The 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

FieldRequiredDescription
cbUuidYesThe reservation
paymentProviderYesStripe, Swish, Klarna, PayOnSite or None
pinPayOnSite, NoneThe PIN from the SMS when payment-provider answered Pin, otherwise null. Ignored for the other providers
stripePaymentMethodIdStripePayment method id (pm_…) from Stripe.js, first call
stripePaymentIntentIdStripePayment intent id (pi_…) after 3D Secure, second call. Send one of the two Stripe fields
swishPayerAliasNoSwish only. The customer's Swish number in E.164 format without +, for example 46701234567. null lets the customer enter it in the Swish app
klarnaAuthTokenKlarnaThe authorization_token from the Klarna SDK
captchaResponseNoSee 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:

ProviderpaymentStatuspaymentProvider
Stripe, KlarnaPaidStripe, Klarna
NonePaidNone
PayOnSiteUnpaidNone (the chosen provider is not echoed back)
SwishNever 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

StatusWhen
400, code 0Captcha failed, or no customer details were added to the reservation
400, code 3The cbUuid has no pending reservation: already confirmed, released or expired
400, code 3000The 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 3002Swish rejected the payment request. message holds the Swish error codes, comma separated, for example ACMT03
400, voucher codeThe applied voucher is no longer valid, see the codes under Apply or remove a voucher
402Payment not completed, see above
403Wrong PIN for PayOnSite or None, or the cbUuid is unknown. Empty body
418The request was blocked by the abuse checks and the reservation was deleted. Treat like 403
429Too 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.

FieldRequiredDescription
paymentProviderNoSwish 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
instructionUuidWith SwishThe 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"
}
}
statusbodyMeaning
PendingConfirmnull, or Swish status detailsThe reservation is still pending. Keep polling, or call /booking/pay (again)
ConfirmedThe confirm responseThe 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

StatusWhen
410The cbUuid has no reservation and no booking: it expired, was released, or never existed
400, code 3002Swish 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" }
]
}
FieldDescription
clientTokenPass to Klarna.Payments.init
sessionIdThe Klarna session id, for your logs
paymentMethodCategoriesThe Klarna payment method categories available. Offer Klarna only when this is not empty
saleItemsThe line items, including an applied voucher, same shape as in confirmation-options

Errors

StatusWhen
400, code 3The cbUuid has no pending reservation
400, code 3000Klarna 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" } }
]
}
errorCodeMeaning
CodeNotFoundNo voucher with that code
MinSpendNotReachedThe booking is below the voucher's minimum amount, data.minSpend
NotValidBeforeThe voucher is not valid yet, data.notValidBefore
NotValidAfterThe voucher has expired, data.notValidAfter
MaxRedeemsReachedThe voucher has been used up
ServiceIdsNotAllowedThe voucher does not apply to the booked services
ResourceIdsNotAllowedThe voucher does not apply to the booked resource
NotValidForPaymentThe voucher cannot be used for online payment

Errors

StatusWhen
400, code 3The 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" }
}
FieldDescription
amountThe new amount to pay including VAT. Replaces paymentAmountIncVat. 0 means the booking is completed with paymentProvider None
saleItemsThe 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
discountVoucherThe 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:

StatusWhen
400, code 3The cbUuid has no pending reservation
400, code 1117CodeNotFound
400, code 1110NotValidForPayment
400, code 1118MinSpendNotReached
400, code 1119NotValidBefore, details holds notValidBefore
400, code 1120NotValidAfter, details holds notValidAfter
400, code 1121MaxRedeemsReached
400, code 1126ServiceIdsNotAllowed
400, code 1127ResourceIdsNotAllowed