Create bookings collected by an external partner
- Availability
- Beta
- Last verified
- Last verified Nov 6, 2026
Use this server-to-server flow when an external booking system collects payment from the guest and settles with the venue later. KORONA Event creates the booking and a fiscal delivery note for the configured reseller. The venue continues its agreed settlement process in KORONA.studio.
Before you start
Obtain the API base URL, tenant domain, service-account API key, point-of-sale ID and existing order-channel ID from the venue's integration administrator. Use an existing test tenant where available. Keep API keys on your server and out of browser bundles, logs and examples.
The administrator must enable fiscal delivery notes and configure the order channel's integration row for your service account, including the reseller contact/customer and its KORONA.studio customer mapping. The guest contact supplied below is separate from that billing customer. The mapping is stored when the order is created; later configuration changes do not move existing orders to another reseller.
The current service-account role needs order create/read/update permissions, plus the read permissions for the offer, price, order-item and contact fields used in the recipe. Cancellation also uses order update permission. These are ordinary role permissions, not an integration-ownership scope. See Create a service account.
Send each operation as a separate HTTPS POST to /api/graphql/booking/v1:
Content-Type: application/json
Authorization: Bearer <service-account API key>
X-Tenant-Domain: <tenant domain>
Accept-Language: enUse a JSON body containing query and variables. Keep the API key on every call, including calls using the returned cart token. A cart token alone does not grant partner payment authority. Management API credentials are not accepted by this endpoint.
Use the same configured posId for discovery, prices and creation. An explicit POS takes precedence over automatic defaults. With automatic creation, the channel/user POS override precedes the user's default POS, then the channel default. The explicit POS in this recipe avoids a different context between reading prices and creating the order. The order channel and POS are configuration, not permission grants.
Discover offers and visit times
query PartnerOffers($posId: ID!, $after: String) {
offers(posId: $posId, offerableTypes: [EVENT_TEMPLATE], first: 50, after: $after) {
edges {
node {
... on EventTemplate {
id
nameTranslated
timeZone
}
}
}
pageInfo {
hasNextPage
endCursor
}
}
}For the selected event template, request a bounded date range and the intended quantity:
query PartnerSlots($id: ID!, $posId: ID!, $span: TsRange!, $quantity: Int!, $after: String) {
offer(type: EVENT_TEMPLATE, id: $id, posId: $posId) {
... on EventTemplate {
potentialEvents(posId: $posId, span: $span, spanOperator: CONTAINS_START, quantity: $quantity, first: 50, after: $after) {
edges {
node {
span
remainingQuota
priceRules(posId: $posId) {
id
minPax
maxPax
priceCategory {
id
nameTranslated
}
product {
currentPriceValue
}
}
}
}
pageInfo {
hasNextPage
endCursor
}
}
}
}
}Pass a half-open range, for example [2026-12-01T00:00:00+01:00,2026-12-02T00:00:00+01:00). Use each returned slot's span unchanged when creating its item. Display times in the offer's IANA timeZone; do not add a fixed timezone offset yourself. Continue pagination with endCursor while hasNextPage is true.
Read prices from the selected occurrence's price rules. Money is a JSON scalar such as {"amount": 2500, "currency": "EUR"}: the amount is in minor currency units (€25.00 here). Prices and capacity can change; discovery does not reserve places or fix the final price. Campaign prices or dedicated contingents must be configured and agreed separately, not inferred from this API flow. See Dates and times.
Create the order
mutation PartnerCreate($input: RequestCreateInput!) {
requestCreate(input: $input) {
request {
id
accessToken
number
externalRef
paymentMethod
state
totalGrossValue
requestItems {
id
offerableId
offerableType
offerableSpan
pricings {
id
quantity
priceValue
}
}
}
errors {
key
message
messageTranslated
}
}
}{
"input": {
"posId": "<configured POS ID>",
"requestChannel": { "id": "<configured order-channel ID>" },
"paymentMethod": "DELIVERY_NOTE",
"externalRef": "PARTNER-ORDER-123",
"contact": {
"firstName": "Alex",
"lastName": "Example",
"email": "[email protected]",
"customer": {}
},
"requestItems": [
{
"offerableType": "EVENT_TEMPLATE",
"offerableId": "<selected template ID>",
"offerableSpan": "<returned slot span>",
"pricings": [{ "priceOriginType": "PRICE_RULE", "priceOriginId": "<returned price-rule ID>", "quantity": 1 }]
}
]
}
}Set externalRef to the external system's order number. It is the reference staff use to match the two systems and is included as the fiscal delivery-note comment. It is separate from KORONA Event's number, externalId and an idempotency key. It is not unique and does not prevent duplicate creation.
Keep the returned id, number and accessToken securely. Inspect the returned prices and total before confirmation. Creating a timed template item can return an EVENT item with a new event ID; use the returned item identity for subsequent changes.
Supply required attendee/personalization data and complete any required forms before confirming. This minimal example assumes an offer without additional required questions. See Collect booking and attendee details for the supported collection flow.
Confirm without charging the guest again
mutation PartnerConfirm($input: RequestFinalizeInput!) {
requestFinalize(input: $input) {
request {
id
number
state
externalRef
paymentMethod
paymentState
shareablePublicTicketsUrl(format: PDF)
}
invoice {
number
}
errors {
key
message
messageTranslated
}
}
}{ "input": { "id": "<returned cart accessToken>", "paymentMethod": "DELIVERY_NOTE" } }A successful delivery-note confirmation returns state: BOOKED and no new invoice for guest payment. It creates the fiscal delivery note for the reseller mapping. Do not call paymentIntentCreate, open hosted payment or use the BOOKING_KIT payment provider to represent money your system collected.
paymentState can remain REQUIRES_PAYMENT: the guest's payment to the partner does not mean the venue has received the partner's settlement. Do not use that state alone to decide whether this delivery-note booking is confirmed or its tickets may be delivered.
Use the returned ticket URL unchanged when present; treat it as a credential. If it is absent, read the order again after ticket preparation. Do not create another order to obtain a ticket link. Ticket output depends on the venue's ticket configuration, and a link alone is not confirmation of the partner's payout.
Ticket activation and PDF preparation can finish asynchronously. If downloading returns HTTP 503 with Retry-After, wait for that interval and retry the same URL.
Read the result and recover an uncertain response
query PartnerStatus($id: IdOrNumberOrToken!) {
request(id: $id) {
id
number
state
externalRef
paymentMethod
paymentState
totalGrossValue
shareablePublicTicketsUrl(format: PDF)
}
}Use the stored cart token as id, with the API-key header. After a confirmation timeout, read this result before attempting further writes. If it is already BOOKED, use that booking. If creation times out before you receive an identifier, reconcile with the venue using your external order number before creating again. This contract does not promise automatic idempotent retries or lookup by externalRef.
Check HTTP failures, top-level GraphQL errors and mutation-payload errors. HTTP 200 alone is not success. On unavailable capacity or changed prices, refresh the selection and obtain the customer's agreement to any change. On an authorization error, check the configured access with the administrator; do not switch to anonymous access. If delivery notes are unavailable, resolve the venue configuration rather than substituting another payment method.
Cancel a booking
mutation PartnerCancel($input: RequestCancelInput!) {
requestCancel(input: $input) {
request {
id
state
externalRef
}
errors {
key
message
messageTranslated
}
}
}{ "input": { "id": "<returned cart accessToken>", "autoRefund": false } }For an order completed only through fiscal delivery notes, cancellation creates a correcting delivery note. Your system remains responsible for refunding the guest. Agree cancellation and amendment rules with the venue; do not implement rebooking by blindly creating a second order after a timeout.