Booking API quickstart
- Last verified
- Last verified Sep 23, 2026
This tutorial sells a voucher through the Booking API and hands payment to the shop's hosted payment page. It uses six requests: find the voucher, add it to a cart, add the customer, read the required legal documents, complete checkout and check the payment. The same steps sell tickets; Show dates and time slots with the Booking API shows how to add dated tickets to the cart instead.
Before you start
- A shop in KORONA Event that sells at least one published voucher.
- The shop's domain, for example
tickets.example.com, and your KORONA Event API host. - A tool that sends HTTP POST requests with a JSON body, such as
curlor your application's HTTP client.
Send every request as POST https://<api-host>/api/graphql/booking/v1 with these headers:
Content-Type: application/json
X-Tenant-Domain: tickets.example.com
Accept-Language: enThe body is a JSON object with query and variables. How the Booking API works explains the records and tokens used below.
1. Find the voucher
List the shop's vouchers. posId: "auto" selects the shop's own sales channel.
query DiscoverVouchers {
offers(posId: "auto", offerableTypes: [VOUCHER_CONFIGURATION], first: 20) {
edges {
node {
... on VoucherConfiguration {
id
nameTranslated
currentPriceValue
}
}
}
}
}{
"data": {
"offers": {
"edges": [
{
"node": {
"id": "2a1dc9d4-fb0b-4dd5-808f-858033098f31",
"nameTranslated": "Gift voucher",
"currentPriceValue": { "amount": 5000, "currency": "EUR" }
}
}
]
}
}
}Money values contain the amount in the currency's smallest unit, so 5000 is 50.00 EUR.
2. Add the voucher to a cart
The first cart mutation without a requestId creates the cart. Keep the returned accessToken: it is the cart token for every later request.
mutation AddToCart($input: RequestItemCreateInput!) {
requestItemCreate(input: $input) {
request {
accessToken
totalGrossValue
checkoutHold {
expiresAt
secondsRemaining
}
}
requestItem {
id
}
errors {
key
message
messageTranslated
}
}
}{
"input": {
"offerableType": "VOUCHER_CONFIGURATION",
"offerableId": "2a1dc9d4-fb0b-4dd5-808f-858033098f31",
"pricings": [
{
"quantity": 1,
"priceOriginType": "VOUCHER_CONFIGURATION",
"priceOriginId": "2a1dc9d4-fb0b-4dd5-808f-858033098f31"
}
]
}
}The response contains the cart token, the cart total and the checkout hold. To add another item to the same cart, send the cart token as requestId in the next requestItemCreate.
3. Add the customer
Send the customer's details with the cart token as id. Add a billing address; mark it with billing: true.
mutation AddCustomer($input: RequestUpdateInput!) {
requestUpdate(input: $input) {
request {
contact {
email
}
billingAddress {
city
}
checkoutPolicy {
compatible
targetState
}
requiresShipping
customFieldsCompletion {
allRequiredComplete
}
}
errors {
key
message
messageTranslated
}
}
}{
"input": {
"id": "<cart token>",
"contact": {
"firstName": "Alex",
"lastName": "Example",
"email": "alex@example.com",
"customer": {
"addresses": [
{
"billing": true,
"street": "Example Street 1",
"postalCode": "12345",
"city": "Berlin",
"country": "DE"
}
]
}
}
}
}Check the returned cart before checkout:
| Field | What to do |
|---|---|
checkoutPolicy.targetState is BOOKED | Continue with this tutorial. |
checkoutPolicy.compatible is false | The cart mixes offers that cannot be checked out together. Remove the lines listed in checkoutPolicy.conflicts. |
requiresShipping is true | Add an address with shipping: true in a country from shop.allowedShippingCountries. |
customFieldsCompletion.allRequiredComplete is false | Do not start checkout. The Booking API cannot collect these answers; send the customer to the hosted shop to complete this purchase. |
4. Read the required legal documents
Customers must accept the shop's required legal documents. Show each document's nameTranslated and checkboxLabelTranslated with a checkbox, and link to the document text from legalDocument(id:).
query RequiredDocuments {
legalDocuments(required: [true], first: 20) {
edges {
node {
id
nameTranslated
checkboxLabelTranslated
publishedAt
}
}
}
}Keep each document's id and publishedAt for the next step. publishedAt identifies the version the customer accepted.
5. Complete checkout
After the customer accepts the documents, complete checkout. The response contains the invoice and the hosted payment link.
mutation CompleteCheckout($input: RequestPaymentInitiateInput!) {
requestPaymentInitiate(input: $input) {
request {
state
}
invoice {
accessToken
paymentState
paymentLink
}
errors {
key
message
messageTranslated
}
}
}{
"input": {
"id": "<cart token>",
"legalDocumentAcceptances": [
{ "legalDocumentId": "1", "legalDocumentVersion": "2026-09-23T19:21:22+00:00" },
{ "legalDocumentId": "2", "legalDocumentVersion": "2026-09-23T19:21:22+00:00" }
]
}
}{
"data": {
"requestPaymentInitiate": {
"request": { "state": "BOOKED" },
"invoice": {
"accessToken": "<invoice token>",
"paymentState": "REQUIRES_PAYMENT",
"paymentLink": "https://tickets.example.com/en/payments/<token>"
},
"errors": null
}
}
}Redirect the customer's browser to paymentLink. The customer chooses a payment method and pays on the hosted payment page, which uses the language of your Accept-Language header.
6. Check the payment
Read the invoice with the invoice token to follow the payment. paymentState becomes PAID when the payment succeeds.
query PaymentStatus($invoiceToken: IdOrNumberOrToken!) {
invoice(id: $invoiceToken) {
paymentState
paymentIntentCreateError
payments {
state
providerInitializationFailed
}
request {
state
checkoutHold {
secondsRemaining
}
ticketsUrl: shareablePublicTicketsUrl(format: PDF)
}
}
}The tickets URL can be present while ticket activation is pending. It may open the hosted ticket page, which makes the tickets available when activation finishes. Do not use the URL's presence as a signal that a PDF is ready.
Check your work
- You stored the cart token from step 2 and used it in steps 3 and 5.
- The customer accepted every required legal document before step 5.
- The customer landed on the hosted payment page from
paymentLink.