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.