REST API

Arrivals

Read-only feed of every check-in, check-out, undo and rejected scan, with the station, device and staff member behind each one.

An arrival is one record of a credential being presented and ruled on: a guest checked in at a gate, a pass rejected outside its valid window, a check-in undone by a supervisor. Each row carries when it happened, what the outcome was, and which station, device and staff member handled it.

This is the endpoint to use for live attendance, gate-by-gate breakdowns, and keeping an external system in step with check-ins. Arrivals are also reachable nested on a ticket via GET /tickets?include=scans, but that relation is keyed scans for historical reasons and cannot be filtered or paged independently.

Arrivals are read-only. Check-ins are written with PATCH /tickets/{id}/check_in.

The collection is not filtered for you

GET /arrivals returns every row, including rejected scans, check-outs and undos, in the same way that /orders returns refunded orders. The action field carries the outcome. Nothing is hidden by default, including archived arrivals.

Three groupings are worth knowing:

Meaning action values
A person was admitted in, pickup, entitlement
An admission was cancelled revalidated
Everything else rejections, check-outs, and diagnostic rows

A net attendance count is the number of admissions minus the number of cancellations. A revalidated row carries reverses_arrival_id, pointing at the arrival it cancels, so you can settle the count against the correct day rather than the day someone pressed undo.

Default response fields

id, created_at, updated_at, scanned_at, action, source, data, message, use_count, ticket_id, event_id, station_id, device_id, staff_id, reverses_arrival_id.

{
  "id": 88213045,
  "created_at": "2026-08-14T14:22:07.481Z",
  "updated_at": "2026-08-14T14:22:07.481Z",
  "scanned_at": "2026-08-14T14:22:07.480Z",
  "action": "in",
  "source": "lookup",
  "data": "9f2c41ab",
  "message": "",
  "use_count": 1,
  "ticket_id": 41902277,
  "event_id": 913044,
  "station_id": 1040,
  "device_id": 66132,
  "staff_id": 24518,
  "reverses_arrival_id": null
}

scanned_at is when the guest was admitted, as reported by the device that admitted them. created_at is when our servers stored the record. The two are the same for an arrival recorded online, and differ when a device was offline at the time — see keeping an external system up to date below.

Sort on scanned_at when you want the order guests arrived in. created_at follows the order we received the records, which for a device that spent time offline is not the same thing.

station_id, device_id and staff_id resolve through Stations, Devices and Staff.

station_id is nullable. A station is only recorded when the scanning app knows which one it is: Shopify POS assigns one automatically per location, while the iPad scanner and web check-in record null unless a station was chosen. If you are breaking attendance down by gate, count the nulls rather than assuming they are zero.

source describes how the credential was read: camera for a device camera, laser for a barcode scanner, and lookup when there was no scan at all — someone found the guest in a list and checked them in, which is what a web check-in, a bulk check-in and a check-in through this API always are.

data is the raw barcode or search term submitted, and is null when a check-in was performed by tapping a name in a list rather than scanning.

The event, station, device and staff relationships are returned only when listed in ?include=.

ticket behaves differently for historical reasons: on GET /arrivals it is returned only when included, but on GET /arrivals/{id} the full ticket is embedded by default. Use ?fields= if you want to trim it.

List arrivals

GET /arrivals

List today’s arrivals at one gate

curl -X GET \
  'https://app.guestmanager.com/api/public/v2/arrivals?filter[station_ids]=1040&filter[scanned_at][from]=2026-08-14&page[cursor]=&page[size]=100' \
  -H 'Authorization: Token abcdefg' \
  -H 'Content-Type: application/json'

Request parameters

Parameter Type Description
page[cursor] string Opaque cursor token. Send empty for the first page. See Pagination.
page[size] integer Records per page. Default 10, maximum 100.
sort[{field}] string Sort by id, created_at, updated_at, scanned_at. Value is asc or desc.
include string Comma separated: ticket, event, station, device, staff.
filter[scanned_at][from] date Arrivals at or after this time.
filter[scanned_at][to] date Arrivals at or before this time.
filter[action] string One action value, e.g. in.
filter[station_ids] integer Station ID.
filter[device_ids] integer Device ID.
filter[staff_ids] integer Staff ID.
filter[event_ids] integer Event ID.
filter[ticket_ids] integer Ticket ID.
filter[source] string lookup, camera or laser.
filter[is_successful] boolean 1 for successful operations only, 0 for rejections only.
filter[archived] string unarchived excludes archived arrivals, archived returns only those.

Get arrival

GET /arrivals/{id}

curl -X GET \
  'https://app.guestmanager.com/api/public/v2/arrivals/88213045?include=station,staff' \
  -H 'Authorization: Token abcdefg' \
  -H 'Content-Type: application/json'

Keeping an external system up to date

Page on updated_at, not on scanned_at. Sort by sort[updated_at]=asc, walk with page[cursor], and remember the updated_at of the last row you stored. On the next run, resume from there.

This matters more than it looks. scanned_at is reported by the scanning device, not by our servers. A scanner that loses connectivity keeps admitting guests offline and uploads the backlog when it reconnects, so arrivals appear stamped with the time they happened, which may be hours in the past. If you page on scanned_at, those rows land behind a cursor you have already passed and you will never see them, with no error to tell you so.

updated_at is set by our servers when the row is stored, so a late upload still sorts after everything you have already collected. It also moves when an arrival is changed, so undos and corrections come back around too.

For the same reason, treat an arrival you have already stored as mutable: match on id and update in place rather than inserting blindly.

One thing this feed cannot tell you is that an arrival was deleted. Deleted arrivals simply stop appearing, with no tombstone and no final updated_at change, so a long-lived mirror can drift if staff remove rows from the dashboard. If exact totals matter, re-walk a closed date range periodically and reconcile against what you hold, rather than trusting an append-only copy indefinitely.