Skip to main content
The Tickets API lets you retrieve tickets for your events and scan them programmatically. This is useful for building custom check-in apps, integrating with access control systems, or syncing attendee data.

Prerequisites

You need an API key with the following scopes:
ScopeRequired for
tickets.readListing tickets
tickets.writeScanning tickets
Create an API key from the Tickable dashboard.

Listing Tickets

Tickets are scoped to an event. Pass the event ID to retrieve all tickets:
curl https://api.tickable.io/events/550e8400-e29b-41d4-a716-446655440000/tickets \
  -H "Authorization: Bearer tk_live_YOUR_API_KEY"
The response includes pagination and a status field for each ticket:
{
  "data": [
    {
      "id": "c3d4e5f6-7890-4a1b-2c3d-4e5f6a7b8c9d",
      "scannable_code": "c3d4e5f6-7890-4a1b-2c3d-4e5f6a7b8c9d",
      "ticket_type_id": "b2c4e6a8-1234-4f5a-9c8d-0e1f2a3b4c5d",
      "order_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
      "owner_name": "Jane Doe",
      "owner_email": "jane@example.com",
      "seat": "A12",
      "status": "valid",
      "scanned_at": null,
      "cancelled_at": null
    }
  ],
  "pagination": {
    "total": 48,
    "limit": 25,
    "offset": 0,
    "has_more": true
  }
}

Ticket Status

StatusMeaning
validTicket has not been scanned or cancelled
scannedTicket has already been scanned
cancelledTicket has been cancelled

Filtering by Order

To get all tickets belonging to a specific order, use the order_id query parameter:
curl "https://api.tickable.io/events/550e8400.../tickets?order_id=f47ac10b..." \
  -H "Authorization: Bearer tk_live_YOUR_API_KEY"

Scanning Tickets

Use the scan endpoint to mark a ticket as scanned. The API validates scanner profiles, timeslot restrictions, and scan windows automatically.
curl -X POST https://api.tickable.io/tickets/c3d4e5f6-7890-4a1b-2c3d-4e5f6a7b8c9d/scan \
  -H "Authorization: Bearer tk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"scanner_id": "e5f6a7b8-9012-4c3d-4e5f-6a7b8c9d0e1f"}'
The scanner_id is optional. If omitted, the first active scanner in your organization is used:
curl -X POST https://api.tickable.io/tickets/c3d4e5f6-7890-4a1b-2c3d-4e5f6a7b8c9d/scan \
  -H "Authorization: Bearer tk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'

Scan Response

The response includes the scan result along with event and ticket details:
{
  "ticket_id": "c3d4e5f6-7890-4a1b-2c3d-4e5f6a7b8c9d",
  "status": "valid",
  "scanned_before": false,
  "event": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "title": "Summer Festival 2026",
    "location": "Vondelpark"
  },
  "timeslot": {
    "title": "Morning Session",
    "starts_at": "2026-06-15T10:00:00Z",
    "ends_at": "2026-06-15T12:00:00Z"
  },
  "ticket_type": {
    "id": "b2c4e6a8-1234-4f5a-9c8d-0e1f2a3b4c5d",
    "name": "Early Bird",
    "price": 2500
  },
  "owner_name": "Jane Doe",
  "owner_email": "jane@example.com",
  "seat": "A12",
  "scanned_at": "2026-06-15T10:05:00Z",
  "cancelled_at": null,
  "related_tickets": null
}

Scan Status

The status field tells you the outcome of the scan:
StatusMeaning
validTicket was successfully scanned
scannedTicket was already scanned before
cancelledTicket has been cancelled
not_allowedScanner profile rules or scan window prevented the scan
A scanned status means the ticket was already used. The scanned_before field is true in this case. The original scanned_at timestamp is preserved.
When the event has order scanning enabled, the response includes related_tickets — all other tickets from the same order. This is useful for group check-in scenarios:
{
  "related_tickets": [
    {
      "id": "a1b2c3d4-5678-4e9f-0a1b-2c3d4e5f6a7b",
      "seat": "A13",
      "owner_name": "John Doe",
      "owner_email": "john@example.com",
      "scanned_at": null,
      "cancelled_at": null,
      "ticket_type_name": "Early Bird"
    }
  ]
}

Example: Custom Check-in App

async function scanTicket(ticketId, scannerId) {
  const response = await fetch(
    `https://api.tickable.io/tickets/${ticketId}/scan`,
    {
      method: 'POST',
      headers: {
        'Authorization': 'Bearer tk_live_YOUR_API_KEY',
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ scanner_id: scannerId }),
    }
  );

  const result = await response.json();

  switch (result.status) {
    case 'valid':
      console.log(`Welcome, ${result.owner_name}!`);
      break;
    case 'scanned':
      console.log(`Already scanned at ${result.scanned_at}`);
      break;
    case 'cancelled':
      console.log('This ticket has been cancelled');
      break;
    case 'not_allowed':
      console.log('Scan not allowed — check scanner profile or scan window');
      break;
  }

  return result;
}