---
title: "Every open alarm in a scope, oldest first, stably paged (the rail's open list)"
method: GET
path: "/events/open-alarms"
tags: ["events"]
---

# Every open alarm in a scope, oldest first, stably paged (the rail's open list)

`GET /events/open-alarms`

## Request

- Base URL: `https://api.verdun.ai` (the document declares no server; this is the origin it was published from)
- URL: `https://api.verdun.ai/events/open-alarms`
- Auth: HTTP bearer

Pages through EVERY open alarm (the queue's own membership) at one site, optionally narrowed to `camera_id`s, oldest first on `(created_at, id)` with an opaque `cursor`. Unlike `GET /operator/events/all` (newest first, offset-paged, 1,000 by default) an old urgent alarm can never sit beyond the first page, and closes or arrivals between pages cannot duplicate or skip rows. Page 1 pins `snapshot.as_of`; every page of the pass bounds membership by CREATION time (`created_at <= as_of`, immutable per alarm) and by current membership at that page's own read (still open, unexpired, queue-visible). An alarm that was open at `as_of` and still is when its page runs is therefore always returned, even if a late AI verdict re-stamps its `queued_at`. Alarms created after `as_of` are the push stream's and the next pass's. Late visibility: an alarm created at or before `as_of` that only becomes visible to the queue after it (a late verdict or failsafe flip) is returned if its key is still ahead of the cursor, otherwise it is the push stream's and the next pass's. `status` is `more` (continue), `complete` (last page; complete as of `as_of` under these rules) or `truncated` (the pass ceiling was hit; `omitted` is the count of eligible alarms after the last returned one at that read, at least that many when `omitted_capped`). `counts` are PAGE-ONE observations: the scalar totals (`scope_total`, `site_total`), the exact overdue counts by `age_marks` and `oldest_queued_at` repeat unchanged from page one on every later page, while `by_camera` is returned on page one only (null afterwards). The rows returned over a pass can exceed the totals (late-visible alarms) or fall short of them (closes). Each row carries `queued_at` (age = `snapshot.server_time - queued_at`) and its live `claim`. A 200 is always a true statement; anything else (404 unknown or out-of-scope site or any invisible camera, one answer; 403 customer roles; 422 `CURSOR_INVALID` for an invalid cursor, 422 `CURSOR_EXPIRED` for an expired one (restart from page 1); 503 deadline or database failure) is UNKNOWN, never an empty list. Central staff only, tenant-scoped in SQL on every page. Read-only: it stamps no first view and takes no claim. Capability: `open_alarms_paged_read` on `GET /auth/me`.

## Query parameters

- `site_id` string, uuid, required: The site
- `camera_id` string[], nullable: Narrow to these cameras (repeat the parameter; at most 50). Absent = the whole site.
- `limit` integer: Rows per page
- `cursor` string, nullable: `next_cursor` from the previous page; absent = a new pass. An invalid cursor is 422 `CURSOR_INVALID`, an expired one 422 `CURSOR_EXPIRED`
- `age_marks` string, nullable: Page 1: comma list of 1 to 3 overdue marks in seconds for `counts.older_than` (default `30,60`). Later pages retain page one's counters; this parameter may be omitted and does not change them.

## Response `200`

Successful Response

- OpenAlarmsResponse
  - `status` 'more' | 'complete' | 'truncated', required: `more`: this page is fine and `next_cursor` continues the pass. `complete`: the last page; nothing after it in scope as of `snapshot.as_of`. `truncated`: the pass hit `limits.pass_ceiling`; the eligible rows counted in `omitted` will NOT be returned (the oldest are always the ones returned).
  - `order` 'oldest_first': Page order is `(created_at, id)` ascending; sort loaded rows by `(queued_at, id)`
  - `snapshot` PagedSnapshot, required
    - `as_of` string, date-time, required: The snapshot instant, pinned on page 1 and identical on every page of the pass. Only rows created at or before it are returned; a row created later, or one that only becomes visible to the queue after it, is the push stream's and the next pass's (it may still appear if its key is ahead of the cursor). `complete` means complete AS OF this instant.
    - `server_time` string, date-time, required: The database clock when this page was read. Age = server_time - queued_at, no workstation clock.
  - `scope` PagedScope, required
    - `site_id` string, required: The site read
    - `camera_ids` string[], nullable: The cameras read, or null for every camera at the site
    - `kind` 'cameras' | 'site', required: `cameras` when narrowed by `camera_id`, else `site`
  - `items` OpenAlarmItem[], required: This page's open alarms, oldest first
    - `event_id` string, required: The alarm's event id: the only identity of an alarm
    - `camera_id` string, required: The camera that raised the alarm
    - `camera_name` string, nullable: The camera's label
    - `zone` string, nullable: The camera zone the motion was in, when known
    - `site_id` string, required: The site the camera belongs to
    - `created_at` string, date-time, required: Alarm time; the paging key with `event_id`
    - `occurred_at` string, date-time, nullable: The ingestor-reported motion time, when sent
    - `queued_at` string, date-time, required: When the alarm entered the operator queue: the same instant the queue row carries
    - `summary` string, nullable: The AI summary of the clip, when one exists
    - `claim` object, nullable: The queue row's `claim` payload (`operator_id`, `operator_name`, `claimed_at`, `expires_at`, `expires_in_seconds`) while someone holds the alarm, else null. Own vs foreign: compare `operator_id` with the caller's id.
    - `first_viewed` object, nullable: Who first opened the alarm (`operator_id`, `operator_name`, `viewed_at`), else null
  - `next_cursor` string, nullable: Opaque; send it back with the same parameters. Null on the last page. An invalid cursor is 422 `CURSOR_INVALID`, an expired one 422 `CURSOR_EXPIRED`
  - `counts` OpenAlarmCounts, required
    - `scope_total` integer, required: Open alarms in the requested scope created by `as_of`, counted when page 1 was read. Rows returned over the pass can exceed it by alarms that became visible mid-pass and fall short of it by alarms closed mid-pass.
    - `site_total` integer, required: Open alarms at the whole site, counted the same way as `scope_total`
    - `older_than` object, required: In scope: alarms queued at least N seconds before `as_of`, per `age_marks` value (exact, by queued_at)
    - `oldest_queued_at` string, date-time, nullable: In scope: the oldest `queued_at`, or null when none
    - `by_camera` object, nullable: Whole-site open count per camera. PAGE 1 ONLY (null on later pages)
  - `limits` PagedLimits, required
    - `page_default` integer, required: Rows per page when `limit` is not sent
    - `page_max` integer, required: Largest `limit` accepted
    - `pass_ceiling` integer, required: Most rows one pass returns; beyond it the last page is `truncated` and `omitted` says how many eligible rows remained
  - `omitted` integer, nullable: Set only when `truncated`: the eligible rows after the last returned key at this read (the page's own filters and keyset), at least this many when `omitted_capped`. A read-time count, not page 1's total minus the rows returned.
  - `omitted_capped` boolean: True when the bounded remainder count hit its cap (10,000): `omitted` is then a lower bound. Always false unless `truncated`.

## Other responses

- `422`: `CURSOR_INVALID` for an invalid cursor (malformed, forged, or not belonging to this request), `CURSOR_EXPIRED` for an expired one (restart from page 1), or `INVALID_PARAMETER`
- `503`: Deadline or database failure (code PAGED_READ_UNAVAILABLE). Retry; never an empty list.

---

[API](https://skmtc.dev/verdun/apis/verdun-api.md) - [All operations](https://skmtc.dev/verdun/apis/verdun-api/llms.txt) - [OpenAPI document](https://skmtc.dev/verdun/apis/verdun-api/revisions/35a2a6ebedda?raw)
