Skip to main content

Attendance Analytics API

Attendance Management API Documentation

Author: Josh,Josh S. Sakweli, Backend Lead Backend Team Last Updated: 2026-05-23 Version: v2.0

Base URL: https://api.nexgate.com/api/v1

Short Description: The Attendance Management API provides comprehensive attendee tracking and analytics for event organizers. Organizers can view overall attendance statistics with per-day and per-ticket-type breakdowns, browse a unified attendance list (present, absent, and partially attended in one view) with rich filtering, and drill into the full check-in history for any individual ticket. The system supports both single-day and multi-day events. All status filtering, day filtering, buyer-type filtering, and search are handled through query parameters on a single list endpoint.

Hints:

  • Organizer Only: All endpoints restricted to the event organizer
  • Unified List: Present and absent tickets are in one list — filter withuse ?status=PRESENT|ABSENT|PARTIALLY_ATTENDED to filter
  • Ticket-Based Rows: Each row in the list is one ticket, not one person — one buyer can appear multiple times
  • Attendee Number: EachUnique human-readable ticket has a unique attendeeNumberID = bookingReference + "-" + ticketSeries (e.g. EVT-A3F4B21C-VIP-0042)
  • Buyer vs Attendee: buyer = who paid, attendee = who holds the ticket — can be different people
  • Buyer Types: SYSTEM_USER = boughtonline online,purchase with account, AT_DOOR = walk-in name captured at dooronly (no account)
  • Multi-Day SupportFiltering: Per-day filtering via ?dayNumber=1dayNumber is optional  optional, omit for overall viewview, provide for per-day status
  • Partial Attendance: PARTIALLY_ATTENDED =only checkedapplies inwhen someno daysdayNumber butfilter notis allset (on multi-day events only)
  • Pagination: Default 20 per page

Standard Response StructuresFormat

All API responses follow a consistent structure using our Globe Response Builder pattern:

AttendanceSummaryResponseSuccess Response Structure

Returned

{
  by"success": true,
  "httpStatus": "OK",
  "message": "Operation completed successfully",
  "action_time": "2026-05-23T10:30:45",
  "data": {
  }
}

Error Response Structure

{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Error description",
  "action_time": "2026-05-23T10:30:45",
  "data": "Error description"
}

Standard Response Fields

FieldTypeDescription
successbooleantrue for successful operations, false for errors
httpStatusstringHTTP status name (OK, NOT_FOUND, FORBIDDEN, etc.)
messagestringHuman-readable message describing the result
action_timestringISO 8601 timestamp of when the response was generated
dataobjectResponse payload for success, error string for failures

HTTP Method Badge Standards

  • GET /summary.- GET - Green (Safe, 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)

Endpoints

1. Get Attendance Summary

Purpose: Returns overall attendance statistics for an event including per-day and per-ticket-type breakdowns. Always covers the full event — no filtering.filtering applied.

Endpoint: GET /e-events/attendance/{eventId}/summary

Access Level: 🔒 Protected — Event Organizer Only

Authentication: Bearer Token

Request Headers:

HeaderTypeRequiredDescription
AuthorizationstringYesBearer <token>

Path Parameters:

ParameterTypeRequiredDescriptionValidation
eventIdUUIDYesEvent identifierMust be a valid UUID of an existing event owned by the authenticated organizer

Success Response JSON Sample:

