# Attendance Analytics API

**Author**: Josh, Lead Backend Team  
**Last Updated**: 2025-12-11  
**Version**: v1.0

**Base URL**: `https://api.nexgate.com/api/v1`

**Short Description**: The Attendance Analytics API provides comprehensive attendee tracking and analytics for event organizers. This API enables organizers to view attendance statistics with per-day breakdowns, list all checked-in attendees with filters, track absentees by category (full no-shows vs specific-day absences), and view detailed check-in history for individual attendees. The system supports multi-day events with separate tracking per day and provides real-time attendance metrics.

**Hints**: 
- **Organizer Only**: All endpoints restricted to event organizers
- **Multi-Day Support**: Per-day statistics and filtering
- **Real-Time**: Updates instantly after check-ins
- **Absentee Categories**: FULL_NO_SHOW, SPECIFIC_DAY_ONLY, ALL
- **Ticket Type Filter**: Filter by specific ticket types
- **Search**: Search attendees by name/email
- **Pagination**: Lists paginated (default 50 per page)
- **Check-In History**: Complete per-day check-in records
- **Attendance Patterns**: Tracks which days attended/absent

---

## Response Structures

### AttendanceStatsResponse
```json
{
  "eventId": "uuid",
  "eventTitle": "East African Tech Summit 2025",
  "totalDays": 3,
  "eventSchedule": [
    {"dayNumber": 1, "dayName": "Day 1 - Opening", "date": "2025-12-15"},
    {"dayNumber": 2, "dayName": "Day 2 - Conference", "date": "2025-12-16"},
    {"dayNumber": 3, "dayName": "Day 3 - Closing", "date": "2025-12-17"}
  ],
  "overallStats": {
    "totalTickets": 500,
    "totalCheckedIn": 462,
    "totalAbsent": 38,
    "attendanceRate": 92.4,
    "byDay": [
      {
        "dayNumber": 1,
        "dayName": "Day 1 - Opening",
        "date": "2025-12-15",
        "totalTickets": 500,
        "checkedIn": 475,
        "absent": 25,
        "attendanceRate": 95.0,
        "status": "COMPLETED"
      }
    ]
  },
  "byTicketType": [
    {
      "ticketTypeId": "uuid",
      "ticketTypeName": "VIP Pass",
      "totalSold": 100,
      "totalCheckedIn": 98,
      "totalAbsent": 2,
      "attendanceRate": 98.0,
      "byDay": [
        {
          "dayNumber": 1,
          "dayName": "Day 1 - Opening",
          "checkedIn": 99,
          "absent": 1,
          "attendanceRate": 99.0
        }
      ]
    }
  ]
}
```

### AttendeeListResponse
```json
{
  "eventId": "uuid",
  "eventTitle": "East African Tech Summit 2025",
  "dayNumber": 1,
  "dayName": "Day 1 - Opening",
  "dayDate": "2025-12-15",
  "ticketTypeId": "uuid",
  "ticketTypeName": "VIP Pass",
  "summary": {
    "totalTicketsForType": 100,
    "checkedInThisDay": 99
  },
  "attendees": [
    {
      "ticketInstanceId": "uuid",
      "attendeeName": "John Doe",
      "attendeeEmail": "john@example.com",
      "attendeePhone": "+255712345678",
      "ticketType": "VIP Pass",
      "ticketSeries": "VIP-0001",
      "bookingReference": "EVT-A3F4B21C",
      "pricePaid": 150.00,
      "checkInTime": "2025-12-15T09:15:00+03:00",
      "checkInLocation": "Main Gate",
      "checkedInBy": "Scanner Operator 1",
      "scannerId": "uuid-string"
    }
  ],
  "pagination": {
    "currentPage": 0,
    "pageSize": 50,
    "totalPages": 2,
    "totalElements": 99,
    "hasNext": true,
    "hasPrevious": false
  }
}
```

