EventsLocationsSpacesSolutionsPricingIntegrationsAPIBlogHelp
EventsLocationsSpacesSolutionsPricingIntegrationsAPIBlogHelp
© 2026 AOA
Getting startedArtistsFor artistsOrganizersVenuesPast eventsCareersPartnersBubble avatarBento pageGitHub
AboutWhat is AOAPrivacySecurityTermsMerchantCookies
Pricing
UAEN
PricingSupport
FOR PEOPLE
People nearbyHow it worksAOA ConnectSuspended coffee
FOR BUSINESS
VenuesOrganizersFor space ownersFor artists and bands
Solutions
Event check-inEvent networkingTelegram botBento pageLoyalty programSuspended coffeeNFC tags for venues
Extend AOA
IntegrationsAPI & SDK
Help centerDocumentationBlogAbout AOAContact

API reference

  • Introduction
  • Authentication
  • Sandbox
  • Rate limits
  • Errors
  • Versioning
  • Events
  • Reservations
  • Checkout

Webhooks

  • Introduction
  • Event types
  • Verifying signatures
  • Make, Zapier, n8n

Webhooks

Event types

Every AOA webhook event type: order.paid, order.failed, order.refunded, event.cancelled, event.updated, attendee.registered and attendee.checked_in.

Subscribe only to what you handle: extra events create noise and use up your handler’s capacity. The list of events is set when you register the URL and changed with PATCH.

TypeWhen it’s sent
order.paidA ticket was paid for. This is where the event attendee appears
order.failedThe payment didn’t go through or the time for it ran out
order.refundedThe money was refunded, the ticket voided
event.cancelledThe event was cancelled: all its tickets are void
event.updatedThe time or place changed
attendee.registeredAn attendee was confirmed without payment: free registration, an approved application, a guest added by the organizer
attendee.checked_inA guest checked in at the door

order.paid

The main event of the money flow. It arrives after the bank confirms the payment, that is when the money has really been charged, not when the buyer pressed “Pay”.

JSON
{
  "id": "3d9f7c21-…",
  "type": "order.paid",
  "createdAt": "2026-07-30T12:02:31.000Z",
  "data": {
    "paymentId": "d3f1a2b4-…",
    "eventId": "a1b2c3",
    "status": "SUCCESS",
    "amountMinor": 90000,
    "currency": "UAH",
    "attendeeId": "clx9attendee01"
  }
}

amountMinor is kopiyky (hundredths of a hryvnia) as an integer. attendeeId appears together with a successful payment; you can match it against the guest list.

order.failed

The payment won’t be completed and there will be no ticket for it. It arrives once per payment, with the same paymentId that creating the order through the API returns.

JSON
{
  "id": "5c1e8a47-…",
  "type": "order.failed",
  "createdAt": "2026-07-30T12:14:05.000Z",
  "data": {
    "paymentId": "e7a4c9d2-…",
    "eventId": "a1b2c3",
    "status": "EXPIRED",
    "amountMinor": 90000,
    "currency": "UAH"
  }
}

status shows the reason: FAILED when the bank declined the card or invoice, and EXPIRED when the time to pay ran out or the buyer abandoned the payment or replaced it with a new one (for example, changed the number of tickets and got a new link). There’s no attendeeId field here: an attendee only appears with a successful payment.

event.cancelled

The event webhooks exist for. It can arrive a week after the purchase, and you won’t see it by polling the order status.

JSON
{
  "id": "7a2b9c14-…",
  "type": "event.cancelled",
  "createdAt": "2026-08-01T09:15:00.000Z",
  "data": {
    "eventId": "a1b2c3",
    "title": "Джаз-вечір",
    "startAt": "2026-08-15T18:00:00.000Z"
  }
}

Cancellation doesn’t mean a refund

These are two different events. Cancelling an event voids the tickets, and the organizer refunds the money: each refund arrives as a separate order.refunded. Don’t treat the money as refunded until you’ve received exactly that.

order.refunded

