Set up webhooks

Last verified
Last verified Oct 2, 2026

Use this to notify an external system when something happens in KORONA Event, for example when an order settles or a customer changes newsletter consent. Webhooks push signed JSON events to an HTTPS URL you control.

Integration settings and connection status in the back office
Use the integrations area to verify credentials, connection state, and external-system setup.

Before you start

Confirm:

  • you have an HTTPS endpoint that can receive POST requests and respond quickly
  • you have back-office permission to manage webhook endpoints under Admin > Webhooks
  • you know which event types the receiving system needs

Create a webhook endpoint

  1. In the back office, open Admin > Webhooks.
  2. On the Webhook endpoints page, select Create.
  3. Enter the Endpoint URL. It must be a valid HTTPS URL.
  4. Select the Event types that should trigger this webhook.
  5. Optionally select a Point of sale to filter events for one point of sale. A point of sale is required when you select booking events.
  6. Leave Active selected; inactive webhooks do not receive events.
  7. Select Create webhook.
  8. Copy the secret from the Webhook secret dialog and store it securely.

Available event types

Select from these event types when creating or editing an endpoint:

  • Orders: Order Settled
  • Bookings: Booking Created, Booking Updated, Booking Cancelled, Quota Updated
  • Customers: Contact Created, Contact Updated, Contact Deleted, Contact Newsletter Consent Changed, Contact Subscription Added, Contact Subscription Removed
  • Scans: Validation checked, Entry committed, Exit committed, Other commitment
  • Segments: Segment Created, Segment Updated, Segment Deleted, Segment Membership Added

Relay scans from selected entry gates

Connect KORONA.entry before setting up scan relay. Entry gates are imported during the regular KORONA synchronization every 15 minutes. To import them sooner, open Integrations and select Synchronize in the KORONA.cloud section. Scan relay does not require a customer email address, personalized communications, or coupon issuance. It applies to newly received scans; enabling it does not replay historical scan activity.

  1. Select Entry committed for entry activity, Exit committed for exits, or Validation checked for validation checks. Other commitment covers commitments with an unrecognized direction.
  2. Under Scan filters, choose Entry gates. Search by gate name or number, then select matching KORONA.entry gates. Select Load more to browse the next page of results. Leave it empty to receive scans from all gates.
  3. For successful admissions only, select Entry accepted under Scan outcomes and Entry under Scan directions. Validation passing alone does not mean the visitor entered.
  4. Optionally restrict scans to Ticket products or Linked events. These filters require a resolved local ticket entitlement or attendance. An unresolved ticket can still be relayed when no local-context filter is selected.
  5. Leave Include original provider payload off unless the receiver needs additional provider ticket details. When selected, the relay excludes inbound credentials.
  6. Save the endpoint and store its signing secret. Use Send test event to check the receiver connection and signature. Test events are explicitly marked as samples and bypass the endpoint's filters; use an actual scan at the selected gate to check gate matching.

Scan filters apply to scan events. The Point of sale selection applies to non-scan events, including bookings; scans use their own scan filters. All selected filter groups must match; multiple values within one group are alternatives. Gate matching uses the gate where the scan actually happened, rather than gates allowed by the ticket. A scan without a gate number cannot match a selected gate. Gate selections are scoped to the KORONA.entry account; review them when changing accounts.

Entry committed includes denied attempts as well as accepted entries. Only an explicit successful access commitment has the Entry accepted outcome. Blank results remain unknown, and exits do not count as successful entries. These notifications describe provider scan activity; they do not issue visitor rewards or send attendance communications.

Scan data and recovery

The receiver gets the provider result, denial reason, direction, gate number, group usage quantity, provider scan time, receipt time, and stable source identity. Gate names are included when available from the gate catalog; reported gate numbers are preserved even without a catalog name. Scans may arrive late or out of order. Process each event by its identity rather than treating delivery time as the scan time.

The JSON id matches X-ToucanTix-Event-Id. Store it to deduplicate outbound retries. Replayed incoming scans retain the same event identity. Local ticket resolution can be retried separately without publishing another scan or repeating its relay.