### AbsenteeListResponse
```json
{
  "eventId": "uuid",
  "eventTitle": "East African Tech Summit 2025",
  "dayNumber": 1,
  "dayName": "Day 1 - Opening",
  "dayDate": "2025-12-15",
  "ticketTypeId": null,
  "ticketTypeName": null,
  "summary": {
    "totalTicketsForType": 500,
    "absentThisDay": 25,
    "absenteeRate": 5.0,
    "breakdown": {
      "fullNoShow": 10,
      "specificDayOnly": 15
    }
  },
  "absentees": [
    {
      "ticketInstanceId": "uuid",
      "attendeeName": "Jane Smith",
      "attendeeEmail": "jane@example.com",
      "attendeePhone": "+255723456789",
      "ticketType": "General Admission",
      "ticketSeries": "GENER-0042",
      "bookingReference": "EVT-B5D2E12F",
      "pricePaid": 50.00,
      "statusForThisDay": "NOT_CHECKED_IN",
      "attendancePattern": {
        "totalEventDays": 3,
        "daysAttended": 2,
        "daysAbsent": 1,
        "attendedDayNumbers": [2, 3],
        "absentDayNumbers": [1],
        "category": "SPECIFIC_DAY_ONLY"
      }
    }
  ],
  "pagination": {
    "currentPage": 0,
    "pageSize": 50,
    "totalPages": 1,
    "totalElements": 25,
    "hasNext": false,
    "hasPrevious": false
  }
}
```

### AttendeeDetailResponse
```json
{
  "ticketInstanceId": "uuid",
  "attendeeName": "John Doe",
  "attendeeEmail": "john@example.com",
  "attendeePhone": "+255712345678",
  "ticketType": "VIP Pass",
  "ticketSeries": "VIP-0001",
  "bookingReference": "EVT-A3F4B21C",
  "pricePaid": 150.00,
  "overallStatus": "FULLY_ATTENDED",
  "daysAttended": 3,
  "daysTotal": 3,
  "checkInsByDay": [
    {
      "dayNumber": 1,
      "dayName": "Day 1 - Opening",
      "dayDate": "2025-12-15",
      "status": "CHECKED_IN",
      "checkInTime": "2025-12-15T09:15:00+03:00",
      "checkInLocation": "Main Gate",
      "checkedInBy": "Scanner Operator 1",
      "scannerId": "uuid-string"
    },
    {
      "dayNumber": 2,
      "dayName": "Day 2 - Conference",
      "dayDate": "2025-12-16",
      "status": "CHECKED_IN",
      "checkInTime": "2025-12-16T08:45:00+03:00",
      "checkInLocation": "VIP Entrance",
      "checkedInBy": "Scanner Operator 2",
      "scannerId": "uuid-string"
    },
    {
      "dayNumber": 3,
      "dayName": "Day 3 - Closing",
      "dayDate": "2025-12-17",
      "status": "CHECKED_IN",
      "checkInTime": "2025-12-17T09:00:00+03:00",
      "checkInLocation": "Main Gate",
      "checkedInBy": "Scanner Operator 1",
      "scannerId": "uuid-string"
    }
  ]
}
```

---

## Endpoints

## 1. Get Attendance Stats
**Endpoint**: <span style="background-color: #28a745; color: white; padding: 4px 8px; border-radius: 4px;">GET</span> `/attendance/{eventId}/stats`

**Access**: 🔒 Event Organizer Only

**Path Parameters**:
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| eventId | string (UUID) | Yes | Event identifier |

**Success Response**: Returns AttendanceStatsResponse

**Success Response Message**: "Attendance stats retrieved"

**Behavior**:
- Validates organizer owns event
- Calculates overall attendance metrics
- Breaks down by day (multi-day events)
- Breaks down by ticket type
- Real-time from check-in data

**Errors**:
- `403 FORBIDDEN`: Not event organizer
- `404 NOT_FOUND`: Event not found

---

## 2. Get Attendees
**Endpoint**: <span style="background-color: #28a745; color: white; padding: 4px 8px; border-radius: 4px;">GET</span> `/attendance/{eventId}/attendees?dayNumber=1&ticketTypeId=uuid&search=john&page=0&size=50`

**Access**: 🔒 Event Organizer Only

**Path Parameters**:
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| eventId | string (UUID) | Yes | Event identifier |

**Query Parameters**:
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| dayNumber | integer | Multi-day: Yes | Day number (1, 2, 3...) |
| ticketTypeId | string (UUID) | No | Filter by ticket type |
| search | string | No | Search by name/email |
| page | integer | No | Page number (0-indexed, default: 0) |
| size | integer | No | Items per page (default: 50) |

**Success Response**: Returns AttendeeListResponse with pagination

**Success Response Message**: "Attendees retrieved"

