Event Check-In System API
Base URL: https://api.nexgate.com/api/v1
Short Description: The Event Check-In System API provides secure ticket validation and scanner management for event entry. This API enables organizers to generate registration tokens (like WhatsApp's "Link Device"), scanners to register for events, and perform real-time ticket validation with JWT signature verification. The system supports multi-day events with per-day check-in tracking, device fingerprinting for security, automatic scanner revocation, and comprehensive duplicate detection.
Hints:
- Registration Flow: Organizer generates token → Scanner scans QR → Scanner registers → Automatic revocation of old scanners
- Device Security: Fingerprint validation prevents credential theft
- JWT Validation: RSA-signed tickets verified offline-capable
- Multi-Day Events: Separate check-in per event day
- One Device Rule: One ACTIVE scanner per device across all events
- Check-In Strategies: 5 strategies (HOURS_BEFORE, SPECIFIC_TIME, ALL_DAY, EXACT_TIME, AS_DAY_START)
- Duplicate Detection: Prevents same-day re-entry per event day
- Scanner Stats: Tracks successful/failed scans automatically
- Auto-Revocation: Old scanners revoked when device registers for new event
- Organizer Only: Only event organizers can generate tokens and manage scanners
API Overview
Registration Token Endpoints
- POST
/check-in/tokens/generate- Generate registration token (organizer) - GET
/check-in/tokens/validate/{token}- Validate registration token
Scanner Management Endpoints
- POST
/check-in/scanners/register- Register scanner device - GET
/check-in/scanners/event/{eventId}- Get all scanners for event - GET
/check-in/scanners/event/{eventId}/active- Get active scanners - POST
/check-in/scanners/{scannerId}/revoke- Revoke scanner
Ticket Validation Endpoint
- POST
/check-in/validate- Validate ticket and check-in
Response Structures
RegistrationTokenResponse
{
"tokenId": "uuid",
"token": "REG-ABC12345-XYZ67890",
"eventId": "uuid",
"eventName": "East African Tech Summit 2025",
"scannerName": "Gate A - Main Entrance",
"expiresAt": "2025-12-11T10:35:00Z",
"validityMinutes": 5,
"remainingSeconds": 240,
"qrCodeData": "scannerapp://register?token=REG-ABC12345-XYZ67890",
"isValid": true,
"used": false
}
ScannerResponse
{
"scannerId": "uuid-string",
"name": "Gate A - Main Entrance",
"eventId": "uuid",
"eventName": "East African Tech Summit 2025",
"status": "ACTIVE",
"deviceFingerprint": "abc123def456...",
"createdAt": "2025-12-11T10:30:00Z",
"credentials": "eyJhbGc...",
"publicKey": "MIIBIjANBgkqhkiG9w0...",
"revocationReason": null
}
ValidateTicketResponse
{
"valid": true,
"status": "VALID",
"message": "✅ Entry granted for Day 1 - Opening Day. Welcome!",
"ticketInstanceId": "uuid",
"ticketTypeName": "VIP Pass",
"ticketSeries": "VIP-0001",
"attendeeName": "John Doe",
"attendeeEmail": "john@example.com",
"eventName": "East African Tech Summit 2025",
"bookingReference": "EVT-A3F4B21C",
"alreadyCheckedIn": false,
"previousCheckInTime": null,
"previousCheckInLocation": null,
"currentCheckInTime": "2025-12-15T09:15:00+03:00",
"validationMode": "ONLINE",
"scannerName": "Gate A - Main Entrance",
"dayName": "Day 1 - Opening Day"
}
Endpoints
1. Generate Registration Token
Endpoint: POST /check-in/tokens/generate
Access: 🔒 Event Organizer Only
Request:
{
"eventId": "uuid",
"scannerName": "Gate A - Main Entrance"
}
Success Response: Returns RegistrationTokenResponse
Behavior:
- Validates user is event organizer
- Validates event has RSA keys (must be published)
- Generates token:
REG-{8-UUID}-{8-UUID} - Sets expiry: 5 minutes (configurable)
- Returns QR code data for scanner app
Errors:
403 FORBIDDEN: Not event organizer404 NOT_FOUND: Event not found422: Event not published or no RSA keys
2. Validate Registration Token
Endpoint: GET /check-in/tokens/validate/{token}
Access: 🔓 Public (for scanner apps)
Success Response: Returns RegistrationTokenResponse with validity status
Use Case: Scanner app validates token before registration
3. Register Scanner
Endpoint: POST /check-in/scanners/register
Access: 🔓 Public (uses registration token)
Request:
{
"registrationToken": "REG-ABC12345-XYZ67890",
"deviceFingerprint": "abc123def456hash",
"scannerName": "Gate A - Main Entrance",
"deviceInfo": "{\"model\":\"iPhone 13\",\"os\":\"iOS 15\"}"
}
Success Response: Returns ScannerResponse with credentials
Behavior:
- Validates device fingerprint (10-255 chars)
- Validates token (not used, not expired)
- Auto-revokes any ACTIVE scanner with same device fingerprint
- Generates scanner ID (UUID)
- Generates JWT credentials (1 year validity)
- Marks token as used (one-time use)
Key Rule: One device → One ACTIVE scanner (across all events)
Errors:
400 BAD_REQUEST: Invalid fingerprint, token used/expired404 NOT_FOUND: Token not found
4. Get Scanners for Event
Endpoint: GET /check-in/scanners/event/{eventId}
Access: 🔒 Event Organizer Only
Success Response: Returns array of ScannerResponse
Includes: All scanners (ACTIVE + REVOKED)
5. Get Active Scanners
Endpoint: GET /check-in/scanners/event/{eventId}/active
Access: 🔒 Event Organizer Only
Success Response: Returns array of ScannerResponse (ACTIVE only)
6. Revoke Scanner
Endpoint: POST /check-in/scanners/{scannerId}/revoke?reason=Suspicious activity
Access: 🔒 Event Organizer Only
Success Response: 200 OK with confirmation message
Behavior:
- Changes status to REVOKED (permanent)
- Records revocation reason and timestamp
- Scanner can no longer validate tickets
7. Validate Ticket and Check-In
Endpoint: POST /check-in/validate
Access: 🔒 Scanner credentials required
Request:
{
"jwtToken": "eyJhbGc...",
"scannerId": "uuid-string",
"deviceFingerprint": "abc123def456hash",
"checkInLocation": "Gate A"
}
Success Response: Returns ValidateTicketResponse
Validation Flow:
- Validate Scanner: Active status, fingerprint match
- Verify JWT: RSA signature with event's public key
- Find Current Day: Match current time to event schedules
- Find Booking: Locate ticket in database
- Check Duplicate: Already checked in for this day?
- Create Check-In: Add check-in record for current day
- Update Stats: Increment scanner counters
Check-In Strategies:
- HOURS_BEFORE: X hours before event + late grace period
- SPECIFIC_TIME: Daily window (e.g., 08:00-23:00)
- ALL_DAY: Anytime on event date (00:00-23:59)
- EXACT_TIME: Only during event start-end
- AS_DAY_START: From day start (00:00) until event end + grace
Validation Statuses:
- VALID: ✅ Entry granted
- DUPLICATE: ❌ Already checked in for this day
- INVALID_SIGNATURE: ❌ JWT signature failed
- EXPIRED: ❌ Ticket validity expired
- NOT_FOUND: ❌ Ticket not in database
- REVOKED: ❌ Scanner revoked
Response Examples:
Success (VALID):
{
"success": true,
"message": "✅ Entry granted for Day 1 - Opening Day. Welcome!",
"data": {
"valid": true,
"status": "VALID",
"attendeeName": "John Doe",
"dayName": "Day 1 - Opening Day",
"currentCheckInTime": "2025-12-15T09:15:00+03:00"
}
}
Duplicate Check-In:
{
"success": false,
"message": "❌ Ticket already used for Day 1 - Opening Day. Entry denied.",
"data": {
"valid": false,
"status": "DUPLICATE",
"alreadyCheckedIn": true,
"previousCheckInTime": "2025-12-15T09:00:00+03:00",
"previousCheckInLocation": "Gate A"
}
}
System Flows
Registration Flow
Step 1: Organizer Generates Token
Organizer → POST /tokens/generate
← Returns: Token + QR code data
Step 2: Scanner Scans QR Code
Scanner App → Scans QR code
Extracts: "scannerapp://register?token=REG-..."
Step 3: Scanner Validates Token (Optional)
Scanner App → GET /tokens/validate/{token}
← Confirms: Token valid, event details
Step 4: Scanner Registers
Scanner App → POST /scanners/register
Sends: Token, device fingerprint, name
← Receives: Scanner credentials (JWT)
Step 5: Scanner Stores Credentials
Scanner App → Saves: credentials, publicKey
Ready to validate tickets offline
Ticket Validation Flow
Step 1: Scanner Scans Ticket QR
Scanner → Reads JWT from QR code
Step 2: Offline Validation (Optional)
Scanner → Verifies JWT signature with public key
Checks: Expiry, event match
Step 3: Online Check-In
Scanner → POST /validate
Sends: JWT, scannerId, fingerprint
Step 4: System Validates
System → Validates scanner status
System → Verifies JWT signature
System → Finds current event day
System → Checks duplicate
System → Records check-in
Step 5: Response to Scanner
System → Returns validation result
Scanner → Shows success/error to staff
Multi-Day Event Support
How It Works
Single-Day Event:
- One check-in expected
- Status: ACTIVE → USED after check-in
Multi-Day Event (e.g., 3-day festival):
- Multiple check-ins allowed (one per day)
- Each day tracked separately
- Ticket stays ACTIVE throughout
Example: 3-Day Festival
JWT contains schedules:
{
"eventSchedules": [
{
"dayName": "Day 1 - Friday Night",
"startDateTime": "2025-12-15T18:00:00+03:00",
"endDateTime": "2025-12-15T23:59:00+03:00"
},
{
"dayName": "Day 2 - Saturday",
"startDateTime": "2025-12-16T10:00:00+03:00",
"endDateTime": "2025-12-16T23:59:00+03:00"
},
{
"dayName": "Day 3 - Sunday",
"startDateTime": "2025-12-17T10:00:00+03:00",
"endDateTime": "2025-12-17T20:00:00+03:00"
}
]
}
Check-In Timeline:
Friday 18:30 → Check-in for "Day 1 - Friday Night" ✅
Friday 19:00 → Duplicate for "Day 1" ❌
Saturday 11:00 → Check-in for "Day 2 - Saturday" ✅
Saturday 15:00 → Duplicate for "Day 2" ❌
Sunday 12:00 → Check-in for "Day 3 - Sunday" ✅
Validation Logic:
- System finds current time: Saturday 11:00
- Matches to event schedule: "Day 2 - Saturday"
- Checks if ticket already checked in for "Day 2"
- If no → Allow check-in
- If yes → Reject as duplicate
Check-In Window Strategies
1. HOURS_BEFORE (Default)
Configuration:
- earlyCheckInHours: 2 (default)
- lateCheckInMinutes: 30 (default)
Window: 2 hours before event until 30 minutes after event ends
Example:
Event: 09:00 - 18:00
Check-in allowed: 07:00 - 18:30
Use Case: Conferences, concerts
2. SPECIFIC_TIME
Configuration:
- checkInOpensAt: "08:00"
- checkInClosesAt: "23:00"
Window: Same time window each event day
Example:
3-day event (Dec 15-17)
Check-in allowed: 08:00-23:00 each day
Use Case: Multi-day festivals with consistent entry hours
3. ALL_DAY
Configuration: None needed
Window: Entire event date (00:00 - 23:59)
Example:
Event date: Dec 15
Check-in allowed: Dec 15 00:00 - Dec 15 23:59
Use Case: All-day events, exhibitions
4. EXACT_TIME
Configuration: None
Window: Only during event start-end times
Example:
Event: 14:00 - 17:00
Check-in allowed: 14:00 - 17:00 only
Use Case: Strict timing events
5. AS_DAY_START
Configuration:
- lateCheckInMinutes: 30 (default)
Window: From start of event day (00:00) until event end + grace
Example:
Event: Dec 15 18:00 - 23:00
Check-in allowed: Dec 15 00:00 - 23:30
Use Case: Evening events with early arrival
Security Features
Device Fingerprinting
Purpose: Prevent credential theft
How It Works:
- Scanner generates fingerprint from device hardware
- Fingerprint sent with every request
- System validates fingerprint matches registered device
- Mismatch → Reject (credentials stolen)
Fingerprint Components (example):
const fingerprint = SHA256(
deviceModel +
osVersion +
hardwareId +
appInstallId
);
JWT Credentials
Scanner Credentials (1 year validity):
{
"scannerId": "uuid",
"eventId": "uuid",
"type": "scanner_credential",
"iat": 1702300000,
"exp": 1733836000
}
Signed with: Event's RSA private key
Used for: Scanner authentication to API
Ticket JWT Verification
Process:
- Scanner receives ticket JWT (QR code)
- Scanner verifies signature with event's public key
- Scanner can validate offline (no internet needed)
- Scanner sends to API for check-in recording
Benefits:
- Offline validation capability
- Cannot forge tickets
- Cannot reuse tokens (duplicate tracking)
- Tamper-proof (signature validation)
Auto-Revocation System
The Rule
One device → One ACTIVE scanner at a time (across ALL events)
Scenarios
Scenario 1: Device Registers for Different Event
Device ABC has ACTIVE scanner for Event 1
Device ABC registers for Event 2
→ System revokes Event 1 scanner automatically
→ Creates new scanner for Event 2
Scenario 2: Same Event Re-Registration
Device ABC has ACTIVE scanner for Event 1
Device ABC registers again for Event 1
→ System revokes old scanner
→ Creates new scanner (new credentials)
Revocation Message:
"Automatically revoked: Device registered as new scanner for event 'East African Tech Summit 2025'"
Why This Rule?
Prevents:
- Device scanning tickets for multiple events simultaneously
- Confusion about which event device is working
- Credential misuse across events
Allows:
- Device switching between events (auto-handled)
- Fresh start for each event
- Clean scanner sessions
Scanner Statistics
Auto-Tracked Metrics
- totalScans: All scan attempts
- successfulScans: Valid entries
- failedScans: Duplicates, invalid tickets
- lastScanAt: Most recent scan timestamp
- lastSyncedAt: Last API contact
Success Rate Calculation
successRate = (successfulScans / totalScans) * 100
Updated Automatically
- Each validation attempt updates counters
- Successful → successfulScans++, totalScans++
- Failed → failedScans++, totalScans++
Error Codes Summary
Registration Token Errors
- 400: Token expired/used, invalid format
- 403: Not event organizer
- 404: Token/event not found
- 422: Event not published, no RSA keys
Scanner Registration Errors
- 400: Invalid fingerprint (too short/long)
- 400: Token expired/used
- 404: Token not found
- 422: Invalid scanner name
Ticket Validation Errors
- INVALID_SIGNATURE: JWT signature failed
- DUPLICATE: Already checked in for this day
- EXPIRED: Ticket validity expired
- NOT_FOUND: Ticket not in database
- REVOKED: Scanner revoked
Best Practices
For Organizers
✅ Generate tokens right before scanner setup
✅ Use descriptive scanner names (e.g., "Gate A - Main")
✅ Monitor scanner activity via dashboard
✅ Revoke suspicious scanners immediately
✅ Test scanner before event starts
For Scanner App Developers
✅ Generate stable device fingerprint
✅ Store credentials securely (encrypted)
✅ Implement offline validation first
✅ Sync check-ins when online
✅ Show clear success/error messages
✅ Handle device fingerprint mismatch gracefully
For Event Staff
✅ Keep scanners charged
✅ Verify scanner name matches gate
✅ Watch for duplicate warnings
✅ Report technical issues immediately
✅ Use backup manual verification if needed
Quick Reference
Token Lifetime
- Registration token: 5 minutes
- Scanner credentials: 1 year
- Ticket JWT: Event validity period
Device Fingerprint
- Min length: 10 characters
- Max length: 255 characters
- Format: Stable hash of device properties
Scanner Name
- Min length: 3 characters
- Max length: 200 characters
- Examples: "Gate A", "VIP Entrance", "Main Hall Scanner"
Status Flow
Registration Token: unused → used (one-time)
Scanner: ACTIVE → REVOKED (permanent)
Validation: VALID | DUPLICATE | INVALID_SIGNATURE | EXPIRED | NOT_FOUND | REVOKED
Integration Guide
Organizer Dashboard Integration
// 1. Generate token
POST /check-in/tokens/generate
{
eventId: "uuid",
scannerName: "Gate A"
}
// 2. Display QR code
<QRCode value={response.qrCodeData} />
// 3. Show token expiry countdown
remainingTime = response.remainingSeconds
// 4. List active scanners
GET /check-in/scanners/event/{eventId}/active
Scanner App Integration
// 1. Scan registration QR code
const token = extractTokenFromQR(qrData);
// 2. Register device
POST /check-in/scanners/register
{
registrationToken: token,
deviceFingerprint: generateFingerprint(),
scannerName: "Gate A",
deviceInfo: JSON.stringify(deviceDetails)
}
// 3. Store credentials
secureStorage.save('credentials', response.credentials);
secureStorage.save('publicKey', response.publicKey);
secureStorage.save('scannerId', response.scannerId);
// 4. Validate tickets
while (eventActive) {
const ticketJWT = scanTicketQR();
// Offline validation
const offlineValid = verifyJWT(ticketJWT, publicKey);
if (offlineValid) {
// Online check-in
POST /check-in/validate
{
jwtToken: ticketJWT,
scannerId: scannerId,
deviceFingerprint: generateFingerprint(),
checkInLocation: "Gate A"
}
}
}
Conclusion
The Event Check-In System provides enterprise-grade security with:
✅ WhatsApp-Style Registration: Scan QR to link device
✅ Device Security: Fingerprint validation prevents theft
✅ Offline Capable: JWT verification without internet
✅ Multi-Day Support: Per-day check-in tracking
✅ Auto-Revocation: Clean scanner sessions per event
✅ Flexible Timing: 5 check-in window strategies
✅ Duplicate Prevention: Same-day re-entry blocked
✅ Real-Time Stats: Automatic success/fail tracking
No comments to display
No comments to display