---
title: "Live alarm claims in the caller's scope (claims-only read)"
method: GET
path: "/events/claims"
tags: ["events"]
---

# Live alarm claims in the caller's scope (claims-only read)

`GET /events/claims`

## 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/claims`
- Auth: HTTP bearer

What a console refetches on the `claims_changed` stream message instead of the whole queue. The live claims (`operator_id`, `operator_name`, `claimed_at`, `expires_at`, `expires_in_seconds`) on queued alarms in the caller's tenant scope, keyed by event id, with the queue's own membership predicate. An alarm with no live claim is absent, so for the alarms the request covers absence means free. Optional `site_id` and repeated `event_ids` (at most 500) narrow what is covered. Central staff only: customer-side callers get an empty map and never staff identity. `activity` (claim-lock R2) is a sibling map of escalation activity per event id (a valid intent and/or live calls, derived from the session records); it is NOT inside `claims` because activity outlives the claim. A keyed read (`event_ids`) covers every queued alarm named, an unfiltered or `site_id`-only read the alarms with a live claim; empty while `CLAIM_ESCALATION_ENABLED` is off. Requires claim participation (`review_label_alarms`, or, for central staff, any of `escalate_voice_down`, `escalate_dispatch`, `notify_property_owner`: whoever the claim refusal can refuse). Malformed ids are 404; also 404 while `CLAIM_CLEANUP_ENABLED` is off (read `claim_cleanup_enabled` on `GET /auth/me` first).

## Query parameters

- `site_id` string, nullable: Only alarms at this site.
- `event_ids` string[], nullable: Only these alarms (repeat the parameter); at most 500 IDs to bound a single explicit filter. This cap does not limit an unfiltered response's size.

## Response `200`

Successful Response