**Behavior**:
- Lists checked-in attendees for specified day
- Filters by ticket type if provided
- Searches by name/email if provided
- Paginated results
- Shows check-in details

**Validation**:
- Multi-day events require dayNumber
- Ticket type must belong to event
- Day number must be valid (1 to totalDays)

---

## 3. Get Absentees
**Endpoint**: <span style="background-color: #28a745; color: white; padding: 4px 8px; border-radius: 4px;">GET</span> `/attendance/{eventId}/absentees?dayNumber=1&ticketTypeId=uuid&category=FULL_NO_SHOW&search=jane&page=0&size=50`

**Access**: 🔒 Event Organizer Only

**Path Parameters**:
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| eventId | string (UUID) | Yes | Event identifier |

**Query Parameters**:
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| dayNumber | integer | Multi-day: Yes | Day number (1, 2, 3...) |
| ticketTypeId | string (UUID) | No | Filter by ticket type |
| category | enum | No | ALL, FULL_NO_SHOW, SPECIFIC_DAY_ONLY (default: ALL) |
| search | string | No | Search by name/email |
| page | integer | No | Page number (0-indexed, default: 0) |
| size | integer | No | Items per page (default: 50) |

**Success Response**: Returns AbsenteeListResponse with pagination

**Success Response Message**: "Absentees retrieved"

**Behavior**:
- Lists NOT checked-in attendees for specified day
- Categories:
  - **ALL**: Everyone not checked-in for this day
  - **FULL_NO_SHOW**: Never checked-in any day
  - **SPECIFIC_DAY_ONLY**: Attended other days but not this one
- Shows attendance patterns
- Paginated results

**Absentee Breakdown**:
- Full no-shows: Tickets never used
- Specific day only: Partial attendance

---

## 4. Get Attendee Detail
**Endpoint**: <span style="background-color: #28a745; color: white; padding: 4px 8px; border-radius: 4px;">GET</span> `/attendance/{eventId}/attendees/{ticketInstanceId}`

**Access**: 🔒 Event Organizer Only

**Path Parameters**:
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| eventId | string (UUID) | Yes | Event identifier |
| ticketInstanceId | string (UUID) | Yes | Ticket instance identifier |

**Success Response**: Returns AttendeeDetailResponse

**Success Response Message**: "Attendee detail retrieved"

**Behavior**:
- Shows complete check-in history
- Per-day status (CHECKED_IN, NOT_CHECKED_IN, UPCOMING)
- Overall attendance status
- Check-in details (time, location, scanner)

**Overall Status**:
- **FULLY_ATTENDED**: Checked-in all days
- **PARTIALLY_ATTENDED**: Checked-in some days
- **NOT_ATTENDED**: Never checked-in

---

## Absentee Categories Explained

### ALL (Default)
**Who**: Everyone who didn't check in for the specified day

**Includes**:
- Full no-shows (never attended any day)
- Specific-day absentees (attended other days)

**Use Case**: Complete list of who's missing today

### FULL_NO_SHOW
**Who**: Tickets never checked-in for ANY event day

**Characteristics**:
- Purchased ticket but never attended
- 0% attendance rate
- Wasted tickets

**Use Case**: Identify completely unused tickets for follow-up

### SPECIFIC_DAY_ONLY
**Who**: Attended some days but NOT this specific day

**Characteristics**:
- Checked-in on other event days
- Partial attendance
- Shows attendance pattern

**Use Case**: Identify who skipped specific days (e.g., missed keynote)

**Example**:
```
3-Day Festival:
- Ticket A: Attended Day 1, 2, 3 → FULLY_ATTENDED
- Ticket B: Attended Day 1, 3 (missed Day 2) → SPECIFIC_DAY_ONLY for Day 2
- Ticket C: Never attended → FULL_NO_SHOW for all days
```

---

## Multi-Day Event Logic

### Day Number Requirements

**Single-Day Events**:
- `dayNumber` optional (defaults to 1)
- Only one day to track

**Multi-Day Events**:
- `dayNumber` REQUIRED
- Must specify which day (1, 2, 3...)
- System returns error if missing

**Validation**:
```
Event has 3 days → dayNumber must be 1, 2, or 3
Request dayNumber=5 → Error: "Invalid day 5. Valid: 1-3"
```

### Per-Day Tracking

