---
title: "Take over an alarm another operator is working"
method: POST
path: "/events/{event_id}/claim/take-over"
tags: ["events"]
---

# Take over an alarm another operator is working

`POST /events/{event_id}/claim/take-over`

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

Claim-lock step 3a, ONE HOLDER PER ALARM. The holder works the alarm: a close (`POST /eval/operator/{id}` 409 `claim_held`, a per-item 423 in the bulk close) and the operator-started Voice down, AI voice announcement, call to the owner, dispatch call and event-scoped Live Talk are refused 409 `claim_held` while ANOTHER operator holds a live claim; anyone else uses this first. Open to every central-staff operator (owners included); not a priority tier. Body `{expected_holder_id, expected_claimed_at}` is the claim the console showed (echo `operator_id` and `claimed_at` from it). Under the event row lock, if that is still the live claim the claim moves to the caller (200 with the new claim, consoles are nudged so the displaced holder learns at once); otherwise nothing moves: 409 `claim_changed` with the CURRENT claim (or null when nobody holds it now), and the console reconfirms. Never retry a `claim_changed` automatically. Two operators taking over at once: exactly one wins. A closed, expired or non-queued alarm is a plain 409. Taking over a claim the caller already holds is 200 and writes nothing. Take over transfers no draft: the displaced operator's notes stay on their own screen. Reserved shape (Focus, `expected_focus_id` with no `expected_claimed_at`): a live claim appearing meanwhile is 409 `claim_changed` with that claim, a reservation that is gone or changed is 409 `claim_changed` with `claim` null (and the current `reservation`, if any), and Focus being off is 404 (after the scope and staff checks below); a body that names neither `expected_claimed_at` nor `expected_focus_id` is 422 (as is any schema-invalid body), except that with Focus on an alarm reserved for another operator's focus with no live claim answers 409 `claim_changed` with the current `reservation`. 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); non-staff callers get 403. 404 while `CLAIM_REFUSAL_ENABLED` is off (or the api predates this): read `claim_refusal_enabled` on `GET /auth/me` first.

## Path parameters

- `event_id` string, required

## Request body

- TakeOverClaimRequest: Body of `POST /events/{id}/claim/take-over`: the claim the caller SAW.
  - `expected_holder_id` string, uuid, required: `operator_id` of the claim the console showed. The take-over moves the claim only if this operator still holds it.
  - `expected_claimed_at` string, date-time, nullable: `claimed_at` of the claim the console showed (ISO 8601, echoed from the claim payload). Must equal the live claim's `claimed_at` (to the millisecond), so a renewal between the console's read and the click answers 409 `claim_changed` and the operator reconfirms against the fresh holder. Omit it only in the Focus reserved shape (together with `expected_focus_id`), to take over an alarm RESERVED for another operator's focus that has no live claim: there is nothing to echo, and it is ignored. In the reserved shape a live claim that appeared meanwhile is 409 `claim_changed` carrying that claim, a reservation that is gone or the caller's own is 409 `claim_changed` with `claim` null, and Focus being off (`FOCUS_ENABLED` off) is 404. A body that names neither `expected_claimed_at` nor `expected_focus_id` is 422 (like any schema-invalid body), except that with Focus on an alarm reserved for another operator's focus with no live claim answers 409 `claim_changed` with the current `reservation`.
  - `expected_focus_id` string, uuid, nullable: Claim-lock R5 (Focus). The `reservation.focus_id` the console showed. Required to take over an alarm that is reserved for another operator's focus and has no live claim (together with `expected_holder_id` = the focuser); otherwise the answer is 409 `claim_changed` with the current `reservation`. When `expected_claimed_at` is omitted and a live claim exists, it is not compared: the answer is 409 `claim_changed` carrying that claim.

## Response `200`

Successful Response

- ClaimEventResponse
  - `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).
  - `renewed` boolean, nullable: Present only on `POST /events/{id}/claim/renew`: true when the caller's own live claim was renewed (`claim` is then the renewed claim), false when it was NOT (the claim was lost or never held: `claim` is another operator's live claim, or null). A renew never acquires. Absent on an ordinary claim.
  - `reservation` Reservation: An alarm reserved for a focuser or held by a site hold. A reservation is NOT a claim: it has no TTL and lives under the claim.
    - `origin` 'focus' | 'story': `story` for a site hold. A reservation from an older api has no `origin`: it is a Focus.
    - `focus_id` string, required: The focus generation or site hold that reserved this alarm.
    - `operator_id` string, required: The focuser or holder.
    - `operator_name` string, nullable: Their display name; null if the operator no longer exists.
    - `reserved_at` string, required: When the alarm entered the queue and was reserved (ISO 8601, UTC).
    - `elapsed_seconds` integer, required: Server-computed now - reserved_at, for the age display; the unreviewed-age cue applies to reserved alarms exactly as to free ones.
    - `engaged` boolean, nullable: Story only: the current holder has opened this alarm.
    - `open` boolean, nullable: Story only: the holder's page is open (no deadline while it is).
    - `return_at` string, nullable: Story only: when the unattended hold returns its alarms (ISO 8601, UTC); null while `open`. After a clean leave, about 30 s after it unless an earlier idle or new-alert deadline or a live call changes it.
    - `now` string, nullable: Story only: the server clock `return_at` is measured against.
    - `serious_unopened` integer, nullable: Story only: serious alarms the holder has not opened (visibility only).
    - `oldest_serious_queued_at` string, nullable: Story only: when the oldest of those entered the queue.

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