---
title: "Read who is working a queued alarm (no claim is taken)"
method: GET
path: "/events/{event_id}/claim"
tags: ["events"]
---

# Read who is working a queued alarm (no claim is taken)

`GET /events/{event_id}/claim`

## 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/{event_id}/claim`
- Auth: HTTP bearer

Read-only twin of `POST /events/{id}/claim`, for a console opening an alarm's deep link before its queue snapshot has loaded: it never takes, renews or clears a claim and sends no nudge. Same body as the POST: the live claim (`operator_id`, `operator_name`, `claimed_at`, `expires_at`, `expires_in_seconds`) whoever holds it, or `claim: null` when nobody does (free, or the alarm has left the active queue). Customer-side callers always get null and never staff identity. 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) and tenant scope (out of scope is 403, unknown or malformed id is 404). Also 404 while `CLAIM_CLEANUP_ENABLED` is off; consoles read `claim_cleanup_enabled` on `GET /auth/me` first, and treat any error here as unknown, never as free. `activity` (claim-lock R2) is the escalation activity on this alarm (a valid intent and/or live calls), present even when `claim` is null because activity outlives the claim; the `activity` key is omitted while `CLAIM_ESCALATION_ENABLED` is off.

## Path parameters

- `event_id` string, required

## Response `200`

Successful Response

- ClaimStatusResponse
  - `claim` AlarmClaim, required
    - `operator_id` string, required: The operator holding the alarm
    - `operator_name` string, nullable: Their display name; null if the operator no longer exists
    - `claimed_at` string, required: When the claim was taken (ISO 8601, UTC)
    - `expires_at` string, required: When the claim lapses (ISO 8601, UTC)
    - `expires_in_seconds` integer, required: Seconds left on the claim, computed server-side. Time the claim from this against the local clock at receipt, not from `expires_at`, so a skewed workstation clock does not move it.
    - `generation` string, nullable: Ownership generation (a UUID): it changes whenever the claim's HOLDER changes (acquire, take-over) and never on a renewal. Echo it on `POST /events/{id}/claim/acting` and its cancel; a write carrying an old one is rejected. Null while CLAIM_ESCALATION_ENABLED is off, or on a claim stamped before generations existed (the holder's next renew fills it in).
  - `activity` ActivityPayload
    - `state` 'preparing' | 'active', required: `active` iff at least one live session record exists (operator or automated). Otherwise `preparing`: a valid intent and no session yet.
    - `operator_acting` boolean, required: True iff a valid intent exists or a live session is attributed to an operator. The console shows no red cue while this is true; the age stays visible either way. False means only an automated agent call is live.
    - `since` string, required: `preparing`: the intent's set_at. `active`: the earliest live session start (ISO 8601, UTC)
    - `elapsed_seconds` integer, required: Server-computed now - since, for the age display; time it from this against the local clock at receipt
    - `action` 'voice_down' | 'dispatch', required: Of the primary source
    - `actor` ActivityActor, required
      - `operator_id` string, nullable: The operator acting. Null for an automated agent call.
      - `operator_name` string, nullable: Their display name; null if the operator no longer exists or kind is agent.
      - `kind` 'operator' | 'agent', required: `agent` is an automated AI call: it never counts as an operator acting.
    - `intent` ActivityIntent
      - `action` 'voice_down' | 'dispatch', required: What the holder opened: the Voice Down panel, or Escalate - dispatch.
      - `set_at` string, required: When the holder opened Voice Down / picked dispatch (ISO 8601, UTC)
      - `operator_id` string, required: The operator who recorded the intent (the claim holder when it was set)
      - `operator_name` string, nullable: Their display name; null if the operator no longer exists
      - `generation` string, required: The ownership generation the intent is bound to
    - `sessions` ActivitySession[]: Live records, oldest first, at most five. Empty while `preparing`.
      - `source` 'voice_session' | 'voice_agent_call' | 'external_talkdown' | 'dispatch_call', required: Which session table the live record comes from.
      - `session_id` string, required: The id of that session record
      - `action` 'voice_down' | 'dispatch', required: `dispatch` is the grouping for every dispatch_call row; `target` says which.
      - `target` 'police' | 'guard' | 'owner', nullable: Dispatch rows only: who was dialled. An owner call is activity but is not police dispatch.
      - `status` string, required: The source's own status word (opening/live, placing/connected, requested/connected, the dispatch status)
      - `started_at` string, required: When the record started being live (ISO 8601, UTC); the source's own start time
      - `actor` ActivityActor, required
        - `operator_id` string, nullable: The operator acting. Null for an automated agent call.
        - `operator_name` string, nullable: Their display name; null if the operator no longer exists or kind is agent.
        - `kind` 'operator' | 'agent', required: `agent` is an automated AI call: it never counts as an operator acting.
  - `reservations` object, nullable: Claim-lock R5 (Focus): the reservation on this alarm, keyed by event id (empty when it is not reserved). The key is omitted entirely while FOCUS_ENABLED is off.
  - `focus` union[], nullable: Claim-lock R5 (Focus): the active focus records at this alarm's site. Omitted 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/a19366be50a3?raw)