If a delivery fails, inspect Delivery observability and fix the receiving URL, signing-secret configuration, or receiver availability. Transient failures retry automatically; a receiver must handle duplicate delivery safely. After synchronization finishes, use Reload gates to load the updated gate list. Missing local ticket context does not prevent an unfiltered relay, but it cannot satisfy selected ticket-product or linked-event filters.

Using Order Settled for external analytics

Order Settled is an authoritative server-side notification that an order entered a settled payment state. Its payload includes the order number, payment state and method, currency, total and settled gross values, settled line items, vouchers, and these additive fiscal fields:

PathMeaning
data.order.total_netSum of the net values of settled invoice and receipt lines
data.order.total_taxSum of the tax values of settled invoice and receipt lines
data.order.shipping.gross_valueGross total of settled lines classified as shipping when each line was created
data.order.shipping.net_valueTax-exclusive total of those shipping lines
data.order.shipping.taxTax total of those shipping lines
data.line_items[].unit_gross_valueSettled line gross total divided by its quantity
data.line_items[].unit_net_valueSettled line net total divided by its quantity
data.line_items[].gross_valueSettled line total including included and additional taxes, after line-item discounts
data.line_items[].net_valueSettled line total excluding included and additional taxes, after line-item discounts
data.line_items[].taxDifference between the settled gross and net line totals
data.line_items[].tax_breakdown[]Tax component values and, when authoritative, their tax_rate and included status
data.line_items[].discountDiscount applied to a settled invoice line; omitted for receipt-backed lines
data.line_items[].is_shippingSnapshot of whether the line used the configured shipping article when it was created
data.vouchers[]Applied voucher id, number, and name

Every monetary value in the webhook uses an object with decimal-string amount, integer cents, and ISO currency. Use the line-total cents for exact reconciliation. A unit value is divided and rounded to the currency's minor unit when the line total does not divide evenly by quantity. When a settled line has quantity zero, both unit fields are omitted because no unit value can be calculated. Tax rates are numeric percentages, so 19 means 19%.

For receipt-backed lines with a verified stored net total, the stored receipt gross and net totals are authoritative, and tax is their difference. Rated components are included only when their reconstructed values sum exactly to that authoritative tax. Otherwise, the breakdown contains one component with the authoritative tax_value and sets tax_rate and included to null; receivers must not infer a rate allocation from that component.

Historical receipt rows created before net totals were persisted are not treated as zero-net purchases. Their additive fiscal line fields are omitted, and order-level net and tax aggregates that depend on those lines are omitted as well. Fiscal fields are also omitted when a receipt line's stored currencies do not match the order currency; receivers must not add differently denominated cents values.

Receipt items do not store an authoritative row-level discount. KORONA Event therefore omits discount from receipt-backed lines instead of repeating a pricing-level discount across multiple receipt rows. Invoice-backed lines continue to expose their persisted line discount.

Shipping classification is stored with each settled line. Changing the configured shipping article later does not reclassify these snapshots, and a newly configured article is treated as shipping only for lines created after that change. Lines that existed before the snapshot field was introduced were classified using the shipping article configured during the upgrade, so their classification might not reflect an earlier configuration.

The top-level net and tax values sum settled lines; vouchers, account transactions, and other order-level adjustments are not distributed across them. Keep data.order.total_gross and settled_gross when reconciling the complete order. The webhook remains contract version 1 because the existing fields and meanings are unchanged and the fiscal fields are additive. Receivers must ignore unknown fields and tolerate the new fields being absent in older or staged deliveries.

It is not a ready-made Google Ads or GA4 conversion feed. The webhook does not contain browser campaign parameters or click identifiers such as gclid, fbclid, or UTM values. A server-side advertising integration must obtain the legally permitted click or session identifier separately, join it to the settled order, transform the documented fiscal fields for its destination, deduplicate webhook deliveries, and send the destination request itself.

For browser-side campaign attribution and purchase events, use Configure external analytics and consent.

Booking identity for grouped event templates

For offerable.booking.created, offerable.booking.updated, and offerable.booking.cancelled, the offerable_* fields identify the public offer that an external system can query or book. When an occurrence was created from an internal child event template in a group, these fields therefore identify the public parent event template rather than the internal child.

FieldGrouped event-template booking
offerable_idID of the public parent event template
offerable_nameName of the public parent event template
offerable_typeEVENT_TEMPLATE
on_demand_event_idID of the materialized occurrence
offerable_span, offerable_span_begin, offerable_span_endTime span of that occurrence