JSON
{
  "id": "b5e8d033-…",
  "type": "order.refunded",
  "createdAt": "2026-08-01T09:16:12.000Z",
  "data": {
    "paymentId": "d3f1a2b4-…",
    "eventId": "a1b2c3",
    "status": "REFUNDED",
    "amountMinor": 90000
  }
}

amountMinor is the amount actually refunded. It can be less than the original payment: if the buyer paid the service fee, it isn’t refunded.

The webhook doesn’t pass on the refund reason: it’s the organizer’s internal note, and it can contain personal data.

event.updated

The time or place of a published event changed. Editing the title, description or cover doesn’t trigger this webhook.

JSON
{
  "id": "9e4f2b10-…",
  "type": "event.updated",
  "createdAt": "2026-08-02T10:20:00.000Z",
  "data": {
    "eventId": "a1b2c3",
    "title": "Джаз-вечір",
    "changed": ["time"],
    "startAt": "2026-08-15T19:00:00.000Z",
    "endAt": "2026-08-15T23:00:00.000Z",
    "allDay": false,
    "dates": [
      {
        "startAt": "2026-08-15T19:00:00.000Z",
        "endAt": "2026-08-15T23:00:00.000Z"
      }
    ],
    "location": {
      "name": "Coffee Lab",
      "address": "вул. Хрещатик, 1",
      "city": "Київ"
    }
  }
}

changed says what exactly changed: time, place or both. The other fields carry the current values in the same shape as GET/api/v1/events/{eventId}: startAt, endAt, allDay and location (null if no place is set), so you can simply overwrite your record.

dates lists the days or time slots in chronological order; a regular event has one item. It matters for multi-day events: when a day in the middle moves, the overall startAt and endAt stay the same. A cancellation arrives as a separate event.cancelled.

attendee.registered

An attendee was confirmed without payment: a free registration, a ticket with a 100% coupon, a guest added by the organizer or an approved application. Paid registrations arrive as order.paid, so they don’t come here.

JSON
{
  "id": "4b7d1e93-…",
  "type": "attendee.registered",
  "createdAt": "2026-08-03T15:40:12.000Z",
  "data": {
    "attendeeId": "clx9attendee01",
    "eventId": "a1b2c3",
    "status": "confirmed",
    "tickets": [
      { "ticketTypeId": "clx9ticket001", "quantity": 1 }
    ]
  }
}

If the event requires approving applications, the webhook arrives when the application is approved, not when it’s submitted. A guest from the waitlist comes here when they’re let in. One attendee gets one webhook: cancelling and registering again doesn’t send a second one.

The body has no name, email or phone number, only identifiers. tickets shows how many seats of which type the attendee got; ticketTypeId matches the id in the event’s list of ticket types.

attendee.checked_in

A ticket was checked in: a QR scan, a search in the guest list or being let in at the door.

JSON
{
  "id": "e2a9c6f4-…",
  "type": "attendee.checked_in",
  "createdAt": "2026-08-15T18:07:44.000Z",
  "data": {
    "attendeeId": "clx9attendee01",
    "eventId": "a1b2c3",
    "ticketTypeId": "clx9ticket001",
    "quantity": 1,
    "method": "qr",
    "checkedInAt": "2026-08-15T18:07:43.000Z"
  }
}

Check-in is about the ticket, not the person: at a multi-day event an attendee has several tickets, and each one arrives as a separate webhook. quantity above 1 means a group ticket that let several people in. method takes the values qr, nfc or manual. Undoing a check-in doesn’t send a webhook, and checking in the same ticket again doesn’t send a second one.

Delivery order

Events aren’t guaranteed to arrive in the order they happened: each delivery is retried independently, so a retried event can arrive after a later one. Go by createdAt in the body, not by the time you received it.

In practice: your handler has to cope with events arriving out of order. For example, an order.refunded that arrives before order.paid shouldn’t create a record from scratch: check the current state on your side.

PreviousIntroductionNextVerifying signatures