REST API reference
The Listo Sessions API exposes a single endpoint that returns the same
screenings Listo published to that tenant's queue, paginated by time.
The endpoint is scoped to one tenant per call via the {tenantId} path
segment.
The Sessions API returns bare screening objects — the screening
payload directly under each items[i], with no CloudEvent envelope
wrapper. The Service Bus delivery of the same screenings does carry a
CloudEvent envelope; see Differences from the Service Bus
payload below.
https://api.<environment> is a placeholder. Use the API base URL
delivered to you at onboarding.
Base URL
https://api.<environment>
Authentication
All endpoints require an OAuth 2.0 bearer token obtained via the
client_credentials grant. See Obtaining tokens.
Authorization: Bearer <access_token>
Calls without a valid token return 401 Unauthorized.
Versioning
The URL prefix /v1 is the API major version. Breaking changes ship
under a new prefix (/v2); new optional fields may appear in the
response within /v1 without a bump. See
Versioning & compatibility.
GET /v1/tenants/{tenantId}/screenings
Returns a page of completed screenings for the tenant in the path whose
testTime falls within [from, to).
GET /v1/tenants/{tenantId}/screenings?from={iso8601}&to={iso8601}&pageSize={n} HTTP/1.1
Host: api.<environment>
Authorization: Bearer <token>
Accept: application/json
Path parameters
| Parameter | Type | Required | Notes |
|---|---|---|---|
tenantId | string (UUID) | yes | The tenant whose screenings to return. Must equal the bearer token's tenant_id claim — see Obtaining tokens. Each token is scoped to exactly one tenant; a different {tenantId} returns 403 forbidden. |
Query parameters
| Parameter | Type | Required | Default | Notes |
|---|---|---|---|---|
from | RFC 3339 date-time | yes | — | Inclusive lower bound on testTime. Any RFC 3339 offset is accepted (Z or e.g. +02:00); the server compares on the absolute instant. |
to | RFC 3339 date-time | yes | — | Exclusive upper bound on testTime. Must be ≥ from. Same offset rules as from. |
pageSize | integer | no | 100 | Range 1–1000. Values outside the range return 400 page_size_out_of_range. |
from_id | integer | no | — | Integer id of the last item from a previous page, used as a tie-breaker for screenings sharing the same testTime. When absent, from is inclusive on testTime. When present, the page begins strictly after (from, from_id). |
The time range is bounded by testTime (when the screening was
performed on the iPad), not by when the screening was persisted or
delivered.
The endpoint enforces to >= from. Setting from equal to to is
allowed, but always returns an empty items array — to is exclusive,
so a zero-width range matches no screenings.
Response
200 OK
HTTP/1.1 200 OK
Content-Type: application/json
Link: <https://api.<environment>/v1/tenants/0d3e2b51-4e7a-44b6-8d5e-1c3a9b2f7c40/screenings?from=2026-05-08T09:30:00Z&from_id=1138291&to=2026-05-08T18:00:00Z&pageSize=100>; rel="next"
{
"items": [
{
"id": 1138291,
"tenantId": "0d3e2b51-4e7a-44b6-8d5e-1c3a9b2f7c40",
"testTime": "2026-05-08T11:25:33+02:00",
"...": "see /events/screening-completed for the full payload"
}
/* … up to pageSize items, ordered by (testTime, id) ascending … */
],
"links": {
"next": "https://api.<environment>/v1/tenants/0d3e2b51-4e7a-44b6-8d5e-1c3a9b2f7c40/screenings?from=2026-05-08T09:30:00Z&from_id=1138291&to=2026-05-08T18:00:00Z&pageSize=100"
}
}
Every screening in items has tenantId equal to the {tenantId} in
the request path — the API does not mix tenants on a single response.
Response fields
| Field | Type | Notes |
|---|---|---|
items | array of screening objects | Up to pageSize screenings. May be empty. |
items[i] | object | A screening object — see Screening completed and Data schema reference for the full schema. The same fields the Service Bus delivery carries inside its CloudEvent data payload, returned here directly without an envelope wrapper. |
items[i].id | integer | The screening's database id — Listo's stable primary key for this session. Use this for application-level idempotency. |
links | object | Always present, even on the last page. |
links.next | string (URL) | Present only when more pages exist. Absent on the final page. |
Link header
The Link HTTP header is provided in addition to links.next, following
RFC 8288. The two carry
the same URL and are equivalent — pick whichever is more convenient for
your HTTP client.
Link: <https://api.<environment>/v1/tenants/{tenantId}/screenings?from=...&from_id=...&to=...&pageSize=...>; rel="next"
The header is omitted on the final page (just like links.next).
Error responses
| HTTP | code | When |
|---|---|---|
400 | invalid_request | Malformed query parameter. |
400 | invalid_time_range | to < from, or from/to not valid RFC 3339. |
400 | page_size_out_of_range | pageSize outside 1–1000. |
401 | unauthenticated | Missing or invalid bearer token. |
403 | forbidden | Token valid but not authorized for the tenant in the URL path — {tenantId} is not in the token's tenant_id claim. |
404 | not_found | The tenant in the URL path is not onboarded for the Sessions API. |
429 | rate_limited | Rate limit exceeded. The response carries a Retry-After header. |
5xx | internal_error / service_unavailable | Retry with exponential backoff. |
Differences from the Service Bus payload
The Sessions API returns the screening payload directly. Service Bus carries the same payload inside a CloudEvent envelope (see Envelope). The fields and values inside the screening are the same on both transports — same field names, same field values. The difference is only the envelope wrapper, which is present on Service Bus and absent on REST.
Application-level deduplication keys on id (the screening's database
id) on both transports. See Delivery
semantics for the
matching guidance from the queue side.
Ordering
Within a page, items are ordered by testTime ascending (and by id
when two share the same testTime). Following links.next to the end
returns every screening in your time window exactly once, in that same
order. Don't change from/to mid-walk — keep following the URLs we
hand you.