{
  "success": true,
  "httpStatus": "OK",
  "message": "Attendance summary retrieved",
  "action_time": "2026-05-23T10:30:45",
  "data": {
    "eventId": "uuid"3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "eventTitle": "East African Tech Summit 2026",
    "totalDays": 3,
    "eventSchedule": [
      { "dayNumber": 1, "dayName": "Day 1 - Opening",    "date": "2026-12-15" },
      { "dayNumber": 2, "dayName": "Day 2 - Conference", "date": "2026-12-16" },
      { "dayNumber": 3, "dayName": "Day 3 - Closing",    "date": "2026-12-17" }
    ],
    "overallStats": {
      "totalTickets": 500,
      "present": 310,
      "partiallyAttended": 45,
      "absent": 145,
      "attendanceRate": 71.0,
      "byDay": [
        {
          "dayNumber": 1,
          "dayName": "Day 1 - Opening",
          "date": "2026-12-15",
          "totalTickets": 500,
          "checkedIn": 400,
          "absent": 100,
          "attendanceRate": 80.0,
          "status": "COMPLETED"
        },
        {
          "dayNumber": 2,
          "dayName": "Day 2 - Conference",
          "date": "2026-12-16",
          "totalTickets": 500,
          "checkedIn": 355,
          "absent": 145,
          "attendanceRate": 71.0,
          "status": "ONGOING"
        },
        {
          "dayNumber": 3,
          "dayName": "Day 3 - Closing",
          "date": "2026-12-17",
          "totalTickets": 500,
          "checkedIn": 0,
          "absent": 500,
          "attendanceRate": 0.0,
          "status": "UPCOMING"
        }
      ]
    },
    "byTicketType": [
      {
        "ticketTypeId": "uuid"3fa85f64-5717-4562-b3fc-2c963f66afa7",
        "ticketTypeName": "VIP Pass",
        "totalSold": 100,
        "present": 75,
        "partiallyAttended": 10,
        "absent": 15,
        "attendanceRate": 85.0,
        "byDay": [
          {
            "dayNumber": 1,
            "dayName": "Day 1 - Opening",
            "checkedIn": 90,
            "absent": 10,
            "attendanceRate": 90.0
          }
        ]
      }
    ]
  }
}

Success

AttendanceListResponse

Response

ReturnedFields:

by /list Reflectsfilters. alwaysscope. onlysetona
FieldDescription
GETdata.eventId Event UUID
data.eventTitle. Event appliedname
summarydata.totalDays Number coversof theevent filtereddays
partiallyAttendeddata.eventSchedule List appearsof whendays nowith dayNumber, filterdayName, isdate
data.overallStats.totalTickets Total tickets sold
data.overallStats.presentTickets checked in for all days
data.overallStats.partiallyAttendedTickets checked in for some days (multi-day event.only)
data.overallStats.absentTickets never checked in
data.overallStats.attendanceRate((present + partiallyAttended) / total) × 100, 2 dp
data.overallStats.byDay[].statusCOMPLETED, ONGOING, or UPCOMING
data.byTicketTypeSame stats broken down per ticket type

Error Response JSON Sample:

{
  "success": false,
  "httpStatus": "NOT_FOUND",
  "message": "Event not found",
  "action_time": "2026-05-23T10:30:45",
  "data": "Event not found"
}

Standard Error Types:

  • 401 UNAUTHORIZED: Missing or invalid token
  • 403 FORBIDDEN: Authenticated user is not the event organizer
  • 404 NOT_FOUND: Event does not exist

2. Get Attendance List

Purpose: Returns a paginated, filterable list of all tickets for an event. Each row is one ticket. Present and absent tickets are unified in one list — use the status filter to narrow down.

Endpoint: GET /e-events/attendance/{eventId}/list

Access Level: 🔒 Protected — Event Organizer Only

Authentication: Bearer Token

Request Headers:

HeaderTypeRequiredDescription
AuthorizationstringYesBearer <token>

Path Parameters:

ParameterTypeRequiredDescriptionValidation
eventIdUUIDYesEvent identifierMust be a valid UUID of an existing event owned by the authenticated organizer

Query Parameters:

ParameterTypeRequiredDescriptionValidationDefault
statusenumNoFilter by attendance statusPRESENT, ABSENT, PARTIALLY_ATTENDEDAll
dayNumberintegerNoFilter for a specific event dayMust be between 1 and totalDaysNone (overall)
ticketTypeIdUUIDNoFilter by ticket typeMust belong to this eventAll types
buyerTypeenumNoFilter by how the ticket was purchasedSYSTEM_USER, AT_DOORAll
searchstringNoSearch by attendee name, email, or phonePartial match, case-insensitiveNone
pageintegerNoPage number (0-indexed)Min: 00
sizeintegerNoItems per pageMin: 120

Success Response JSON Sample:

