Event Booking Orders API
Event Booking Orders API Documentation
Base URL: https://api.nexgate.com/api/v1
Short Description: The Event Booking Orders API provides comprehensive booking management functionality formanages confirmed event ticket purchases infor events on the Nexgate platform. ThisIt APIallows enables userscustomers to view their bookingbookings details includingwith JWT-signed QR codes for secure event entry,codes, track multi-day event check-ins, retrieve complete bookingin history, download ticket PDFs, and accessenables event snapshotsorganizers atto timemonitor ofall booking.orders Thefor system supports both single-day and multi-daytheir events with per-day check-in tracking, automated ticket series generation,filtering and comprehensiverevenue booking references.stats.
Hints:
Auto-Creation:BookingsBookingsare created automatically after successful checkout payment — never created directly via this APIJWT Security:QR codes are RSA-signed JWTs containing full ticket and eventdatadata; they cannot be forgedMulti-Day Support: Full support for multi-day events with per-day check-in trackingTicket Series: Auto-generated unique series (e.g., "VIP-0001", "GENER-0042")Event Snapshots:Event detailscaptured(title, venue, organizer) are snapshotted at booking time(immutable)and never change on a booking recordBookingTicketReference:seriesShortarereadable codesauto-generated (e.g.,"EVT-2025-A3F4"VIP-0001,GENER-0042) using a per-ticket-type counterCheck-InMulti-dayTracking:eventsCompletestorehistoryawithfulllocation,checkInsstaff,arrayandper ticket — one record per dayinformationattendedAccess Control:Customers see their ownbookings,bookings only; organizers seeeventbookingsbookings,for their events; admins see allNotificationTheIntegration:PDFAuto-emailsendpointticketsreturnstoabuyerbinaryandfile,optionallynottoaattendeesJSON Virtual Events: Includes virtual meeting links for online/hybrid eventsAttendee Management: Track tickets for buyer and other attendees separatelyresponse
Standard Response Format
All APIJSON responsesendpoints follow a consistent structure using ourthe Globe Response Builder pattern:
Success Response Structure
{
"success": true,
"httpStatus": "OK",
"message": "Operation completed successfully",
"action_time": "2025-12-11T10:2026-05-23T10:30:45",
"data": {
// Actual response data goes here
}
}
Error Response Structure
{
"success": false,
"httpStatus": "BAD_REQUEST"NOT_FOUND",
"message": "ErrorBooking description"not found",
"action_time": "2025-12-11T10:2026-05-23T10:30:45",
"data": "ErrorBooking description"not found"
}
Standard Response Fields
| Field | Type | Description |
|---|---|---|
success |
boolean | true for successful operations, false for errors |
httpStatus |
string | HTTP status name (OK, NOT_FOUND, FORBIDDEN, etc.) |
message |
string | Human-readable description of the result |
action_time |
string | ISO 8601 timestamp of when the response was generated |
data |
object/string | Response payload on success; error message string on failure |
HTTP Method Badge Standards
- GET - GET - Green (
ReadSafe, read-only operations) - POST - POST - Blue (Create new resources)
- PUT - PUT - Yellow (Update/replace entire resource)
- PATCH - PATCH - Orange (Partial updates)
- DELETE - DELETE - Red (Remove resources)
BookingOrderResponseEndpoints
1. Get Booking by ID
ThisPurpose: isRetrieve the comprehensive response structure returned bycomplete booking endpoints:details including all tickets, check-in history, and event snapshot for a specific booking.
Endpoint: GET {base_url}/e-events/booking-orders/{bookingId}
Access Level: 🔒 Protected (Booking Owner, Event Organizer, or Admin)
Authentication: Bearer Token
Request Headers:
| Header | Type | Required | Description |
|---|---|---|---|
Authorization |
string | Yes | Bearer token (format: Bearer <token>) |
Path Parameters:
| Parameter | Type | Required | Description | Validation |
|---|---|---|---|---|
bookingId |
string (UUID) | Yes | Unique booking order ID | Must be valid UUID |
Success Response JSON Sample:
{
"success": true,
"httpStatus": "OK",
"message": "Booking retrieved successfully",
"action_time": "2026-05-23T10:30:45",
"data": {
"bookingId": "550e8400-e29b-41d4-a716-446655440000",
"bookingReference": "EVT-A3F4B21C",
"status": "CONFIRMED",
"event": {
"eventId": "770e8400-e29b-41d4-a716-446655440002",
"title": "East African Tech Summit 2025",
"startDateTime": "2025-12-15T09:00:00",
"endDateTime": "2025-12-17T18:00:00",
"timezone": "Africa/Nairobi",
"location": "KICC Nairobi, Harambee Avenue, Nairobi",
"format": "HYBRID",
"virtualDetails": {
"platform": "ZOOM",
"meetingUrl": "https://zoom.us/j/123456789",
"meetingId": "123 456 789",
"passcode": "summit2025",
"additionalInstructions": "Join 5 minutes early for networking"
}
},
"organizer": {
"name": "TechEvents Kenya",
"email": "organizer@techevents.ke",
"phone": "+254712345678"
},
"customer": {
"customerId": "660e8400-e29b-41d4-a716-446655440001",
"name": "johndoe",
"email": "john@example.com"
},
"tickets": [
{
"ticketInstanceId": "880e8400-e29b-41d4-a716-446655440010",
"ticketTypeName": "VIP Pass",
"ticketSeries": "VIP-0001",
"ticketNumber": "VIP-0001",
"price": 150.00,
"qrCode": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"attendanceMode": "IN_PERSON",
"attendee": {
"name": "John Doe",
"email": "john@example.com",
"phone": "+255712345678"
},
"buyer": {
"name": "John Doe",
"email": "john@example.com",
"buyerType": "SYSTEM_USER"
},
"checkIns": [
{
"checkInTime": "2025-12-15T09:15:00+03:00",
"checkInLocation": "Main Gate",
"checkedInBy": "Scanner Operator 1",
"dayName": "Day 1 - Opening Day",
"scannerId": "SCANNER-001",
"checkInMethod": "QR_SCAN"
},
{
"checkInTime": "2025-12-16T08:45:00+03:00",
"checkInLocation": "VIP Entrance",
"checkedInBy": "Scanner Operator 2",
"dayName": "Day 2 - Conference Day",
"scannerId": "SCANNER-003",
"checkInMethod": "QR_SCAN"
}
],
"hasBeenCheckedIn": true,
"lastCheckedInAt": "2025-12-16T08:45:00+03:00",
"lastCheckedInBy": "Scanner Operator 2",
"lastCheckInLocation": "VIP Entrance",
"lastCheckInDayName": "Day 2 - Conference Day",
"status": "USED",
"validFrom": "2025-12-15T09:00:00+03:00",
"validUntil": "2025-12-17T18:00:00+03:00"
},
{
"ticketInstanceId": "880e8400-e29b-41d4-a716-446655440011",
"ticketTypeName": "General Admission",
"ticketSeries": "GENER-0042",
"ticketNumber": "GENER-0042",
"price": 50.00,
"qrCode": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"attendanceMode": "IN_PERSON",
"attendee": {
"name": "Jane Smith",
"email": "jane@example.com",
"phone": "+255723456789"
},
"buyer": {
"name": "John Doe",
"email": "john@example.com",
"buyerType": "SYSTEM_USER"
},
"checkIns": [],
"hasBeenCheckedIn": false,
"lastCheckedInAt": null,
"lastCheckedInBy": null,
"lastCheckInLocation": null,
"lastCheckInDayName": null,
"status": "ACTIVE",
"validFrom": "2025-12-15T09:00:00+03:00",
"validUntil": "2025-12-17T18:00:00+03:00"
}
],
"totalTickets": 2,
"checkedInTicketsCount": 1,
"subtotal": 200.00,
"total": 200.00,
"bookedAt": "2025-12-11T10:30:45",
"cancelledAt": null
}
}
Success Response Field Descriptions
:Root Level Fields
| Field | Description | |
|---|---|---|
bookingId | Unique booking |
|
bookingReference | Short readable code (e.g., EVT-) |
|
status | Booking status: CONFIRMED, CANCELLED |
|
event.eventId |
Event |
|
event.title |
Event title at time of booking (immutable snapshot) | |
event.startDateTime |
Event start — LocalDateTime (no timezone) | |
event.endDateTime |
Event end — LocalDateTime (no timezone) | |
event.timezone |
IANA timezone string (e.g., Africa/Nairobi) |
|
event.location |
Full venue address snapshotted at booking | |
event.format |
ONLINE, HYBRID |
|
event.virtualDetails |
ONLINE/HYBRID events |
|
event.virtualDetails.platform |
GOOGLE_MEET, MS_TEAMS, CUSTOM |
|
event.virtualDetails.meetingUrl |
||
event.virtualDetails.meetingId |
||
event.virtualDetails.passcode |
Meeting passcode (optional) | |
organizer.name |
Organizer name snapshotted at booking | |
organizer.email |
||
organizer.phone |
Organizer phone | |
customer.customerId |
Customer account UUID | |
customer.name |
Customer username | |
customer.email |
Customer email | |
tickets |
Array of all booked ticket instances | |
tickets[].ticketInstanceId |
Unique ticket instance UUID | |
tickets[].ticketTypeName |
Ticket type name (e.g., VIP Pass) |
|
tickets[].ticketSeries |
Auto-generated series (e.g., VIP-0001) |
|
tickets[].ticketNumber |
Same as ticketSeries — used for PDF labeling | |
tickets[].price |
Price paid for this ticket | |
tickets[].qrCode |
RSA-signed JWT — used for gate check-in scanning | |
tickets[].attendanceMode |
IN_PERSON or ONLINE (relevant for hybrid events) |
|
tickets[].attendee |
Person this ticket is for | |
tickets[].attendee.name |
Attendee full name | |
tickets[].attendee.email |
Attendee email | |
tickets[].attendee.phone |
Attendee phone | |
tickets[].buyer |
Person who purchased this ticket | |
tickets[].buyer.name |
Buyer name | |
tickets[].buyer.email |
Buyer email (null for AT_DOOR purchases) | |
tickets[].buyer.buyerType |
SYSTEM_USER (online) or AT_DOOR (sold at gate) |
|
tickets[].checkIns |
Full check-in history — one record per day attended | |
tickets[].checkIns[].checkInTime |
ZonedDateTime of check-in | |
tickets[].checkIns[].checkInLocation |
Where checked in (e.g., Main Gate, VIP Entrance) |
|
tickets[].checkIns[].checkedInBy |
Staff/scanner operator name | |
tickets[].checkIns[].dayName |
Event day label (e.g., Day 1 - Opening Day) |
|
tickets[].checkIns[].scannerId |
Scanner device ID | |
tickets[].checkIns[].checkInMethod |
QR_SCAN (default), MANUAL, or NFC |
|
tickets[].hasBeenCheckedIn |
true if ticket has at least one check-in |
|
tickets[].lastCheckedInAt |
Most recent check-in time (ZonedDateTime, null if none) | |
tickets[].lastCheckedInBy |
Who performed the most recent check-in (null if none) | |
tickets[].lastCheckInLocation |
Location of most recent check-in (null if none) | |
tickets[].lastCheckInDayName |
Day label of most recent check-in (null if none) | |
tickets[].status |
ACTIVE, USED, or CANCELLED |
|
tickets[].validFrom |
Ticket validity start — ZonedDateTime | |
tickets[].validUntil |
Ticket validity end — ZonedDateTime | |
totalTickets |
Total ticket count in this booking | |
checkedInTicketsCount |
Count of tickets with at least one check-in | |
subtotal | Subtotal |
|
total | Total amount paid | |
bookedAt | When the booking was created — LocalDateTime | |
cancelledAt | When |
Event Snapshot Object
Virtual Details Object
Organizer Snapshot Object
Customer Info Object
Booked Ticket Response Object
Attendee/Buyer Info Object
Check-In Record Object
BookingOrderSummaryResponse Structure
Lightweight response for listing bookings:
{
"bookingId": "550e8400-e29b-41d4-a716-446655440000",
"bookingReference": "EVT-A3F4B21C",
"status": "CONFIRMED",
"eventTitle": "East African Tech Summit 2025",
"eventStartDateTime": "2025-12-15T09:00:00",
"eventLocation": "KICC Nairobi, Harambee Avenue, Nairobi",
"totalTickets": 2,
"checkedInTickets": 1,
"total": 200.00,
"bookedAt": "2025-12-11T10:30:45"
}
Endpoints
1. Get Booking by ID
Purpose: Retrieve complete booking details including all tickets and check-in history
Endpoint: GET {base_url}/e-events/booking-orders/{bookingId}
Access Level: 🔒 Protected (Booking Owner, Event Organizer, or Admin)
Authentication: Bearer Token
Request Headers:
|
Path Parameters:
Success Response: Returns complete BookingOrderResponse structure
Success Response Message: "Booking retrieved successfully"
HTTP Status Code: 200 OK
Access Rules:
Customers: Can view their own bookingsOrganizers: Can view bookings for their eventsAdmins(SUPER_ADMIN, STAFF_ADMIN): Can view any booking
Behavior:
Returns complete booking details with all ticketsIncludes event snapshot (as it was at booking time)Shows full check-in history for multi-day eventsIncludes JWT-signed QR codes for all ticketsVirtual details included for ONLINE/HYBRID events
Use Cases:
Customer viewing booking confirmationCustomer retrieving QR codes for event entryOrganizer checking booking detailsAdmin reviewing booking for support
Standard Error Types:
Error Response ExamplesJSON Sample:
Booking Not Found (404):
{
"success": false,
"httpStatus": "NOT_FOUND",
"message": "Booking not found: 550e8400-e29b-41d4-a716-446655440000",
"action_time": "2025-12-11T10:2026-05-23T10:30:45",
"data": "Booking not found: 550e8400-e29b-41d4-a716-446655440000"
}
AccessStandard DeniedError (403)Types:
2. Get My Bookings
Purpose: Retrieve a summary list of all bookings for the authenticated useruser, sorted newest first.
Endpoint: GET {base_url}/e-events/booking-orders/my-bookings
Access Level: 🔒 Protected (Authenticated Users)
Authentication: Bearer Token
Request Headers:
| Header | Type | Required | Description |
|---|---|---|---|
Authorization |
string | Yes | Bearer token (format: Bearer <token>) |
Success Response: Returns array of BookingOrderSummaryResponse
Success Response Message: "Bookings retrieved successfully"
Success Response JSON Sample:
{
"success": true,
"httpStatus": "OK",
"message": "Bookings retrieved successfully",
"action_time": "2025-12-11T10:2026-05-23T10:30:45",
"data": [
{
"bookingId": "550e8400-e29b-41d4-a716-446655440000",
"bookingReference": "EVT-A3F4B21C",
"status": "CONFIRMED",
"eventTitle": "East African Tech Summit 2025",
"eventStartDateTime": "2025-12-15T09:00:00",
"eventLocation": "KICC Nairobi, Harambee Avenue, Nairobi",
"totalTickets": 2,
"checkedInTickets": 1,
"total": 200.00,
"bookedAt": "2025-12-11T10:30:45"
},
{
"bookingId": "550e8400-e29b-41d4-a716-446655440001",
"bookingReference": "EVT-B5D2E12F",
"status": "CONFIRMED",
"eventTitle": "Dar es Salaam Food Festival",
"eventStartDateTime": "2025-12-20T11:00:00",
"eventLocation": "Mlimani City, Sam Nujoma Road, Dar es Salaam",
"totalTickets": 4,
"checkedInTickets": 0,
"total": 150.00,
"bookedAt": "2025-12-10T14:20:30"
}
]
}
HTTPSuccess StatusResponse Code: 200 OK
Sorting: Bookings sorted by bookedAt descending (newest first)
BehaviorFields:
ReturnslightweightsummaryField forDescription alluser'sbookingsSortednewestbookingIdfirstUnique Includesbooking UUIDbookingReferenceShort readable code (e.g., EVT-A3F4B21C)statusCONFIRMEDorCANCELLEDeventTitleEvent title snapshotted at booking time eventStartDateTimeEvent start date/time — LocalDateTime (no timezone) eventLocationVenue address snapshotted at booking time totalTicketsTotal number of tickets in this booking checkedInTicketsNumber of tickets with at least one check-in progress(checkedInTickets/totaltotalTickets)Total ShowsamountupcomingpaidandpasteventsbookedAtWhen booking was created — LocalDateTime
UseError CasesResponse JSON Sample:
{
"success": false,
"httpStatus": "NOT_FOUND",
"message": "User viewingnot bookingfound",
history "action_time": Dashboard"2026-05-23T10:30:45",
showing"data": all"User purchases not Findingfound"
booking}
for upcoming event
Checking past event attendance
Standard Error Types:
3. Download Ticket PDF
Purpose: Download or preview the PDF ticket PDF for a specific ticket instance belonging to the authenticated useruser.
Endpoint: GET {base_url}/e-events/booking-orders/tickets/{ticketId}ticketInstanceId}/pdf
Access Level: 🔒 Protected (AuthenticatedTicket Users)Owner)
Authentication: Bearer Token
Request Headers:
| Header | Type | Required | Description |
|---|---|---|---|
Authorization |
string | Yes | Bearer token (format: Bearer <token>) |
Path Parameters:
| Parameter | Type | Required | Description | Validation |
|---|---|---|---|---|
ticketInstanceId |
Yes | Must be valid UUID |
Query Parameters:
| Parameter | Type | Required | |||
|---|---|---|---|---|---|
mode |
string | No | download | inline opens the PDF |
download |
Mode Values:
| | |
| |
Success Response: Returns a binaryBinary PDF file (not JSON)
Success Response Headers:
| Header | Value |
|---|---|
Content-Type |
application/pdf |
Content-Disposition |
attachment; filename="ticket-{series}.pdf" |
HTTPPDF StatusContent Code: 200 OK
BehaviorIncludes:
VerifiesEventthename,ticketdate,belongsandto the authenticated user before generating the PDFvenueGeneratesAttendeethe ticket PDF on-the-fly using the event's SVG templatenameTheTicketPDF contains event details, attendee name, seat/series, QR code for check-in,series andticket numbertype- QR code
encodes a(RSA-signedJWTJWT)token used byfor gatestaff to verify the ticket at entryscanning FileTicketnamevalidityin theContent-Dispositionheader uses the ticket's series (e.g.ticket-VIP-0033.pdf)period
UseError CasesResponse JSON Sample:
{
User"success": downloadingfalse,
their"httpStatus": ticket"FORBIDDEN",
after"message": booking "You Re-downloadingdon't ahave lost or deleted ticket
Saving ticketpermission to deviceaccess forthis offlineticket",
"action_time": "2026-05-23T10:30:45",
"data": "You don't have permission to access atthis theticket"
event }
Standard Error Types:
4. Get Event Orders (Organizer)
Purpose: Retrieve a paginated list of all booking orders for a specific event with filtering by status, buyer type, and ticket type, plus aggregated revenue and order stats.
Endpoint: GET {base_url}/e-events/booking-orders/event/{eventId}
Access Level: 🔒 Protected (Event Organizer or Admin)
Authentication: Bearer Token
Request Headers:
| Type | Required | Description | ||
|---|---|---|---|---|
|
Yes | Bearer token | ||
| ||||
| ||||
|
Path Booking Status Lifecycle
Parameters:Status Descriptions
| Type | Required | Description | |||
|---|---|---|---|---|---|
eventId |
Yes | Target | |||
CurrentQuery ImplementationParameters:
All bookings created as CONFIRMEDCancellation not yet implemented (status exists for future)Tickets remain ACTIVE after booking until checked in
Ticket Instance Status
Status Lifecycle
| Type | Required | Description | Default | ||
|---|---|---|---|---|---|
status |
Filter by booking status | CONFIRMED, CANCELLED |
— | ||
buyerType |
Filter |
SYSTEM_USER, AT_DOOR |
— | ||
ticketTypeId |
No | Filter orders that include a specific ticket type | Must be valid UUID | — | |
search |
string | No | Search by buyer name, email, or booking reference | — | — |
page |
integer | No | Zero-based page index | Min: 0 | 0 |
size |
integer | No | Number of orders per page | Min: 1 | 20 |
Multi-DaySuccess EventsResponse JSON Sample:
{
"success": true,
"httpStatus": "OK",
"message": "Event orders retrieved",
"action_time": "2026-05-23T10:30:45",
"data": {
"eventId": "770e8400-e29b-41d4-a716-446655440002",
"eventTitle": "East African Tech Summit 2025",
"appliedFilters": {
"status": "CONFIRMED",
"buyerType": null,
"ticketTypeId": null,
"ticketTypeName": null,
"search": null
},
"stats": {
"totalOrders": 142,
"totalTicketsSold": 318,
"totalRevenue": 47500.00,
"confirmedOrders": 139,
"cancelledOrders": 3,
"refundedOrders": 0,
"onlineOrders": 128,
"atDoorOrders": 14
},
"orders": [
{
"bookingId": "550e8400-e29b-41d4-a716-446655440000",
"bookingReference": "EVT-A3F4B21C",
"status": "CONFIRMED",
"bookedAt": "2025-12-11T10:30:45",
"buyer": {
"buyerName": "John Doe",
"buyerEmail": "john@example.com",
"buyerType": "SYSTEM_USER"
},
"totalTickets": 2,
"checkedInCount": 1,
"total": 200.00,
"ticketTypes": [
{
"ticketTypeName": "VIP Pass",
"quantity": 1
},
{
"ticketTypeName": "General Admission",
"quantity": 1
}
]
},
{
"bookingId": "550e8400-e29b-41d4-a716-446655440099",
"bookingReference": "EVT-C7E1F34A",
"status": "CONFIRMED",
"bookedAt": "2025-12-10T18:15:00",
"buyer": {
"buyerName": "Grace Njeri",
"buyerEmail": null,
"buyerType": "AT_DOOR"
},
"totalTickets": 1,
"checkedInCount": 1,
"total": 50.00,
"ticketTypes": [
{
"ticketTypeName": "General Admission",
"quantity": 1
}
]
}
],
"pagination": {
"currentPage": 0,
"pageSize": 20,
"totalPages": 8,
"totalElements": 142,
"hasNext": true,
"hasPrevious": false
}
}
}
Success Response Fields:
| Field | Description |
|---|---|
eventId |
UUID of the queried event |
eventTitle |
Event title |
appliedFilters |
Echo of the active filter values (null = not filtered) |
appliedFilters.status |
Active booking status filter |
appliedFilters.buyerType |
Active buyer type filter |
appliedFilters.ticketTypeId |
Active ticket type UUID filter |
appliedFilters.ticketTypeName |
Resolved name of the filtered ticket type |
appliedFilters.search |
Active search string |
stats |
Aggregated stats across ALL orders for the event (not just current page) |
stats.totalOrders |
Total booking count |
stats.totalTicketsSold |
Total individual tickets across all bookings |
stats.totalRevenue |
Sum of all order totals |
stats.confirmedOrders |
Orders with status CONFIRMED |
stats.cancelledOrders |
Orders with status CANCELLED |
stats.refundedOrders |
Orders with status REFUNDED |
stats.onlineOrders |
Orders placed online (SYSTEM_USER buyer type) |
stats.atDoorOrders |
Orders sold at the gate (AT_DOOR buyer type) |
orders |
Paginated list of order rows |
orders[].bookingId |
Booking UUID |
orders[].bookingReference |
Short readable code (e.g., EVT-A3F4B21C) |
orders[].status |
Booking status string |
orders[].bookedAt |
When booking was created — LocalDateTime |
orders[].buyer.buyerName |
Buyer's name |
orders[].buyer.buyerEmail |
Buyer's email (null for AT_DOOR) |
orders[].buyer.buyerType |
SYSTEM_USER or AT_DOOR |
orders[].totalTickets |
Number of tickets in this order |
orders[].checkedInCount |
Number of tickets checked in at least once |
orders[].total |
Order total amount |
orders[].ticketTypes |
Breakdown of ticket types in this order |
orders[].ticketTypes[].ticketTypeName |
Name of the ticket type |
orders[].ticketTypes[].quantity |
How many of this type are in the order |
pagination.currentPage |
Zero-based current page index |
pagination.pageSize |
Number of items per page |
pagination.totalPages |
Total number of pages |
pagination.totalElements |
Total matching orders |
pagination.hasNext |
true if a next page exists |
pagination.hasPrevious |
true if a previous page exists |
Error Response JSON Sample:
{
"success": false,
"httpStatus": "FORBIDDEN",
"message": "Access denied: you are not the organizer of this event",
"action_time": "2026-05-23T10:30:45",
"data": "Access denied: you are not the organizer of this event"
}
Standard Error Types:
Ticket Series Generation
How Ticket Series Work
Format: {TICKET_CODE}-{COUNTER}
The ticket code is the first 5 characters of the ticket type name (uppercased, spaces removed). The counter is a zero-padded incrementing integer per ticket type, managed with pessimistic locking to prevent duplicates.
Examples:
VIP Pass→VIP-0001,VIP-0002,VIP-0003General Admission→GENER-0001,GENER-00020042Early Bird→EARLY-0001,EARLY-0002
Ticket Code Extraction:
Take first 5 characters of ticket type nameRemove spacesConvert to uppercaseFallback to "TICK" if name empty
Counter System:
SeparateThe counterper ticket type (stored in DB)Pessimistic locking prevents duplicatesCounternever resets(unique forever)Increments with each— ticketcreatedseries
Exampleglobally Flow:unique per type forever.
Ticket Type: "VIP Pass"
Counter: 0
First Booking (3 tickets):
- VIP-0001 (counter now 1)
- VIP-0002 (counter now 2)
- VIP-0003 (counter now 3)
Second Booking (2 tickets):
- VIP-0004 (counter now 4)
- VIP-0005 (counter now 5)
Why Series Numbers?
Easy reference for staffQuick ticket identificationHelps track capacity soldProfessional appearance on PDF tickets
JWT-Signed QR Codes
Security
Each Architectureticket's
WhatqrCode field is a full RSA-signed JWT (2048-bit key pair generated when the QRevent Code?is published). The JWT payload contains:
NotticketInstanceId,aticketTypeId,randomticketTypeName,stringticketSeriesFulleventId,JWTeventName,(JSON Web Token)eventStartDateTimeSignedattendeeName,withattendeeEmail,event'sattendeePhone,RSA private keyattendanceModeContains complete ticket and event databookingReferenceCannoteventSchedulesbe—forged or tampered with
JWT Structure:
Header.Payload.Signature
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.
eyJ0aWNrZXRJbnN0YW5jZUlkIjoiODgw...
[RSA signature]
JWT Payload Contains:
ticketInstanceId (UUID)ticketTypeId (UUID)ticketTypeName (string)ticketSeries (string)eventId (UUID)eventName (string)eventStartDateTime (timestamp)attendeeName (string)attendeeEmail (string)attendeePhone (string)attendanceMode (IN_PERSON/ONLINE)bookingReference (string)eventSchedules(array ofdaysday objects for multi-dayevents)validFrom (timestamp)validUntil (timestamp)events:
Multi-Day Event Schedules:
"eventSchedules": [
{
"dayName": "Day 1 - Opening Day",
"startDateTime": "2025-12-15T09:00:00+03:00",
"endDateTime": "2025-12-15T18:00:00+03:00",
"description": "Conference Opening & Keynotes"
},
{
"dayName": "Day 2 - Conference Day",
"startDateTime": "2025-12-16T09:00:00+03:00",
"endDateTime": "2025-12-16T18:00:00+03:00",
"description": "Technical Sessions & Workshops"
},
{
"dayName": "Day 3 - Closing Day",
"startDateTime": "2025-12-17T09:00:00+03:00",
"endDateTime": "2025-12-17T18:00:00+03:00",
"description": "Finals & Networking"
}
]
RSA Key Pair:
GeneratedvalidFrom,when event is published (2048-bit)Private key: Stored securely in database (for signing)Public key: Available to scanners (for verification)Cannot derive private key from public keyvalidUntil
VerificationWhy ProcessJWT? (Scanner App):
Scanner reads QR codeExtracts JWT tokenFetches event's public keyVerifies JWT signatureChecks expiry and validityChecks if already checked in for this dayRecords check-in if valid
Why JWT over Simple Codes?
Cannot be forged: Requires private keyContains all data: No database lookup neededOffline capable: ScannerScanners can verify the ticket offline using the public key withoutinterneta Tamper-proof:database lookup. Anymodificationtampering breakssignaturethe Time-limited:signature.validFrom/validUntilThebuiltembeddedinDayletstracking:scannersMulti-enforce per-dayschedulescheck-inembeddedrules
eventSchedules Multi-Day Event Check-Ins
How
For Multi-Daymulti-day Check-Insevents, Workeach
Single-Dayticket Event:
- be
Onecheckedcheck-inexpectedAfter check-in, status changes to USEDSimple entry tracking
Multi-Day Event (e.g., 3-day festival):
Multiple check-ins allowed (oneonce perday)day. Each day tracked separatelyTicket remains ACTIVE after first dayFull history stored inThecheckInsarray
Check-Inthe Recordfull Fields:
- The
checkInTime: When check-in happenedcheckInLocation: Where (e.g.hasBeenCheckedIn,"Main Gate"lastCheckedInAt,"VIP Entrance")checkedInBy: Staff/scanner operator namedayName: Which day (must match eventSchedules)scannerId: Scanner device IDcheckInMethod: QR_SCAN, MANUAL, NFC (default: QR_SCAN)
Example: 3-Day Festival
Day 1 (Friday):
,{ "checkInTime": "2025-12-15T18:30:00+03:00"lastCheckedInBy"checkInLocation": "Main Gate"lastCheckInLocation,"checkedInBy":and"StafflastCheckInDayNameMemberconvenience1",fields"dayName":reflect"Daythe1most-recentFriday Night", "scannerId": "SCANNER-001", "checkInMethod": "QR_SCAN" }
Day 2 (Saturday):
{
"checkInTime": "2025-12-16T14:15:00+03:00",
"checkInLocation": "VIP Entrance",
"checkedInBy": "Staff Member 2",
"dayName": "Day 2 - Saturday",
"scannerId": "SCANNER-003",
"checkInMethod": "QR_SCAN"
}
Day 3 (Sunday):
{
"checkInTime": "2025-12-17T12:00:00+03:00",
"checkInLocation": "Main Gate",
"checkedInBy": "Staff Member 1",
"dayName": "Day 3 - Sunday",
"scannerId": "SCANNER-001",
"checkInMethod": "QR_SCAN"
}
Validation Rules:entry.
dayNamein a check-in record must match onefromofJWT'stheeventSchedulesday names embedded in the JWTCannotAcheckticket cannot be checked in twice on the same dayCanTicketcheckstatusinstaysonACTIVEdifferentacross daysCheck-inandtime must be within day's start/end
Benefits:
Accurate attendance tracking per dayPrevents duplicate same-day entriesAllows organizerstransitions toseeUSEDdaily attendanceSupports flexible entry (don't needafter alldays)expected check-ins
Event Snapshots
Why Snapshots?
Problem: Event details can(title, changevenue, aftertimes, organizer contacts) are captured into the booking record at the moment of purchase and never change. This means:
Event renamedTime changedVenue movedOrganizer updated contact
Solution: Capture event state at booking time
Snapshot Fields (Immutable after booking):
eventTitleeventStartDateTimeeventEndDateTimeeventTimezoneeventLocation (full venue details)eventFormatvirtualDetails (for online/hybrid)organizerNameorganizerEmailorganizerPhone
Benefits:
- PDF
Tickets:ticketsGeneratealwayscorrectshowticketwhatwith original details Legal Record: Whatthe customer actuallypurchasedboughtEmailBookingsConfirmations: Showremain accuratebookingevendetailsif the organizer later renames or reschedules the eventRefundTheLogic:eventReferenceandoriginalorganizereventblocksdetailsin AuditBookingOrderResponseTrail:alwaysTrackreflectwhatbooking-timechanged vs. what was bookedstate
Example Scenario:
User books ticket for "Tech Summit" at KICCSnapshot created: "Tech Summit", "KICC Nairobi"Organizer renames event to "African Tech Summit"Organizer moves venue to "Safari Park Hotel"User's booking still shows original: "Tech Summit at KICC"This is what they paid for (legal protection)
Booking Creation Flow
Automatic
Bookings Creationare Aftercreated Paymentautomatically
Trigger:by EventPaymentCompletedListener after a successful payment fires
Steps:— never through a direct API call. The flow:
Fetch
checkoutEntities:- session,
Getevent,EventCheckoutSessionand ticket type from the databaseGet Event detailsGet TicketType details
Generate
uniqueBookingaReference:- booking
Format:reference (EVT-{8-char-UUID})Example:Createticket instances (one per quantity unit, tagged with attendee and buyer info)EVT-A3F4B21CUniqueSignand readable
Create Ticket Instances:For buyer's tickets (ticketsForMe count)For each other attendee (their quantity)Each ticket gets unique series numberEach ticket assigned to correct attendee
codeGeneratea JWT QRCodes:- for
Create JWT payload witheach ticketdatausing Include event schedules for multi-daySign withthe event's RSA private keySet as ticket's qrCode field
Snapshot
Event Details:Capturecurrent eventtitle, times, locationCaptureand organizer detailsStoreintointhe booking(immutable)
Save Booking:Create EventBookingOrderEntityrecord- Save
tothedatabasebookingwithandallmarktickets Link tothe checkout session
COMPLETEDUpdate Checkout Session:Set createdBookingOrderIdMark as COMPLETED
Publish
aEvent:FiretoBookingCreatedEventTriggers notifications
BookingCreatedEventSend Notifications:Email buyer confirmation with ticketsOptionallytrigger emailothernotificationsattendeestotheirbuyer,ticketsattendees, Notify eventand organizerof new booking
TransactionThe Safety:
Entireentire flow runs in a single transactionIf—any step fails, everything rolls backNono partial bookingscreatedare
Timing:created.
Happens immediately after payment successUsually completes in < 2 secondsUser gets instant confirmation
Notification System Integration
Who Gets Notified?
1. Buyer (ALWAYS):
Receives booking confirmationGets ALL tickets (if sendTicketsToAttendees = false)Gets only THEIR tickets (if sendTicketsToAttendees = true)Channels: EMAIL, SMS, PUSH, IN_APPPriority: HIGH
2. Other Attendees (CONDITIONAL):
Only if sendTicketsToAttendees = trueEach attendee gets ONLY their ticketsChannels: EMAIL, SMSPriority: HIGHGrouped by email (all tickets for same person)
3. Event Organizer (ALWAYS):
Notified of new bookingGets booking summary (not tickets)Channels: EMAIL, IN_APPPriority: NORMAL
Notification Content
Buyer Confirmation:
Booking referenceEvent title and dateNumber of ticketsQR codes attachedEvent location/virtual linkInstructions for entry
Attendee Ticket Email:
"You've received tickets from [Buyer Name]"Event detailsTheir specific QR codesEntry instructions
Organizer Alert:
New booking notificationBuyer name and emailNumber of tickets soldTotal amountBooking reference for lookup
QR Code Delivery
sendTicketsToAttendees = false:
Buyer receives:
- Ticket 1 QR (for self)
- Ticket 2 QR (for self)
- Ticket 3 QR (for Jane)
- Ticket 4 QR (for Jane)
- Ticket 5 QR (for Bob)
Buyer must forward to Jane and Bob manually
sendTicketsToAttendees = true:
Buyer receives:
- Ticket 1 QR (for self)
- Ticket 2 QR (for self)
Jane receives:
- Ticket 3 QR (for Jane)
- Ticket 4 QR (for Jane)
Bob receives:
- Ticket 5 QR (for Bob)
Everyone gets their own tickets directly
Access Control Rules
Who
| Actor | Endpoint | Allowed? |
|---|---|---|
| Booking |
GET /booking-orders/{ |
Yes |
| Event organizer | GET /booking-orders/{id} |
Yes |
| Admin ( |
GET /booking-orders/{id} |
Yes |
| Other authenticated user | GET /booking-orders/{id} |
No — 403 |
| Any authenticated user | GET /my-bookings |
Yes (own bookings only) |
| Ticket owner | GET /tickets/{id}/pdf |
Yes |
| Other authenticated user | GET /tickets/{id}/pdf |
No — 403 |
| Event organizer | GET /booking-orders/event/{id} |
Yes |
| Admin | GET /booking-orders/event/{id} |
Yes |
| Non-organizer user | GET /booking-orders/event/{id} |
No — 403 |
Booking and Ticket Status
2.Booking Event OrganizerStatus:
CanviewbookingsStatus forDescription theireventsUsefulforcheckingCONFIRMEDattendanceActive ticketsVerifyingbooking,purchases validCustomeraresupportCANCELLEDBooking cancelled (placeholder — cancellation logic not yet implemented) Roles:SUPER_ADMIN,STAFF_ADMINStatus CanDescription viewanybookingForsupportACTIVEandTicket dispute resolution4. Others:Cannot view bookings they don't ownCannot view other users' bookings403 Forbidden error
Virtual Event SupportVirtual Details StructureIncluded for:ONLINE events (only virtual)HYBRID events (both physical + virtual)
Fields:platform: ZOOM, GOOGLE_MEET, MS_TEAMS, CUSTOMmeetingUrl: Full meeting URL (required)meetingId: Meeting ID if applicablepasscode: Meeting passcode if requiredadditionalInstructions: Extra guidance
Example (Zoom):{ "platform": "ZOOM", "meetingUrl": "https://zoom.us/j/123456789?pwd=abc123", "meetingId": "123 456 789", "passcode": "summit2025", "additionalInstructions": "Please join 5 minutes earlyready forause;smoothcanstart"be}scannedUSEDExampleTicket (Custom):hasbeen checked in; still scannable for additional days
{CANCELLED"platform":Ticket "CUSTOM",cancelled;"meetingUrl":cannot"https://customplatform.com/event/12345",be"meetingId":usednull, "passcode": null, "additionalInstructions": "Use your booking email to log in" }Delivery:Included in booking responseSent in confirmation emailSent in ticket emailsAvailable before event starts
Hybrid Events:Virtual details + physical venueAttendees choose format via ticket typeIN_PERSON tickets: Physicalfor entry
ONLINEtickets:Both in same booking possibleFormat:YYYY-MM-DDTHH:mm:ssExample:2025-12-15T09:00:00UsedbookedAt,for: bookedAt, cancelledAt,cancelledAt, event snapshotsFormat:YYYY-MM-DDTHH:mm:ss±HH:mmExample:2025-12-15T09:00:00+03:00UsedvalidFrom,for:validUntil,validFrom,checkInTimevalidUntil,Server-localtimeAudit timestampsNo conversion neededSimple comparisonUser-aware timeTicket validityCheck-in timesHandlestimezone offset and display correctly
3.Ticket PlatformInstance AdminsStatus:
Date/Time Formats
DateTime
| Type | Format | Example | Used For |
|---|---|---|---|
LocalDateTime |
|||
ZonedDateTime |
WhyZonedDateTime Differentfields Formats?
LocalDateTime:
- event's
ZonedDateTime:
Example:of the viewer's locale.
Event in Nairobi (UTC+3):
startDateTime: 2025-12-15T09:00:00+03:00
Ticket validFrom: 2025-12-15T09:00:00+03:00
User in London sees: 2025-12-15T06:00:00+00:00 ✓
User in New York sees: 2025-12-15T01:00:00-05:00 ✓
Validation Rules Summary
Booking Retrieval Validation
Access Validation:
✅ User must be authenticated✅ Booking must exist✅ User must be: owner OR organizer OR admin❌ Other users cannot access
Data Validation:
✅ bookingId must be valid UUID✅ Booking must not be deleted (soft delete support)
Quick Reference Guide
HTTP Method Badge Code Templates
GET Badge:
<span style="background-color: #28a745; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">GET</span>
POST Badge:
<span style="background-color: #007bff; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">POST</span>
PUT Badge:
<span style="background-color: #ffc107; color: black; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">PUT</span>
PATCH Badge:
<span style="background-color: #fd7e14; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">PATCH</span>
DELETE Badge:
<span style="background-color: #dc3545; color: white; padding: 4px 8px; border-radius: 4px; font-family: monospace; font-size: 12px; font-weight: bold;">DELETE</span>
Endpoint Summary
| # | Endpoint | Access |
|---|---|---|
| 1 | GET /e-events/booking-orders/{bookingId} |
Owner / Organizer / Admin |
| 2 | GET /e-events/booking-orders/my-bookings |
Any authenticated user |
| 3 | GET /e-events/booking-orders/tickets/{ticketInstanceId}/pdf |
Ticket owner |
| 4 | GET /e-events/booking-orders/event/{eventId} |
Event organizer / Admin |
Common HTTP Status Codes
200 OK: Successful request401 Unauthorized:AuthenticationMissingrequired/failedor invalid token403 Forbidden:UserAccessdoesn't have permissiondenied404 Not Found:BookingResource not found500 Internal Server Error: Server-side failure (e.g., PDF generation)
BookingKey ReferenceData FormatFormats
Pattern:Booking reference:EVT-{8-char-UUID}Examples:— e.g.,EVT-A3F4B21C,EVT-F2D5E891Unique and readableEasy for customer service
Ticket Series Format
Pattern:series:{CODE}-{COUNTER}Examples:— e.g.,VIP-0001,GENER-0042,EARLY-0123Code: First 5 chars of ticket nameCounter: Unique incrementing number
QR Code Format
Type:code: RSA-signed JWTLength:(~1000-1000–2000charactersContains: Complete ticket + event dataCannot be: Forged, tampered, reused (tracked)
Data Format Standards
Dates: LocalDateTime for timestamps, ZonedDateTime for validitycharacters)IDs: UUID formatMoneyAmounts: Decimal with 2decimalsplaces (e.g.,150.)00)00CurrencyTimestamps:TZS (Tanzanian Shilling)Timezone: IANA format (Africa/Nairobi)
Booking Flow Checklist
✅ User completes checkout payment✅ Payment listener triggers✅ System generates booking reference✅ System creates ticket instances✅ System generates JWT QR codes✅ System snapshots event details✅ System saves booking order✅ System publishes booking event✅ System sends email notifications✅ User receives confirmation + QR codes
Best Practices
For Users:
Save QR codes to phone immediatelyScreenshot confirmation emailArrive earlyLocalDateTimeforcheck-inaudit Onefields;QR per person at gate
For Developers:
Always show booking reference prominentlyDisplay QR codes large enough to scanCache booking details locallyHandle offline QR displayShow check-in history clearlySupport PDF ticket generation
For Organizers:
Provide clear entry instructionsTest scanner apps before eventHave backup manual verificationTrack attendance by dayMonitor check-in progress live
Error Handling Tips
403 Forbidden: Show "This booking belongs to another user"404 Not Found: Suggest "Check your emailZonedDateTimeforcorrect reference"Network Error: Show cached booking if availableQR Load Failure: Provide booking reference as backup
Common Mistakes to Avoid
❌ Showing other users' bookings
❌ Not displaying virtual details for ONLINE events
❌ Forgetting to show multi-day check-in history
❌ Not caching QR codes for offline use
❌ Displaying mutable event data instead of snapshot
❌ Not grouping attendees' tickets properly
❌ Ignoring timezone in validity checks
Additional Notes
Future Enhancements (Not Yet Implemented)
Booking Cancellation:
Status: CANCELLED exists but not functionalFuture: Refund logic + ticket releaseFuture: Cancellation deadline rulesFuture: Partial cancellations
Ticket Transfers:
Currently: Tickets assigned at bookingFuture: Transfer ticket to another personFuture: Resale marketplaceFuture: Name change requests
Check-In API:
Currently: Manual check-ins not via APIFuture: Scanner app endpointsFuture: QR verification endpointFuture: Manual check-in endpoint
Booking Modifications:
Currently: Cannot modify after creationFuture: Add more ticketsFuture: Change attendee namesFuture: Upgrade ticket types
Integration Points
Payment System:
Booking created after payment successLinked via checkoutSessionIdPayment status determines creation
Notification System:
BookingCreatedEvent triggers emailsAsync processing via listenersMultiple notification channels
Escrow System:
Payments held until event completionBooking tracks payment via checkout sessionOrganizer paid after event
Scanner Apps:
Will consume booking dataVerify JWT QR codesRecordand check-insUpdate ticket status
Conclusion
The Event Booking Orders API provides comprehensive booking management with:
✅ Security: JWT-signed QR codes prevent fraud
✅ Flexibility: Multi-day events with per-day tracking
✅ Reliability: Event snapshots preserve booking details
✅ Scalability: Unique ticket series with DB counters
✅ User Experience: Clear references and notifications
✅ Multi-Attendee: Support for group bookings
✅ Virtual Events: Full online/hybrid event support
✅ Access Control: Proper permissions for all parties