- ListClaimsResponse
  - `claims` object, required: Live claim per event id for the queued alarms the caller may see (their tenant scope, the queue's own membership predicate), optionally narrowed by `site_id` / `event_ids`. An alarm with no live claim is ABSENT: for every alarm the request covers, absence means nobody holds it. Empty for customer-side callers, who cannot read staff identity.
  - `activity` object: Escalation activity per event id (claim-lock R2): a valid escalation intent (Voice Down opened, Escalate - dispatch picked) and/or live calls, talk-downs and dispatches derived from the session records. NOT inside `claims`: activity outlives the claim (a call keeps going after a lapse or a take-over), so an alarm can be in `activity` without being in `claims`. A keyed read (`event_ids`) covers every queued alarm named; an unfiltered or `site_id`-only read covers the alarms with a live claim. Empty while CLAIM_ESCALATION_ENABLED is off and for customer-side callers.
  - `reservations` object: Claim-lock R5 (Focus): the alarms reserved for a focuser, per event id. A reservation is NOT a claim: it has no TTL, it lives under the focuser's ordinary claim and outlives it, and an alarm can be here with or without being in `claims` (while a live claim exists the console shows the claim, afterwards the reservation). A keyed read covers every queued alarm named; an unfiltered or `site_id` read adds the alarms that carry a valid reservation. Empty while FOCUS_ENABLED is off and for customer-side callers.
  - `focus` union[]: Claim-lock R5 (Focus): the active focus records in the caller's tenant scope (narrowed to `site_id` when given): who is focusing. Empty while FOCUS_ENABLED is off.
    - union
      - FocusRecord: One Focus generation (`id` IS the generation: a restart is a new id).
        - `id` string, required: The focus generation id; a restart is a new id.
        - `site_id` string, required: The site the focus is on.
        - `scope` FocusScopeOut, required
          - `kind` 'site' | 'camera', required: Whether the focus covers the whole site or one camera.
          - `camera_id` string, nullable: Set iff `kind` is `camera`.
        - `operator_id` string, required: The focuser.
        - `operator_name` string, nullable: Their display name; null if the operator no longer exists.
        - `started_at` string, required: ISO 8601, UTC.
        - `last_input_at` string, required: Last accepted meaningful input or Keep (ISO 8601, UTC).
        - `check_due_at` string, required: `last_input_at` + FOCUS_CHECK_IDLE_SECONDS: when the console asks "Still focusing?".
        - `check_deadline` string, required: `check_due_at` + FOCUS_CHECK_ANSWER_SECONDS: with no explicit Keep by then the server releases the focus (and treats it as released at once, before any sweep).
        - `check_pending` boolean, required: True while the check is due and unanswered (active records only).
        - `check_due_in_seconds` integer, required: Seconds until `check_due_at`, computed server-side: time it from this against the local clock at receipt.
        - `check_deadline_in_seconds` integer, required: Seconds until `check_deadline`, computed server-side.
        - `state` 'active' | 'released', required: `active` while reserving alarms; `released` once it has ended.
        - `released_at` string, nullable: When the focus was released (ISO 8601, UTC); null while active.
        - `release_reason` 'manual' | 'check_timeout' | 'disabled' | 'scope_lost', nullable: Why it was released; null while active.
      - StoryHoldRecord: One site hold (`origin = "story"`, Site Story step 3): one operator owns the site while its story is open. `id` is the hold; `lineage_id` is shared by a Take over's successor, which is how the held alarms stay held for the taker with no gap.
        - `origin` 'story', required: Always `story`.
        - `id` string, required: The hold id (a site Take over creates a successor with a new id in the same lineage).
        - `lineage_id` string, required: Shared by a hold and its Take over successors.
        - `site_id` string, required: The site the hold is on.
        - `scope` FocusScopeOut, required
          - `kind` 'site' | 'camera', required: Whether the focus covers the whole site or one camera.
          - `camera_id` string, nullable: Set iff `kind` is `camera`.
        - `operator_id` string, required: The holder.
        - `operator_name` string, nullable: Their display name; null if the operator no longer exists.
        - `started_at` string, required: ISO 8601, UTC.
        - `last_seen_at` string, nullable: The holder's last heartbeat (ISO 8601, UTC).
        - `left_at` string, nullable: Set by the holder's best-effort leave signal, cleared by a heartbeat inside the grace. After a clean leave the hold returns its alarms about 30 s after this instant (a fixed server constant), or sooner on its idle or new-alert deadline.
        - `open` boolean, required: True while the holder's page is open: no clean leave and a heartbeat within `open_s`. While true there is no deadline of any kind.
        - `return_at` string, nullable: When the unattended hold returns its alarms to the queue (ISO 8601, UTC); null while `open`. The earliest of the idle deadline, the new-alert deadline and (after a clean leave) 30 s after `left_at`, pushed later by a live call. A display, never a decision.
        - `now` string, required: The server clock this record was built against: compute `return_at - now` once and tick it locally.
        - `serious_unopened` integer: Serious alarms the current holder has not opened (visibility only).
        - `oldest_serious_queued_at` string, nullable: When the oldest of those entered the queue (ISO 8601, UTC).
        - `open_seconds_total` integer: Seconds the holder's page was open during the hold (each interval since the last heartbeat, capped at `open_s`, settled at the next heartbeat, a leave or the release).
        - `idle_s` integer, required: Frozen at start: seconds since the last heartbeat before an unattended hold returns. The backstop for a page that vanished without a clean leave; after a clean leave the hold returns about 30 s later instead (a fixed server constant, not frozen).
        - `new_alert_s` integer, required: Frozen at start: seconds after a new alert before an unattended hold returns.
        - `open_s` integer, required: Frozen at start: seconds since the last heartbeat within which the page still counts as open.
        - `state` 'active' | 'released', required: `active` while it holds the site.
        - `released_at` string, nullable: ISO 8601, UTC; null while active.
        - `release_reason` 'idle_2min' | 'new_alert_2min' | 'holder_gone' | 'submit' | 'taken_over' | 'flag_off' | 'scope_lost' | 'disabled', nullable: Why it ended; null while active. `idle_2min` is the unattended return after a clean leave (about 30 s after it, or the idle deadline if that came first; the name is historical, not a measured 2 minutes); `holder_gone` is the idle deadline with no clean leave.

## Other responses

- `422`: Validation Error

---

[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)