{
  "success": true,
  "httpStatus": "OK",
  "message": "Attendance list retrieved",
  "action_time": "2026-05-23T10:30:45",
  "data": {
    "eventId": "uuid"3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "eventTitle": "East African Tech Summit 2026",
    "appliedFilters": {
      "ticketTypeId": "uuid"3fa85f64-5717-4562-b3fc-2c963f66afa7",
      "ticketTypeName": "VIP Pass",
      "status": "ALL",
      "dayNumber": 1,
      "dayName": "Day 1 - Opening",
      "buyerType": "ALL",
      "search": null
    },
    "summary": {
      "totalTickets": 100,
      "present": 90,
      "absent": 10,
      "partiallyAttended": null,
      "attendanceRate": 90.0
    },
    "attendees": [
      {
        "ticketInstanceId": "uuid"3fa85f64-5717-4562-b3fc-2c963f66afa8",
        "attendeeNumber": "EVT-A3F4B21C-VIP-0042",
        "ticketTypeId": "uuid"3fa85f64-5717-4562-b3fc-2c963f66afa7",
        "ticketType": "VIP Pass",
        "ticketSeries": "VIP-0042",
        "attendeeName": "John Doe",
        "attendeeEmail": "john@example.com",
        "attendeePhone": "+255712345678",
        "buyer": {
          "buyerName": "Jane Doe",
          "buyerEmail": "jane@example.com",
          "buyerId": "uuid"3fa85f64-5717-4562-b3fc-2c963f66afa9",
          "buyerType": "SYSTEM_USER"
        },
        "bookingReference": "EVT-A3F4B21C",
        "pricePaid": 150.00,
        "attendanceStatus": "PRESENT",
        "checkInTime": "2026-12-15T09:15:00+03:00",
        "checkInLocation": "Main Gate",
        "checkedInBy": "Scanner Operator 1",
        "scannerId": "uuid-string"3fa85f64-5717-4562-b3fc-2c963f66afb0"
      },
      {
        "ticketInstanceId": "uuid-2"3fa85f64-5717-4562-b3fc-2c963f66afb1",
        "attendeeNumber": "EVT-B5D2E12F-GENE-0101",
        "ticketType": "General Admission",
        "ticketSeries": "GENE-0101",
        "attendeeName": "Ali Hassan",
        "attendeeEmail": null,
        "attendeePhone": null,
        "buyer": {
          "buyerName": "Ali Hassan",
          "buyerEmail": null,
          "buyerId": null,
          "buyerType": "AT_DOOR"
        },
        "bookingReference": "EVT-B5D2E12F",
        "pricePaid": 50.00,
        "attendanceStatus": "ABSENT",
        "checkInTime": null,
        "checkInLocation": null,
        "checkedInBy": null,
        "scannerId": null
      }
    ],
    "pagination": {
      "currentPage": 0,
      "pageSize": 20,
      "totalPages": 5,
      "totalElements": 100,
      "hasNext": true,
      "hasPrevious": false
    }
  }
}

Success

AttendeeDetailResponse

Response

ReturnedFields:

FieldDescription
data.appliedFiltersEcho of all active filters so the client knows what was applied
data.summary.totalTicketsTotal tickets matching the ticket type filter (unaffected by status/search)
GETdata.summary.presentTickets with PRESENT status under current filters
data.summary.absentTickets with ABSENT status under current filters
data.summary.partiallyAttendednull when dayNumber is active; count when viewing overall multi-day
data.summary.attendanceRate((present + partiallyAttended) /attendees/ total) × 100, 2 dp
data.attendees[].attendeeNumberUnique readable ID = bookingReference + "-" + ticketSeries
data.attendees[].attendanceStatusPRESENT, ABSENT, or PARTIALLY_ATTENDED
data.attendees[].buyer.buyerTypeSYSTEM_USER (online) or AT_DOOR (walk-in)
data.attendees[].buyer.buyerIdPopulated for SYSTEM_USER, null for AT_DOOR
data.attendees[].checkInTimenull when attendanceStatus is ABSENT
data.paginationStandard pagination envelope

