Collect booking and attendee details with the Booking API
- Availability
- Beta
- Last verified
- Last verified Sep 28, 2026
Venues can ask questions for each booking or each ticket, for example attendee names, a date of birth or a dietary requirement. Use this guide to show those questions in your checkout and save the answers before payment.
Required questions due before payment must be answered before you complete checkout, even when you use the hosted payment page. If your integration cannot collect them, use the venue’s hosted online shop for the booking instead.
Before you start
- A cart with at least one item and its cart token.
- The Booking API quickstart explains carts and tokens.
Product and voucher questions
Product and voucher cart lines use the same question collection flow as event bookings. A BOOKING question group is answered once per cart line, even when the quantity is greater than one. An ATTENDEE group applies to each attendance created for that line. Use booking questions for information shared by the whole purchase; use attendee questions for details tied to an individual ticket or voucher.
To assign an existing question group to a product or voucher, a staff integration can call the authenticated application GraphQL API. This configuration operation requires staff authorization for custom-field assignments; a cart token does not authorize it.
mutation AssignQuestions($input: CustomFieldsOfferableFieldSetCreateInput!) {
customFieldsOfferableFieldSetCreate(input: $input) {
customFieldsOfferableFieldSet {
id
position
}
errors {
key
messages
}
}
}{
"input": {
"offerableType": "VOUCHER_CONFIGURATION",
"offerableId": "<voucher ID>",
"fieldSetId": "<question group ID>"
}
}Use PRODUCT and the product ID for a product. Read customFieldsFieldSets on Product or VoucherConfiguration to obtain their question groups, and customFieldsOfferableFieldSets for assignment IDs and display positions. Duplicating either offer preserves the question-group assignments.
Use answers in templates
In templates with a cart-line, ticket or voucher context, {{request_item_custom_field_123}} inserts the booking answer for input ID 123. {{attendance_custom_field_456}} inserts the attendee answer for input ID 456 when an attendance is available. These numbers identify individual inputs, not question groups.
For an order email, put booking answers inside the cart-line loop:
{{#request_item_context}} {{#is_request_item_custom_field_123_present}}
<p>{{request_item_custom_field_123}}</p>
{{/is_request_item_custom_field_123_present}} {{/request_item_context}}The presence section hides its contents when that line has no answer. For attendee answers, use is_attendance_custom_field_456_present in the same way. Keep the double braces shown above so typed answers are HTML-escaped. To include a formatted summary of booking and attendee answers, use {{request_item_custom_fields_summary}} inside request_item_context.
Check whether the cart has questions
Read customFieldsCompletion on the cart. When allRequiredComplete is false, required questions are still open. Ask for customFieldsCollectionToken in the same query; you need it to read and answer the questions. Reading the field again returns the same token; KORONA Event renews it once less than 12 hours of its validity remain.
query CartQuestions($id: IdOrNumberOrToken!) {
request(id: $id) {
customFieldsCompletion {
hasCustomFields
allRequiredComplete
}
customFieldsCollectionToken(requiredByStage: BEFORE_PAYMENT)
}
}requiredByStage: BEFORE_PAYMENT includes every question that must be answered before payment. The token is valid for 24 hours; read a new one when it expires.
Read the open questions
query OpenQuestions($token: Token!) {
requestCustomFields(requestId: $token) {
openCustomFields {
customizableId
customizableType
offerableNameTranslated
pricingNameTranslated
customFieldSets {
id
legendTranslated
descTranslated
inputs {
id
labelTranslated
descTranslated
type
required
mapping
options {
value
labelTranslated
}
}
}
}
}
}Each entry in openCustomFields is one set of answers to collect:
customizableType | Asked for |
|---|---|
REQUEST_ITEM | One cart line, once per booking. |
ATTENDANCE | One ticket. Show pricingNameTranslated so the customer knows which ticket they fill in. |
CONTACT | The customer. These questions cannot be answered through the Booking API yet; see below. |
customFieldSets groups the questions under a heading from legendTranslated. Show the inputs in the order returned.
Build the form
Choose the form control from type and send the answer as text:
type | Answer |
|---|---|
TEXT, TEXTAREA | Free text. |
EMAIL | An email address. |
TEL | A phone number, validated for the venue's country. |
DATE | A date as YYYY-MM-DD. For mapping: DATE_OF_BIRTH, it must not be in the future. |
SELECT, RADIO | The value of the chosen option. Show labelTranslated. |
CHECKBOX | With options, the value of the chosen option; without options, a text value. |
FILE, WAIVER | Not supported through the Booking API yet; see below. |
mapping tells you when an answer fills a standard attendee detail such as FIRST_NAME or LAST_NAME; CUSTOM is a question the venue defined.
Save the answers
Send the answers for one entry of openCustomFields at a time, with one item per question group:
mutation SaveAnswers($input: RequestCustomFieldsCollectionSubmitInput!) {
requestCustomFieldsCollectionSubmit(input: $input) {
customFieldsResponses {
id
completionState
}
errors {
key
message
messageTranslated
}
}
}{
"input": {
"requestId": "<customFieldsCollectionToken>",
"customFieldsResponses": [
{
"customizableId": "<customizableId>",
"customizableType": "ATTENDANCE",
"fieldSetId": "<customFieldSets.id>",
"values": [{ "inputId": "<inputs.id>", "value": "Alex" }]
}
]
}
}When the answers are saved, completionState is REQUIREMENTS_MET. Read the open questions again and repeat until openCustomFields is empty, then complete checkout.
When a cart line has several tickets in the same price category, openCustomFields lists them as one entry. Each save fills the next ticket, so read the list again after every save until the entry disappears.
Questions the Booking API cannot answer yet
File uploads (FILE), waivers (WAIVER) and customer questions (CONTACT) cannot be submitted through the Booking API. If any of these are required before payment, this configuration does not support a complete headless checkout: checkout returns custom_fields_incomplete and does not return an invoice or paymentLink.
Use the venue’s hosted online shop for these bookings from the start, so the customer can provide the required details before checkout. Do not complete API checkout expecting a payment link to collect them.
Errors
| Key | Message | What to do |
|---|---|---|
customFieldsResponses.0.values.0.value | invalid | The answer does not match the question's type, for example an invalid email address or an unknown option. The indexes point to the question group and the answer. |
customFieldsResponses | invalid | A required answer is missing, or the answers belong to more than one cart line or ticket. Check the answers and send them again. |
customFieldsResponses | already_completed | These questions are already answered. Read the open questions again. |
Depending on the venue's settings, checkout fails with custom_fields_incomplete while required questions are open. Answer them and complete checkout again.