> ## Documentation Index
> Fetch the complete documentation index at: https://apidocs.sessionboard.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Registration and Expo

> Sync registrations and orders, place orders from another system, check people in from a scanner, and manage booth inventory.

Registration inside Sessionboard sells tickets through registration forms and keeps one registration per person, attached to an order. Expo puts the event's booths on a floor map and sells booth and sponsorship packages. The API reads both, and writes the parts an outside system needs: orders taken elsewhere, approval decisions, door check-ins and booth holds.

Every endpoint is per event, under `/v1/event/{eventId}/…`.

> [!NOTE]
>
> Registration and Expo are Early Access. An event without Registration turned on answers `403 FORBIDDEN` on the Registration endpoints, and an event without Expo answers `403 FORBIDDEN` on the Expo endpoints — even with the right scopes.

## 1. Get a token with the right scopes

| Scope | Lets the token |
| - | - |
| `read:registrations` | Read forms, ticket types, registrations, orders, attendees and check-ins |
| `write:registrations` | Create and update ticket types, place orders, approve / decline / cancel registrations, check people in |
| `read:expo` | Read booths and sponsorship packages |
| `write:expo` | Update booths |

These four scopes are in **preview**. A token carries them only when they are set on it explicitly — they are not part of a new token's default scopes and the token picker does not offer them yet, so ask Sessionboard support to add them. An OAuth client gets them only by naming them in its `scope` request; they are not listed in `scopes_supported`.

A write scope does not include the matching read scope, so an integration that reads back what it writes needs both. Legacy tokens with no scopes can read but not write. See [Authentication](https://apidocs.sessionboard.com/authentication.md) and [OAuth](https://apidocs.sessionboard.com/oauth.md).

## 2. Sync registrations

[List registrations](https://apidocs.sessionboard.com/api-reference/registration/list-registrations.md) returns one row per person in every status, with ticket type, form, order, answers, badge and approval state. Page through it with `page` and `page_size` (up to 200).

For an incremental sync, send the latest `updated_at` you received (or the time you started the previous run) as `updated_since`:

```bash theme={null}
curl "https://public-api.sessionboard.com/v1/event/7001/registrations?updated_since=2026-10-26T14:00:00Z&page_size=200" \
  -H "x-access-token: $SESSIONBOARD_TOKEN"
```

Filters: `status` (comma-separated; an unknown value is a `400`), `form_id`, `ticket_type_id`, `registrant_type_id`, `account_id` and `email`.

* For a door list or badge printer, [List attendees](https://apidocs.sessionboard.com/api-reference/registration/list-attendees.md) returns only `confirmed` and `checked_in` people in a short shape, sorted by name, with `updated_at` on each row.
* For money, [List orders](https://apidocs.sessionboard.com/api-reference/registration/list-orders.md) returns each order's lines, totals, payments and invoice. Amounts are integers in cents.

## 3. Place orders taken somewhere else

[Create a registration](https://apidocs.sessionboard.com/api-reference/registration/create-a-registration.md) places one order for one or more people through a registration form. Pick the form with [List registration forms](https://apidocs.sessionboard.com/api-reference/registration/list-registration-forms.md) and the ticket types it sells (`ticket_type_ids`).

```bash theme={null}
curl -X POST "https://public-api.sessionboard.com/v1/event/7001/registrations" \
  -H "x-access-token: $SESSIONBOARD_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "form_id": "6c1f2d9e-3b4a-4f5e-8a7b-1c2d3e4f5a6b",
    "payment": "paid_externally",
    "idempotency_key": "crm-order-58213",
    "attendees": [
      {
        "email": "priya.raman@example.org",
        "first_name": "Priya",
        "last_name": "Raman",
        "ticket_type_id": "0b9d6a3c-2e1f-4a5b-9c8d-7e6f5a4b3c2d"
      }
    ]
  }'
```

* `payment` is `comp`, `invoice`, `paid_externally`, `cash` or `check`. No card is charged.
* Always send an `idempotency_key` (1–128 characters). If your request times out, send it again with the same key: you get the original order back with `200` instead of a second order.
* One order takes up to 100 people.
* Capacity, per-order limits, required questions and one-registration-per-email all apply. A refusal is `409 CONFLICT` with `details.reason` (`duplicate`, `sold_out`, …).
* Registrations come back `confirmed`, or `pending_approval` when the event's approval rules hold them.

To approve, decline or cancel later, use [Update a registration](https://apidocs.sessionboard.com/api-reference/registration/update-a-registration.md) with `action`.

## 4. Check people in from a scanner

[Check someone in](https://apidocs.sessionboard.com/api-reference/registration/check-someone-in.md) takes a `registration_id`, an `email` or a scanned `badge_code` — the `sb-checkin:…` QR from the confirmation email, or a printed or in-app badge.

```bash theme={null}
curl -X POST "https://public-api.sessionboard.com/v1/event/7001/check-ins" \
  -H "x-access-token: $SESSIONBOARD_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "badge_code": "sb-checkin:5d4c3b2a-1f0e-4d9c-8b7a-6f5e4d3c2b1a", "checked_in_at": "2026-11-10T13:02:55Z" }'
```

* It is safe to retry: a second scan answers `200` with `already_checked_in: true`.
* A canceled, declined or transferred registration is refused with `409 CONFLICT` (`details.reason: wrong_status`, with the status in `details.status`).
* Send `checked_in_at` when uploading scans made offline.

To mirror check-ins made in Sessionboard's own door app, poll [List check-ins](https://apidocs.sessionboard.com/api-reference/registration/list-check-ins.md) with `since`, which filters on the check-in time (`checked_in_at`). Key each check-in by its registration `id`.

## 5. Manage booths and sponsorships

[List booths](https://apidocs.sessionboard.com/api-reference/expo/list-booths.md) returns every booth on the event's floor maps with its status (`available`, `hold`, `reserved`, `sold`, `blocked`), price, package and the exhibitor or sponsor holding it. Filter with `floor_map_id` and `status`.

[Update a booth](https://apidocs.sessionboard.com/api-reference/expo/update-a-booth.md) holds, reserves, sells, blocks or releases a booth, assigns it to an exhibitor or sponsor of the event (`account_id`), sets its own price (`list_price_cents`) or moves it to another booth package (`product_id`). A hold with `hold_until` lapses back to `available` on its own.

[List sponsorships](https://apidocs.sessionboard.com/api-reference/expo/list-sponsorships.md) returns the active sponsorship packages with how many are sold and left (`include_archived=true` adds archived ones).

## Errors

Errors use `{ error, message, details }`. The ones to handle:

| Status | `error` | Meaning |
| - | - | - |
| `400` | `VALIDATION_ERROR` | A malformed ID or body field; the message names it |
| `403` | `SCOPE_REQUIRED` | The token lacks the scope |
| `403` | `FORBIDDEN` | Registration or Expo is off for the event |
| `404` | `NOT_FOUND` | No such record on this event |
| `409` | `CONFLICT` | The order, status change or check-in is refused; see `details.reason` |
| `422` | `VALIDATION_ERROR` | The promo code is not valid for the order |
| `429` | — | Rate limited; wait `Retry-After` seconds. See [Rate Limiting](https://apidocs.sessionboard.com/rate-limiting.md) |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.