Notes:

  • When dayNumber is provided: attendanceStatus is PRESENT or ABSENT for that specific day only
  • When dayNumber is omitted: attendanceStatus reflects the overall across all days (PARTIALLY_ATTENDED possible)
  • summary reflects the current filter scope, not the whole event
  • partiallyAttended in summary is null when a dayNumber filter is active

Error Response JSON Sample:

{ticketInstanceId}
  "success": false,
  "httpStatus": "NOT_FOUND",
  "message": "Invalid day 5. Valid: 1-3",
  "action_time": "2026-05-23T10:30:45",
  "data": "Invalid day 5. Valid: 1-3"
}
.
Full

Standard per-Error Types:

  • 401 UNAUTHORIZED: Missing or invalid token
  • 403 FORBIDDEN: Authenticated user is not the event organizer
  • 404 NOT_FOUND: Event not found / day number out of range / ticket type not found

3. Get Attendee Detail

Purpose: Returns the full check-in history for onea ticket.single ticket across all event days. Shows per-day status including upcoming days.

Endpoint: GET /e-events/attendance/{eventId}/attendees/{ticketInstanceId}

Access Level: 🔒 Protected — Event Organizer Only

Authentication: Bearer Token

Request Headers:

HeaderTypeRequiredDescription
AuthorizationstringYesBearer <token>

Path Parameters:

ParameterTypeRequiredDescriptionValidation
eventIdUUIDYesEvent identifierMust be owned by the authenticated organizer
ticketInstanceIdUUIDYesIndividual ticket instance identifierMust belong to a booking under this event

Success Response JSON Sample:

{
  "success": true,
  "httpStatus": "OK",
  "message": "Attendee detail retrieved",
  "action_time": "2026-05-23T10:30:45",
  "data": {
    "ticketInstanceId": "uuid"3fa85f64-5717-4562-b3fc-2c963f66afa8",
    "attendeeNumber": "EVT-A3F4B21C-VIP-0042",
    "attendeeName": "John Doe",
    "attendeeEmail": "john@example.com",
    "attendeePhone": "+255712345678",
    "ticketType": "VIP Pass",
    "ticketSeries": "VIP-0042",
    "bookingReference": "EVT-A3F4B21C",
    "pricePaid": 150.00,
    "overallStatus": "PARTIALLY_ATTENDED",
    "daysAttended": 2,
    "daysTotal": 3,
    "checkInsByDay": [
      {
        "dayNumber": 1,
        "dayName": "Day 1 - Opening",
        "dayDate": "2026-12-15",
        "status": "CHECKED_IN",
        "checkInTime": "2026-12-15T09:15:00+03:00",
        "checkInLocation": "Main Gate",
        "checkedInBy": "Scanner Operator 1",
        "scannerId": "uuid-string"3fa85f64-5717-4562-b3fc-2c963f66afb0"
      },
      {
        "dayNumber": 2,
        "dayName": "Day 2 - Conference",
        "dayDate": "2026-12-16",
        "status": "NOT_CHECKED_IN",
        "checkInTime": null,
        "checkInLocation": null,
        "checkedInBy": null,
        "scannerId": null
      },
      {
        "dayNumber": 3,
        "dayName": "Day 3 - Closing",
        "dayDate": "2026-12-17",
        "status": "UPCOMING",
        "checkInTime": null,
        "checkInLocation": null,
        "checkedInBy": null,
        "scannerId": null
      }
    ]
  }
}

Endpoints

1. Get Attendance Summary

Endpoint:Success GETResponse /attendance/{eventId}/summary

Access: 🔒 Event Organizer Only

Path ParametersFields:

readable
ParameterTypeRequiredField Description
eventIddata.attendeeNumber stringUnique (UUID) YesEvent identifier

Success Response: Returns AttendanceSummaryResponse

Success Response Message: "Attendance summary retrieved"

Behavior:

  • Validates organizer owns the event
  • Returns overall attendance metrics across all tickets
  • Breaks down by day (all events, including single-day)
  • Breaks down by ticket type with per-day stats
  • present = checked in for all days, partiallyAttended = some days, absent = none
  • attendanceRate includes both present and partiallyAttended tickets