KORONA Event still calculates the quota and point-of-sale routing from the internal source template and the specific occurrence. It retains the child ID as source_event_template_id in the integration-event metadata for routing and diagnostics; this internal metadata is not an additional field in the delivered JSON body. Ungrouped event templates use their own ID in both roles.

Verify deliveries

Each delivery is a JSON POST. There is no universal envelope: scan, order, and segment-membership events use canonical event fields such as key, version, occurred_at, and tenant, while booking, quota, and legacy record-change variants use type, created_at, tenant_id, and tenant_slug. contact.created and contact.updated can be delivered in both canonical and legacy variants when both production subscription paths are active. Build your receiver against the event and payload family shown in the complete webhook reference, not against one sample body.

Every production delivery includes these common authoritative headers:

  • Content-Type — application/json
  • X-Webhook-Signature — HMAC-SHA256 over <timestamp>.<raw-json-body> using the endpoint secret, formatted as t=<timestamp>,v1=<hex>
  • X-Webhook-Event-Id — the database event ID for the delivery path; use it for deduplication
  • X-Webhook-Event-Type — the event key for this delivery
  • X-Webhook-Timestamp — the Unix timestamp used in signature verification

The integration and legacy delivery paths add compatibility aliases and path-specific metadata headers. The complete webhook reference lists them and identifies which path each event currently uses. Custom headers cannot replace any authoritative header, regardless of letter casing. You may use a custom Authorization header for your receiver.

Verify the signature on the raw request body for every delivery. Compare the expected and received HMAC with a constant-time comparison, and reject timestamps more than five minutes from your receiver's current time. Store X-Webhook-Event-Id with the processing result and make handling idempotent: a repeated event ID must be rejected or acknowledged without applying the event twice. This protects against both replayed requests and normal delivery retries.

To test the wiring, open the endpoint's edit page and select Send test event. Scan test events use the canonical delivery route, include data.sample: true and X-Toucantix-Webhook-Sample: true, and bypass the endpoint's filters. They go only to the selected endpoint; use an actual scan to check filter matching. Other test events use the legacy delivery route, so their body and path-specific headers can differ from the production delivery for the same event. Use test events to check connectivity and signature verification, then build your parser against the production variant in the reference.

Delivery, retries, and monitoring

A delivery has five total attempts: the initial attempt followed, when the failure is retryable, by attempts after 30 seconds, 60 seconds, 5 minutes, and 15 minutes. There is no fifth retry after 30 minutes. Integration delivery retries HTTP 408, 425, 429, and 5xx responses; the legacy route retries HTTP 408, 429, 499, and 5xx responses. Both routes retry connection and timeout failures. When all attempts are exhausted, the delivery is marked dead-lettered and is not retried again.

KORONA Event allows 5 seconds to establish the connection and 10 seconds to receive the response. Verify the signature, enqueue durable processing, and return a 2xx response within that 10-second response/read deadline. Do not perform long-running work before acknowledging the delivery; a connection or response timeout is treated as a failed attempt and can cause a retry.

The Webhook endpoints list shows Last delivery, Failures, and a Status of Healthy, Failing, or Disabled per endpoint, so you can spot a broken receiver quickly.

To stop new deliveries, archive the endpoint. Archiving removes it from active subscriptions and can be undone from the archived list. A request that was already queued or in progress can still reach your URL, so keep signature verification and idempotent handling active during the transition.

Expected result

Your endpoint receives signed JSON events for the selected event types shortly after they happen, a test event arrives on demand, and the endpoint shows Healthy in the list.

Troubleshooting

ProblemWhat to check
No events arriveThe endpoint is Active, not archived, the right Event types are selected, and no Point of sale filter excludes the events.
Endpoint shows FailingYour receiver returns a success status quickly; check Last delivery and Failures in the list and your server logs.
Signature verification failsYou verify HMAC-SHA256 over timestamp.payload with the stored secret; if the secret is lost, create a new endpoint.
Events stop after repeated failuresDead-lettered deliveries are not retried; fix the receiver and use Send test event to confirm recovery.

Ready to Get Started?

Book a free demo or reach out — we’d love to hear from you.