# Blocklive docs (full text) > Blocklive is a ticketing and events platform. Organizers sell tickets, run the door and build an audience. Its developer API lets their apps read events, ticket types, attendees and check-ins with an API key. Every page of https://blocklive.io/docs in one file, built from the same Markdown. The index is https://blocklive.io/llms.txt. # Blocklive docs Source: https://blocklive.io/docs Blocklive is a ticketing and events platform. Organizers sell tickets, run the door and build an audience; attendees buy tickets and keep them in their Blocklive account. These docs cover what is live today. Pages for organizers are on their way. ## Developers - [Developer API](https://blocklive.io/docs/developers): read your events, ticket types, attendees and check-ins from your own app. - [Read endpoints](https://blocklive.io/docs/developers/reads): every endpoint, its fields, paging, and how to keep an onsite app up to date. - [Limits and errors](https://blocklive.io/docs/developers/limits-and-errors): the rate limit and every error code. ## Help - Questions about a ticket or an order: [Support](https://blocklive.io/support). - Anything else: [support@blocklive.io](mailto:support@blocklive.io). ## For AI assistants These docs are also at [blocklive.io/llms.txt](https://blocklive.io/llms.txt) (an index) and [blocklive.io/llms-full.txt](https://blocklive.io/llms-full.txt) (every page in one file). # Developer API Source: https://blocklive.io/docs/developers Read your events, ticket types, attendees and check-ins from your own app: a door or badge app, a CRM sync, a dashboard. > The developer API is in a pilot. Blocklive switches developer access on for > an organization or a team. To take part, email > [support@blocklive.io](mailto:support@blocklive.io). ## What it does - It reads. Every endpoint is a `GET` under `https://blocklive.io/api/v1` and answers JSON. - It covers events, ticket types, attendees (one row per ticket holder) and the door's check-ins and check-outs. - It doesn't check guests in, add guests or change anything. Check-in stays in Blocklive's door app. ## Get a key An API key comes from the organizer: an admin of the organization or team, in Blocklive's admin, on its **Developer** page. When the key is made, the organizer chooses which events it reads and which attendee details it gets. The key is shown once. Blocklive keeps only a hash of it, so a lost key can't be shown again: ask the organizer for a new one. ## Authenticate Send the key as a bearer token on every request: ```http Authorization: Bearer bl_live__ ``` ```sh curl -H "Authorization: Bearer $BLOCKLIVE_API_KEY" https://blocklive.io/api/v1/events ``` > **Warning:** Keep the key on your server, in your app's secret settings. > Never put it in a browser, a phone app or a repository. Anyone with the key > can read what it reads. - A revoked or expired key gets `401 unauthorized`. - Keys made outside production start with `bl_test_` and only work there. ## What a key reads Every key reads **events, ticket types and attendees by an anonymous ID**. Attendee details are included only if the organizer shared them with the key: | Detail | Field in the API | Needs | | ----------------------- | --------------------- | ----------------------- | | Email address | `email` | | | Full name | `name` | | | Company | `company` | | | Job title | `job_title` | | | Ticket types and access | `ticket_entitlements` | | | Ticket type names | `ticket_type_names` | Ticket types and access | | Purchase status | `purchase_state` | | | Venue check-in | `checkin_state` | | | Check-in time | `checkin_time` | Venue check-in | Each answer lists what your key reads for that event in `shared_fields`. ## Endpoints | Path | Returns | | -------------------------------------------- | -------------------------------------------------------- | | `/events` | The events your key can read, newest first | | `/events/{event_id}/ticket-types` | The event's ticket types | | `/events/{event_id}/attendees` | One row per ticket holder, with only the details shared | | `/events/{event_id}/attendees/{attendee_id}` | One attendee | | `/events/{event_id}/check-ins` | Check-ins and check-outs in order (needs venue check-in) | Fields, examples and paging are in [read endpoints](https://blocklive.io/docs/developers/reads). The rate limit and every error code are in [limits and errors](https://blocklive.io/docs/developers/limits-and-errors). ## Privacy - A key reads only the details shared with it, only for its events, and only while developer access is on. - Attendees are identified by anonymous IDs made for each key and event. - Blocklive logs each request's time, key, endpoint, status and duration for 90 days, never the data it returned. ## Help Email [support@blocklive.io](mailto:support@blocklive.io) with the `request_id` of any error. # Read endpoints Source: https://blocklive.io/docs/developers/reads Every endpoint of the developer API, what it returns, how paging works, and how to keep an onsite app up to date. For keys and authentication, see the [developer API overview](https://blocklive.io/docs/developers). All endpoints are `GET` under `https://blocklive.io/api/v1` and answer JSON. Send your key on every request: ```http Authorization: Bearer bl_live__ ``` | Path | Returns | | -------------------------------------------- | --------------------------------------------------------------------------- | | `/events` | The events your key can read, newest first. | | `/events/{event_id}/ticket-types` | The event's ticket types. | | `/events/{event_id}/attendees` | One row per ticket holder, with only the details shared with your key. | | `/events/{event_id}/attendees/{attendee_id}` | One attendee. | | `/events/{event_id}/check-ins` | Check-ins and check-outs in the order they happened (needs venue check-in). | ## IDs `event_id` is the event's Blocklive ID. Every other ID is an opaque string made for your key and that event: `att_…` (attendees), `typ_…` (ticket types), `ent_…` (tickets) and `pur_…` (purchases). The same person has a different `attendee_id` at another event and for another app. Use them only with this API, and don't try to match them across keys or events. ## Events ``` GET /api/v1/events ``` ```json { "data": [ { "event_id": "6f1c…", "title": "Summer Opening", "starts_at": "2026-11-13T18:00:00.000Z", "ends_at": "2026-11-14T02:00:00.000Z", "time_zone": "Europe/London", "shared_fields": ["name", "checkin_state"] } ], "next_cursor": null, "complete": true, "as_of": "2026-11-05T12:00:00.000Z" } ``` `shared_fields` lists the attendee details your key reads for that event. ## Ticket types ``` GET /api/v1/events/{event_id}/ticket-types ``` ```json { "data": [{ "ticket_type_id": "typ_…", "name": "General admission" }], "as_of": "2026-11-05T12:00:00.000Z", "shared_fields": ["ticket_entitlements", "ticket_type_names"] } ``` `name` is there only if ticket type names are shared with your key. This endpoint isn't paged. ## Attendees ``` GET /api/v1/events/{event_id}/attendees ``` ``` GET /api/v1/events/{event_id}/attendees/{attendee_id} ``` One row per ticket holder. Every row has: | Field | Meaning | | ------------- | ---------------------------------------------------------------------------------------- | | `event_id` | The event. | | `attendee_id` | The holder, as an opaque ID for your key. | | `eligibility` | `eligible` (holds a ticket), `pending` (a purchase is still processing) or `ineligible`. | | `updated_at` | When anything about this attendee last changed. | The rest appears only if it is shared with your key: | Field | Detail | Value | | --------------------- | ----------------------- | ------------------------------------------------------------------------------------------------- | | `email` | Email address | A string or `null`. | | `name` | Full name | A string or `null`. | | `company` | Company | A string or `null`. | | `job_title` | Job title | A string or `null`. | | `ticket_entitlements` | Ticket types and access | Their tickets: `id`, `ticket_type_id`, `status`, and `checkin_state` if venue check-in is shared. | | `ticket_type_names` | Ticket type names | `id` and `name` of each ticket type they hold. Needs ticket types and access. | | `purchase_state` | Purchase status | Their purchases: `id` and `state` (`confirmed`, `pending`, `rejected` or `unknown`). | | `checkin_state` | Venue check-in | `checked_in`, `not_checked_in` or `unknown`. | | `checkin_time` | Check-in time | When they were checked in, or `null`. Needs venue check-in. | ```json { "data": [ { "event_id": "6f1c…", "attendee_id": "att_…", "eligibility": "eligible", "updated_at": "2026-11-05T11:58:00.000Z", "name": "Sam Lee", "checkin_state": "not_checked_in" } ], "next_cursor": "…", "complete": false, "as_of": "2026-11-05T12:00:00.000Z", "shared_fields": ["name", "checkin_state"] } ``` ## Check-ins ``` GET /api/v1/events/{event_id}/check-ins?cursor= ``` ```json { "data": [ { "attendee_id": "att_…", "entitlement_id": "ent_…", "checkin_state": "checked_in", "at": "2026-11-13T18:00:00.000Z" } ], "next_cursor": "…", "complete": true, "as_of": "2026-11-13T18:00:05.000Z" } ``` Needs venue check-in shared with your key. - `checkin_state` is `checked_in` or `checked_out`, one row per scan or change at the door, oldest first. A ticket can appear more than once (scanned again, or out and back in): apply rows in order, and the last one for a ticket is its state. - A scan shows up about 5 seconds after it happens. - `entitlement_id` (the ticket) is there only if ticket types and access are shared. `attendee_id` is the ticket's holder now (it matches the attendees endpoint), or `null` for a ticket that no longer exists. - Every answer has a `next_cursor`, even an empty one. Keep the last one and send it on the next poll: you get only what happened since. The attendees endpoint gives each attendee's current `checkin_state` and `checkin_time`, if shared. It is the source of truth: use it to correct your copy when you resync. ## Pages `/events`, `/attendees` and `/check-ins` return up to `limit` rows (1 to 100, default 100) and a `next_cursor`. Pass it back as `?cursor=` for the next page. `complete: true` means there is nothing after this page. `limit` and `cursor` are the only query parameters. A page cursor can be used for 15 minutes; a check-in cursor for 24 hours, so a door app restarted during doors carries on where it was. A `410 resync_required` means the cursor can't be used any more: it is too old, or the organizer changed what your key may read. Start again without a cursor. See [limits and errors](https://blocklive.io/docs/developers/limits-and-errors). ## Keeping an onsite app up to date 1. **At start-up, and every 10 minutes:** read every page of `/events/{event_id}/attendees` for the guest list. 2. **Every 5 seconds during doors:** poll `/events/{event_id}/check-ins` with your last cursor, and apply the rows in order. An empty answer is cheap. 3. On `410`, drop the cursor and read check-ins from the start (it replays the night's scans, oldest first). On `429`, wait for `Retry-After`. That is about 12 requests a minute for check-ins plus one guest-list sync every 10 minutes (one request per 100 attendees), well under the limit. Don't run more than one poller per key. Check-in itself stays in Blocklive's door app. This API reads it; it doesn't change it. # Limits and errors Source: https://blocklive.io/docs/developers/limits-and-errors How much you can ask for, and what every error means. ## Limits | Limit | Value | | --------------- | ------------------------------------------------- | | Requests | 120 a minute per key. Other keys aren't affected. | | Rows a page | 1 to 100 (`?limit=`, default 100). | | Page cursor | Can be used for 15 minutes. | | Check-in cursor | Can be used for 24 hours. | Over 120 requests in a minute you get `429 rate_limited` with a `Retry-After` header: the seconds until the next minute starts. Wait that long, then carry on. ## Errors Errors are JSON, with a code and the request's ID: ```json { "error": { "code": "rate_limited", "request_id": "6f9e…" } } ``` Quote the `request_id` when you contact Blocklive. | Status | Code | Meaning | What to do | | ------ | -------------------- | ---------------------------------------------------------------------------------------------- | -------------------------------------------------- | | 400 | `invalid_request` | A query parameter is wrong or not allowed, or `limit` is outside 1 to 100. | Fix the request. | | 401 | `unauthorized` | No key, a malformed key, or a key that is revoked, expired or for the other environment. | Check the key; ask the organizer. | | 404 | `not_found` | The path, event or attendee doesn't exist, or your key can't read it. These aren't told apart. | Check the IDs and the key's events. | | 405 | `method_not_allowed` | Only `GET` is supported. | Use `GET`. | | 410 | `resync_required` | The cursor is too old, or what your key may read changed. | Start again without a cursor. | | 429 | `rate_limited` | Over 120 requests a minute. | Wait for `Retry-After`. | | 503 | `unavailable` | Blocklive couldn't answer just now. | Retry with backoff: 1 s, 2 s, 4 s, up to a minute. | Retrying is safe: every endpoint only reads.