Errors:

  • 403 FORBIDDEN: Not event organizer
  • 404 NOT_FOUND: Event not found

2. Get Attendance List

Endpoint: GET /attendance/{eventId}/list

Access: 🔒 Event Organizer Only

Path Parameters:

ParameterTypeRequiredDescription
eventIdstring (UUID)YesEvent identifier

Query Parameters:

ParameterTypeRequiredDescription
statusenumNoPRESENT, ABSENT, PARTIALLY_ATTENDED — omit for all
dayNumberintegerNoFilter for a specific day (1, 2, 3...) — omit for overall view
ticketTypeIdstring (UUID)NoFilter by ticket type
buyerTypeenumNoSYSTEM_USER, AT_DOOR — omit for all
searchstringNoSearch by attendee name, email, or phone (partial, case-insensitive)
pageintegerNoPage number (0-indexed, default: 0)
sizeintegerNoItems per page (default: 20)

Success Response: Returns AttendanceListResponse with pagination

Success Response Message: "Attendance list retrieved"

Behavior:

  • Returns one row per ticket (not per person)
  • One buyer can appear multiple times if they purchased multiple tickets
  • attendeeNumberID = bookingReference + "-" + ticketSeries (unique per ticket)
  • data.overallStatus
  • When dayNumber is set: attendanceStatus is PRESENT or(all days), PARTIALLY_ATTENDED (some), ABSENT for(none) that specific day
  • data.daysAttended
  • WhenCount dayNumber is omitted: attendanceStatus reflects overall across allof days (PARTIALLY_ATTENDEDthis possible)
  • ticket
  • summarywas block always reflects the current filter scope (not the full event)
  • partiallyAttendedchecked in summary is data.daysTotal Total number of event days data.checkInsByDay[].status CHECKED_IN, NOT_CHECKED_IN, or UPCOMING data.checkInsByDay[].checkInTime null when anot dayNumberchecked filterin or day is active
  • upcoming
data.checkInsByDay[].checkedInBy Name of the scanner operator who processed the check-in

ValidationError Response JSON Sample:

{
  "success": false,
  "httpStatus": "NOT_FOUND",
  "message": "Ticket not found",
  "action_time": "2026-05-23T10:30:45",
  "data": "Ticket not found"
}

Standard Error Types:

  • dayNumber401 UNAUTHORIZED: mustMissing beor withininvalid valid range for the event (1 to totalDays)token
  • ticketTypeId must belong to the event

Errors:

  • 403 FORBIDDEN: NotAuthenticated user is not the event organizer
  • 404 NOT_FOUND: Event not found / invalid day number /or ticket typeinstance does not foundbelong to this event

3.Quick GetReference Attendee DetailGuide

Endpoint: GET /attendance/{eventId}/attendees/{ticketInstanceId}

Access: 🔒 Event Organizer Only

Path Parameters:

ParameterTypeRequiredDescription
eventIdstring (UUID)YesEvent identifier
ticketInstanceIdstring (UUID)YesIndividual ticket instance identifier

Success Response: Returns AttendeeDetailResponse

Success Response Message: "Attendee detail retrieved"

Behavior:

  • Returns the full check-in history for one specific ticket across all event days
  • overallStatus computed from all days
  • checkInsByDay lists every event day with its status and check-in details
  • Day status reflects whether the day has passed, is today, or is upcoming

Errors:

  • 403 FORBIDDEN: Not event organizer
  • 404 NOT_FOUND: Event or ticket not found

Key Concepts

Attendee Number

Every ticket has a unique attendeeNumber formatted as:

{bookingReference}-{ticketSeries}

Example: EVT-A3F4B21C-VIP-0042

Use this as the primary human-readable identifier for a ticket on the attendance list — safer than ticketInstanceId (UUID) for display and manual lookup.


Buyer vs Attendee

These are two different people on the same ticket:

FieldWho
attendeeName / attendeeEmailThe person who will attend (holds the ticket)
buyer.buyerName / buyer.buyerEmailThe person who paid