**Statistics**:
- Total tickets (same for all days)
- Checked-in (varies per day)
- Absent (varies per day)
- Attendance rate (varies per day)

**Day Status**:
- **COMPLETED**: Day has passed
- **ONGOING**: Day is today
- **UPCOMING**: Day hasn't started

**Example**:
```
3-Day Event (Dec 15-17), Today: Dec 16

Day 1: status=COMPLETED, checkedIn=475
Day 2: status=ONGOING, checkedIn=450
Day 3: status=UPCOMING, checkedIn=0
```

---

## Attendance Rate Calculations

### Overall Attendance Rate
```
Rate = (Unique Tickets Checked-In / Total Tickets) × 100
```

**Example**:
- Total tickets: 500
- Tickets with at least one check-in: 462
- Rate: 92.4%

### Per-Day Attendance Rate
```
Rate = (Checked-In This Day / Total Tickets) × 100
```

**Example**:
- Total tickets: 500
- Checked-in Day 1: 475
- Rate: 95.0%

### Per-Ticket-Type Rate
```
Rate = (Checked-In for Type / Total Sold for Type) × 100
```

**Example**:
- VIP tickets sold: 100
- VIP checked-in: 98
- Rate: 98.0%

---

## Search Functionality

**Searches**:
- Attendee name (case-insensitive)
- Attendee email (case-insensitive)

**Match**: Partial match (contains)

**Examples**:
- Search "john" → Matches "John Doe", "Johnny Smith", "john@example.com"
- Search "@gmail" → Matches all Gmail addresses
- Search "255712" → Matches phone numbers containing this

---

## Use Cases

### Dashboard Overview
```
GET /attendance/{eventId}/stats

Shows:
- Overall attendance rate
- Per-day breakdown
- Per-ticket-type breakdown
- Real-time metrics
```

### Check Who Attended Today
```
GET /attendance/{eventId}/attendees?dayNumber=1

Shows:
- All checked-in attendees for Day 1
- Check-in times and locations
- Searchable and filterable
```

### Find No-Shows
```
GET /attendance/{eventId}/absentees?dayNumber=1&category=FULL_NO_SHOW

Shows:
- Tickets that were never used
- For follow-up or refund processing
```

### Track Partial Attendance
```
GET /attendance/{eventId}/absentees?dayNumber=2&category=SPECIFIC_DAY_ONLY

Shows:
- Who attended Day 1 but missed Day 2
- Useful for understanding engagement patterns
```

### Individual Ticket History
```
GET /attendance/{eventId}/attendees/{ticketInstanceId}

Shows:
- Complete check-in history
- Which days attended/missed
- Check-in details per day
```

---

## Best Practices

### For Organizers
✅ Monitor attendance in real-time during event  
✅ Follow up with full no-shows post-event  
✅ Track partial attendance patterns  
✅ Export attendee lists for post-event communications  
✅ Use absentee data to improve future events  

### For Developers
✅ Require dayNumber for multi-day events  
✅ Show attendance rate with 1 decimal (92.4%)  
✅ Implement export to CSV functionality  
✅ Cache stats (refresh every 5 minutes)  
✅ Display check-in times in event timezone  

---

## Quick Reference

### HTTP Status Codes
- `200 OK`: Successful request
- `401 UNAUTHORIZED`: Authentication required
- `403 FORBIDDEN`: Not event organizer
- `404 NOT_FOUND`: Event/ticket not found

### Attendance Status
- **FULLY_ATTENDED**: All days checked-in
- **PARTIALLY_ATTENDED**: Some days checked-in
- **NOT_ATTENDED**: Never checked-in

### Day Status
- **COMPLETED**: Day has passed
- **ONGOING**: Day is today
- **UPCOMING**: Day hasn't started

### Absentee Categories
- **ALL**: All absentees for this day
- **FULL_NO_SHOW**: Never attended any day
- **SPECIFIC_DAY_ONLY**: Missed this day only

---

## Conclusion

The Attendance Analytics API provides comprehensive tracking with:

✅ **Real-Time Stats**: Instant attendance metrics  
✅ **Multi-Day Support**: Per-day breakdowns  
✅ **Absentee Tracking**: Full no-shows vs partial attendance  
✅ **Search & Filter**: By ticket type, day, name/email  
✅ **Individual History**: Complete check-in records  
✅ **Attendance Patterns**: Understand engagement