Event Booking Orders API
Base URL: https://api.nexgate.com/api/v1
Short Description: The Event Booking Orders API provides comprehensive booking management functionality for confirmed event ticket purchases in the Nexgate platform. This API enables users to view their booking details including JWT-signed QR codes for secure event entry, track multi-day event check-ins, retrieve complete booking history, and access event snapshots at time of booking. The system supports both single-day and multi-day events with per-day check-in tracking, automated ticket series generation, and comprehensive booking references.
Hints:
- Auto-Creation: Bookings created automatically after successful checkout payment
- JWT Security: QR codes are RSA-signed JWTs containing ticket and event data
- Multi-Day Support: Full support for multi-day events with per-day check-in tracking
- Ticket Series: Auto-generated unique series (e.g., "VIP-0001", "GENER-0042")
- Event Snapshots: Event details captured at booking time (immutable)
- Booking Reference: Short readable codes (e.g., "EVT-2025-A3F4")
- Check-In Tracking: Complete history with location, staff, and day information
- Access Control: Customers see own bookings, organizers see event bookings, admins see all
- Notification Integration: Auto-emails tickets to buyer and optionally to attendees
- Virtual Events: Includes virtual meeting links for online/hybrid events
- Attendee Management: Track tickets for buyer and other attendees separately
Standard Response Format
All API responses follow a consistent structure using our Globe Response Builder pattern:
Success Response Structure
{
"success": true,
"httpStatus": "OK",
"message": "Operation completed successfully",
"action_time": "2025-12-11T10:30:45",
"data": {
// Actual response data goes here
}
}
Error Response Structure
{
"success": false,
"httpStatus": "BAD_REQUEST",
"message": "Error description",
"action_time": "2025-12-11T10:30:45",
"data": "Error description"
}
HTTP Method Badge Standards
- GET - GET - Green (Read operations)
BookingOrderResponse Structure
This is the comprehensive response structure returned by booking endpoints:
{
"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",
"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"
},
"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",
"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"
},
"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
}
Response Field Descriptions
Root Level Fields
| Field | Type | Description |
|---|---|---|
| bookingId | string (UUID) | Unique booking identifier |
| bookingReference | string | Short readable code (e.g., "EVT-A3F4B21C") |
| status | enum | Booking status: CONFIRMED, CANCELLED |
| event | object | Event snapshot at time of booking |
| organizer | object | Organizer details snapshot |
| customer | object | Customer (buyer) information |
| tickets | array | List of all booked tickets with check-in history |
| totalTickets | integer | Total number of tickets in booking |
| checkedInTicketsCount | integer | Count of tickets with at least one check-in |
| subtotal | decimal | Subtotal amount |
| total | decimal | Total amount paid |
| bookedAt | string (LocalDateTime) | When booking was created |
| cancelledAt | string (LocalDateTime) | When booking was cancelled (null if active) |
Event Snapshot Object
| Field | Type | Description |
|---|---|---|
| eventId | string (UUID) | Event identifier |
| title | string | Event title at time of booking |
| startDateTime | string (LocalDateTime) | Event start date/time |
| endDateTime | string (LocalDateTime) | Event end date/time |
| timezone | string | IANA timezone (e.g., "Africa/Nairobi") |
| location | string | Full venue location (name + address) |
| format | string | Event format: IN_PERSON, ONLINE, HYBRID |
| virtualDetails | object | Virtual meeting details (for ONLINE/HYBRID) |
Virtual Details Object
| Field | Type | Description |
|---|---|---|
| platform | string | Platform: ZOOM, GOOGLE_MEET, MS_TEAMS, CUSTOM |
| meetingUrl | string | Full meeting URL |
| meetingId | string | Meeting ID (optional) |
| passcode | string | Meeting passcode (optional) |
| additionalInstructions | string | Extra instructions (optional) |
Organizer Snapshot Object
| Field | Type | Description |
|---|---|---|
| name | string | Organizer name at time of booking |
| string | Organizer email | |
| phone | string | Organizer phone |
Customer Info Object
| Field | Type | Description |
|---|---|---|
| customerId | string (UUID) | Customer account ID |
| name | string | Customer username |
| string | Customer email |
Booked Ticket Response Object
| Field | Type | Description |
|---|---|---|
| ticketInstanceId | string (UUID) | Unique ticket instance ID |
| ticketTypeName | string | Ticket type name (e.g., "VIP Pass") |
| ticketSeries | string | Auto-generated series (e.g., "VIP-0001") |
| price | decimal | Ticket price |
| qrCode | string | JWT-signed QR code (very long string) |
| attendanceMode | string | IN_PERSON or ONLINE (for hybrid events) |
| attendee | object | Attendee information |
| buyer | object | Buyer information (who paid) |
| checkIns | array | Complete check-in history (all days) |
| hasBeenCheckedIn | boolean | True if checked in at least once |
| lastCheckedInAt | string (ZonedDateTime) | Most recent check-in time |
| lastCheckedInBy | string | Who performed last check-in |
| lastCheckInLocation | string | Location of last check-in |
| lastCheckInDayName | string | Day name of last check-in |
| status | enum | ACTIVE, USED, CANCELLED |
| validFrom | string (ZonedDateTime) | Ticket valid from |
| validUntil | string (ZonedDateTime) | Ticket valid until |
Attendee/Buyer Info Object
| Field | Type | Description |
|---|---|---|
| name | string | Person's name |
| string | Person's email | |
| phone | string | Person's phone (attendee only) |
Check-In Record Object
| Field | Type | Description |
|---|---|---|
| checkInTime | string (ZonedDateTime) | When check-in occurred |
| checkInLocation | string | Where (e.g., "Main Gate", "VIP Entrance") |
| checkedInBy | string | Staff/scanner operator name |
| dayName | string | Event day (e.g., "Day 1", "Day 2 - Saturday") |
| scannerId | string | Scanner device ID |
| checkInMethod | string | Method: QR_SCAN (default), MANUAL, NFC |
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:
| Header | Type | Required | Description |
|---|---|---|---|
| Authorization | string | Yes | Bearer token (format: Bearer <token>) |
Path Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
| bookingId | string (UUID) | Yes | Booking order ID |
Success Response: Returns complete BookingOrderResponse structure
Success Response Message: "Booking retrieved successfully"
HTTP Status Code: 200 OK
Access Rules:
- Customers: Can view their own bookings
- Organizers: Can view bookings for their events
- Admins (SUPER_ADMIN, STAFF_ADMIN): Can view any booking
Behavior:
- Returns complete booking details with all tickets
- Includes event snapshot (as it was at booking time)
- Shows full check-in history for multi-day events
- Includes JWT-signed QR codes for all tickets
- Virtual details included for ONLINE/HYBRID events
Use Cases:
- Customer viewing booking confirmation
- Customer retrieving QR codes for event entry
- Organizer checking booking details
- Admin reviewing booking for support
Standard Error Types:
Error Response Examples:
Booking Not Found (404):
{
"success": false,
"httpStatus": "NOT_FOUND",
"message": "Booking not found: 550e8400-e29b-41d4-a716-446655440000",
"action_time": "2025-12-11T10:30:45",
"data": "Booking not found: 550e8400-e29b-41d4-a716-446655440000"
}
Access Denied (403):
{
"success": false,
"httpStatus": "FORBIDDEN",
"message": "You don't have permission to view this booking",
"action_time": "2025-12-11T10:30:45",
"data": "You don't have permission to view this booking"
}
2. Get My Bookings
Purpose: Retrieve all bookings for the authenticated user
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: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"
}
]
}
HTTP Status Code: 200 OK
Sorting: Bookings sorted by bookedAt descending (newest first)
Behavior:
- Returns lightweight summary for all user's bookings
- Sorted newest first
- Includes check-in progress (checkedInTickets / totalTickets)
- Shows upcoming and past events
Use Cases:
- User viewing booking history
- Dashboard showing all purchases
- Finding booking for upcoming event
- Checking past event attendance
Standard Error Types:
Booking Status Lifecycle
Status Descriptions
| Status | Description | Can Cancel? | Tickets Valid? |
|---|---|---|---|
| CONFIRMED | Booking active and tickets valid | Future (not implemented) | Yes |
| CANCELLED | Booking cancelled (placeholder) | N/A | No |
Current Implementation:
- All bookings created as CONFIRMED
- Cancellation not yet implemented (status exists for future)
- Tickets remain ACTIVE after booking until checked in
Ticket Instance Status
Status Lifecycle
| Status | Description | Can Check In? |
|---|---|---|
| ACTIVE | Ticket ready for use | Yes |
| USED | Ticket checked in | Yes (for multi-day) |
| CANCELLED | Ticket cancelled | No |
Multi-Day Events:
- Ticket status remains ACTIVE even after first check-in
- Can check in multiple times (once per day)
- Status changes to USED after all expected check-ins
- Each check-in tracked separately in checkIns array
Ticket Series Generation
How Ticket Series Work
Format: {TICKET_CODE}-{COUNTER}
Examples:
- VIP Pass →
VIP-0001,VIP-0002,VIP-0003 - General Admission →
GENER-0001,GENER-0002 - Early Bird →
EARLY-0001,EARLY-0002
Ticket Code Extraction:
- Take first 5 characters of ticket type name
- Remove spaces
- Convert to uppercase
- Fallback to "TICK" if name empty
Counter System:
- Separate counter per ticket type (stored in DB)
- Pessimistic locking prevents duplicates
- Counter never resets (unique forever)
- Increments with each ticket created
Example Flow:
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 staff
- Quick ticket identification
- Helps track capacity sold
- Professional appearance on PDF tickets
JWT-Signed QR Codes
Security Architecture
What is the QR Code?
- Not a random string
- Full JWT (JSON Web Token)
- Signed with event's RSA private key
- Contains complete ticket and event data
- Cannot be 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 of days for multi-day events)
- validFrom (timestamp)
- validUntil (timestamp)
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:
- Generated 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 key
Verification Process (Scanner App):
- Scanner reads QR code
- Extracts JWT token
- Fetches event's public key
- Verifies JWT signature
- Checks expiry and validity
- Checks if already checked in for this day
- Records check-in if valid
Why JWT over Simple Codes?
- Cannot be forged: Requires private key
- Contains all data: No database lookup needed
- Offline capable: Scanner can verify without internet
- Tamper-proof: Any modification breaks signature
- Time-limited: validFrom/validUntil built in
- Day tracking: Multi-day schedules embedded
Multi-Day Event Check-Ins
How Multi-Day Check-Ins Work
Single-Day Event:
- One check-in expected
- After check-in, status changes to USED
- Simple entry tracking
Multi-Day Event (e.g., 3-day festival):
- Multiple check-ins allowed (one per day)
- Each day tracked separately
- Ticket remains ACTIVE after first day
- Full history stored in
checkInsarray
Check-In Record Fields:
- checkInTime: When check-in happened
- checkInLocation: Where (e.g., "Main Gate", "VIP Entrance")
- checkedInBy: Staff/scanner operator name
- dayName: Which day (must match eventSchedules)
- scannerId: Scanner device ID
- checkInMethod: QR_SCAN, MANUAL, NFC (default: QR_SCAN)
Example: 3-Day Festival
Day 1 (Friday):
{
"checkInTime": "2025-12-15T18:30:00+03:00",
"checkInLocation": "Main Gate",
"checkedInBy": "Staff Member 1",
"dayName": "Day 1 - Friday 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:
dayNamemust match one from JWT's eventSchedules- Cannot check in twice on same day
- Can check in on different days
- Check-in time must be within day's start/end
Benefits:
- Accurate attendance tracking per day
- Prevents duplicate same-day entries
- Allows organizers to see daily attendance
- Supports flexible entry (don't need all days)
Event Snapshots
Why Snapshots?
Problem: Event details can change after booking
- Event renamed
- Time changed
- Venue moved
- Organizer updated contact
Solution: Capture event state at booking time
Snapshot Fields (Immutable after booking):
- eventTitle
- eventStartDateTime
- eventEndDateTime
- eventTimezone
- eventLocation (full venue details)
- eventFormat
- virtualDetails (for online/hybrid)
- organizerName
- organizerEmail
- organizerPhone
Benefits:
- PDF Tickets: Generate correct ticket with original details
- Legal Record: What customer actually purchased
- Email Confirmations: Show accurate booking details
- Refund Logic: Reference original event details
- Audit Trail: Track what changed vs. what was booked
Example Scenario:
- User books ticket for "Tech Summit" at KICC
- Snapshot 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 Creation After Payment
Trigger: EventPaymentCompletedListener fires after successful payment
Steps:
-
Fetch Entities:
- Get EventCheckoutSession
- Get Event details
- Get TicketType details
-
Generate Booking Reference:
- Format:
EVT-{8-char-UUID} - Example:
EVT-A3F4B21C - Unique and readable
- Format:
-
Create Ticket Instances:
- For buyer's tickets (ticketsForMe count)
- For each other attendee (their quantity)
- Each ticket gets unique series number
- Each ticket assigned to correct attendee
-
Generate JWT QR Codes:
- Create JWT payload with ticket data
- Include event schedules for multi-day
- Sign with event's RSA private key
- Set as ticket's qrCode field
-
Snapshot Event Details:
- Capture current event title, times, location
- Capture organizer details
- Store in booking (immutable)
-
Save Booking:
- Create EventBookingOrderEntity
- Save to database with all tickets
- Link to checkout session
-
Update Checkout Session:
- Set createdBookingOrderId
- Mark as COMPLETED
-
Publish Event:
- Fire BookingCreatedEvent
- Triggers notifications
-
Send Notifications:
- Email buyer confirmation with tickets
- Optionally email other attendees their tickets
- Notify event organizer of new booking
Transaction Safety:
- Entire flow in single transaction
- If any step fails, everything rolls back
- No partial bookings created
Timing:
- Happens immediately after payment success
- Usually completes in < 2 seconds
- User gets instant confirmation
Notification System Integration
Who Gets Notified?
1. Buyer (ALWAYS):
- Receives booking confirmation
- Gets ALL tickets (if sendTicketsToAttendees = false)
- Gets only THEIR tickets (if sendTicketsToAttendees = true)
- Channels: EMAIL, SMS, PUSH, IN_APP
- Priority: HIGH
2. Other Attendees (CONDITIONAL):
- Only if sendTicketsToAttendees = true
- Each attendee gets ONLY their tickets
- Channels: EMAIL, SMS
- Priority: HIGH
- Grouped by email (all tickets for same person)
3. Event Organizer (ALWAYS):
- Notified of new booking
- Gets booking summary (not tickets)
- Channels: EMAIL, IN_APP
- Priority: NORMAL
Notification Content
Buyer Confirmation:
- Booking reference
- Event title and date
- Number of tickets
- QR codes attached
- Event location/virtual link
- Instructions for entry
Attendee Ticket Email:
- "You've received tickets from [Buyer Name]"
- Event details
- Their specific QR codes
- Entry instructions
Organizer Alert:
- New booking notification
- Buyer name and email
- Number of tickets sold
- Total amount
- Booking 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 Can View Bookings?
1. Booking Owner (Customer):
- Can view all their own bookings
- GET /booking-orders/my-bookings (all)
- GET /booking-orders/{bookingId} (specific)
2. Event Organizer:
- Can view bookings for their events
- Useful for checking attendance
- Verifying purchases
- Customer support
3. Platform Admins:
- Roles: SUPER_ADMIN, STAFF_ADMIN
- Can view any booking
- For support and dispute resolution
4. Others:
- Cannot view bookings they don't own
- Cannot view other users' bookings
- 403 Forbidden error
Virtual Event Support
Virtual Details Structure
Included for:
- ONLINE events (only virtual)
- HYBRID events (both physical + virtual)
Fields:
- platform: ZOOM, GOOGLE_MEET, MS_TEAMS, CUSTOM
- meetingUrl: Full meeting URL (required)
- meetingId: Meeting ID if applicable
- passcode: Meeting passcode if required
- additionalInstructions: 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 early for a smooth start"
}
Example (Custom):
{
"platform": "CUSTOM",
"meetingUrl": "https://customplatform.com/event/12345",
"meetingId": null,
"passcode": null,
"additionalInstructions": "Use your booking email to log in"
}
Delivery:
- Included in booking response
- Sent in confirmation email
- Sent in ticket emails
- Available before event starts
Hybrid Events:
- Virtual details + physical venue
- Attendees choose format via ticket type
- IN_PERSON tickets: Physical entry
- ONLINE tickets: Virtual access
- Both in same booking possible
Date/Time Formats
DateTime Standards
LocalDateTime (No timezone):
- Format:
YYYY-MM-DDTHH:mm:ss - Example:
2025-12-15T09:00:00 - Used for: bookedAt, cancelledAt, event snapshots
ZonedDateTime (With timezone):
- Format:
YYYY-MM-DDTHH:mm:ss±HH:mm - Example:
2025-12-15T09:00:00+03:00 - Used for: validFrom, validUntil, checkInTime
Why Different Formats?
LocalDateTime:
- Server-local time
- Audit timestamps
- No conversion needed
- Simple comparison
ZonedDateTime:
- User-aware time
- Ticket validity
- Check-in times
- Handles timezone correctly
Example:
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
Common HTTP Status Codes
200 OK: Successful request401 Unauthorized: Authentication required/failed403 Forbidden: User doesn't have permission404 Not Found: Booking not found
Booking Reference Format
- Pattern:
EVT-{8-char-UUID} - Examples:
EVT-A3F4B21C,EVT-F2D5E891 - Unique and readable
- Easy for customer service
Ticket Series Format
- Pattern:
{CODE}-{COUNTER} - Examples:
VIP-0001,GENER-0042,EARLY-0123 - Code: First 5 chars of ticket name
- Counter: Unique incrementing number
QR Code Format
- Type: RSA-signed JWT
- Length: ~1000-2000 characters
- Contains: Complete ticket + event data
- Cannot be: Forged, tampered, reused (tracked)
Data Format Standards
- Dates: LocalDateTime for timestamps, ZonedDateTime for validity
- IDs: UUID format
- Money: Decimal with 2 decimals (150.00)
- Currency: 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 immediately
- Screenshot confirmation email
- Arrive early for check-in
- One QR per person at gate
For Developers:
- Always show booking reference prominently
- Display QR codes large enough to scan
- Cache booking details locally
- Handle offline QR display
- Show check-in history clearly
- Support PDF ticket generation
For Organizers:
- Provide clear entry instructions
- Test scanner apps before event
- Have backup manual verification
- Track attendance by day
- Monitor check-in progress live
Error Handling Tips
- 403 Forbidden: Show "This booking belongs to another user"
- 404 Not Found: Suggest "Check your email for correct reference"
- Network Error: Show cached booking if available
- QR 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 functional
- Future: Refund logic + ticket release
- Future: Cancellation deadline rules
- Future: Partial cancellations
Ticket Transfers:
- Currently: Tickets assigned at booking
- Future: Transfer ticket to another person
- Future: Resale marketplace
- Future: Name change requests
Check-In API:
- Currently: Manual check-ins not via API
- Future: Scanner app endpoints
- Future: QR verification endpoint
- Future: Manual check-in endpoint
Booking Modifications:
- Currently: Cannot modify after creation
- Future: Add more tickets
- Future: Change attendee names
- Future: Upgrade ticket types
Integration Points
Payment System:
- Booking created after payment success
- Linked via checkoutSessionId
- Payment status determines creation
Notification System:
- BookingCreatedEvent triggers emails
- Async processing via listeners
- Multiple notification channels
Escrow System:
- Payments held until event completion
- Booking tracks payment via checkout session
- Organizer paid after event
Scanner Apps:
- Will consume booking data
- Verify JWT QR codes
- Record check-ins
- Update 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
No comments to display
No comments to display