Example: Jane buys 3 VIP tickets. She is the buyer on all 3. The attendees are John, Ali, and herself.

One buyer can appear multiple times in the list — once per ticket they purchased.


Buyer Types

ValueMeaning
SYSTEM_USERBought online — has a platform account, buyerId is populated
AT_DOORWalk-in sale — only name captured at door, buyerEmail and buyerId are null

Attendance Status Values

Value Meaning WhenAvailable when
PRESENT Checked in (all days if no day filter) With dayNumber: checked in that day. Without: checked in all daysAlways
ABSENT NotNever checked in With dayNumber: not that day. Without: never checked inAlways
PARTIALLY_ATTENDED Checked in some days but not all Only possible withoutNo dayNumber filter, on multi-day events only

Day Status (in detail view)Values

Value Meaning
CHECKED_IN ScannedTicket was scanned on that day
NOT_CHECKED_IN Day has passed, wasticket not scanned
UPCOMING Day has not happenedstarted yet

Buyer

AttendanceType Rate Calculation

attendanceRate = ((present + partiallyAttended) / totalTickets) × 100

Rounded to 2 decimal places.

Per-day rate (in summary byDay):

rate = (checkedIn for that day / totalTickets) × 100

Multi-Day Event Logic

Day Filter BehaviourValues

scope=did=overall
ValuedayNumberbuyerEmail StatusbuyerId Meaning
Provided (e.g. ?dayNumber=2SYSTEM_USER) StatusPopulated Populated Bought thisonline, tickethas checkplatform in on Day 2? (PRESENT or ABSENT only)account
OmittedAT_DOOR Statusnull null Walk-in acrosssale, allname daysonly (PRESENT,captured ABSENT,at or PARTIALLY_ATTENDED)door

Day

Common number validation:

Event has 3 days → dayNumber must be 1, 2, or 3
Request dayNumber=5 → 404: "Invalid day 5. Valid: 1-3"

Single-day events: dayNumber is always optional. Omitting it gives the same result as ?dayNumber=1.


Use Cases

Dashboard Overview

GET /attendance/{eventId}/summary

Use: top-of-page stats card
Shows: overall rate, per-day breakdown, per-ticket-type breakdown

Full Attendance Table

GET /attendance/{eventId}/list

Use: default organizer table view — all tickets, all statuses

Who Attended Today (Day 2)

GET /attendance/{eventId}/list?dayNumber=2&status=PRESENT

Shows: tickets checked in on Day 2

Who Missed Today

GET /attendance/{eventId}/list?dayNumber=2&status=ABSENT

Shows: tickets that didn't check in on Day 2 (regardless of other days)

At-Door Walk-Ins Only

GET /attendance/{eventId}/list?buyerType=AT_DOOR

Shows: all tickets sold at the door

Partially Attended (Multi-Day)

GET /attendance/{eventId}/list?status=PARTIALLY_ATTENDED

Shows: tickets that attended some days but not all
Requires: no dayNumber filter

Search by Name

GET /attendance/{eventId}/list?search=john

Matches: attendee name, email, or phone containing "john"

Individual Ticket History

GET /attendance/{eventId}/attendees/{ticketInstanceId}

Shows: full per-day check-in record for one ticket

Quick Reference

HTTP Status Codes

  • 200 OK: SuccessSuccessful request
  • 401 UNAUTHORIZED: NotAuthentication authenticatedrequired or token invalid
  • 403 FORBIDDEN: Not the event organizer
  • 404 NOT_FOUND: EventEvent, /ticket, ticket /or day number not found

Attendance Status Values

  • PRESENT500 INTERNAL_SERVER_ERROR: Unexpected attendedserver (all days if no day filter)
  • ABSENT — did not attend
  • PARTIALLY_ATTENDED — attended some days (multi-day, no day filter)error

Day Status Values

  • COMPLETED — day has passed
  • ONGOING — day is today
  • UPCOMING — day has not started

Buyer Type Values

  • SYSTEM_USER — online purchase, has platform account
  • AT_DOOR — walk-in, name only, no account

Default Pagination

  • page: 0 (first page)
  • size: 20 items per page