cameras

Raise Operator Alarm

Changed on
post/cameras/{camera_id}/operator-alarm

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/cameras/{camera_id}/operator-alarm
  • Auth
    HTTP bearer

Path parameters

camera_idstring required

Request body

incident_type'INTRUSION' | 'TAILGATING' | 'FIGHTING' | 'ARMED_ROBBERY' | 'VEHICLE_THEFT' | 'FIRE' | 'LOITERING' | 'PACKAGE_LEFT' | 'MAINTENANCE' | 'DELIVERY' | 'MULTI_PERSON' | 'LOW_LIGHT_ACTIVITY' | 'HEIGHTENED_ACTIVITY' | 'PERSON_DOWN' | 'NORMAL' | 'UNKNOWN' required
notestring nullable

Optional free text, trimmed; empty becomes null. Stored as raised_note and shown beside the alarm, never in the AI-rationale slot.

idempotency_keystring nullable

Optional client-minted key. A retry carrying the same key from the same operator returns the original alarm (existing=true) instead of raising another.

Example request

{
  "note": "Two people at the north gate, one carrying a crowbar",
  "idempotency_key": "3f2b8e1c9d"
}

Response

The camera already had an open operator-raised alarm (or this replays an idempotency key): that alarm, with existing=true.

operator_evaluationobject nullable

The disposition on this event, if any (actor='agent' marks an agent auto-dismissal). Central-staff callers also get an optional operator_name (the closing operator's First Last) on a human row whose operator still exists and has a name; absent otherwise, and never present for a customer-side role.

interpretation_resultobject nullable

The AI verdict in the queue-payload shape (to_dict_frontend), incl. matched_exception, terminal_stage, gate_outcome and failsafe_sentinel (true on the fail-safe sweep's placeholder row, whose UNKNOWN is a stand-in, not a VLM verdict); null when no eval has landed. gate_outcome is {reason, detector_provider, detector_confidence} on an object_gate row and null otherwise — it is NOT coalesced, because null means 'no gate stop happened' rather than a missing value. reason is an open string: incident-intelligence owns the closed set and may add values, so never switch on it exhaustively. Also pre_floor_recommended_action (the VLM's own suggestion before policy: key absent when its provenance is unknown, null when the VLM made none) and assessment_context ({withheld_reason, withheld_provenance, detection_check}, written once at ingest; null on older rows). Both are stripped for every customer role (the curated roles and SITE_MANAGER); on a VLM fallback (backup) row only, the stripped assessment_context is replaced by the content-free marker {backup_row: true} so a customer-side composer can refuse to draft from that row's envelope text. For staff, a VLM fallback (backup) row's assessment_context.backup_assessment carries the backup model's own read in raw, except when that read names NORMAL, MAINTENANCE, DELIVERY or UNKNOWN: then raw is null, raw_withheld is true and raw_withheld_reason is backup_benign or backup_unknown (the stored row keeps the full read for audit). detection_stages and debug are deliberately not exposed.

agent_actionobject nullable

Newest agent action for this event (voice-down lifecycle states), if any

threadobject nullable

Incident-thread summary when this event is part of a cross-camera thread; GET /threads/{id} returns the full timeline

extended_clip_urlstring nullable

Presigned URL for the event's extended pre-roll ("Rewind") clip, top-level on the bundle rather than inside event because it is derived per request and has no column behind it. Null when the clip key does not derive one (demo/training/pre-cutover rows) or the presign failed. Presigned BLIND: a non-null URL does NOT mean the object exists — probe it (a 403/404 means the extended clip was never uploaded or has expired by the clips-extended/ 7-day lifecycle). Valid for the same 3600s as GET /events/{event_id}/clip-url, which returns a fresh pair.

poster_urlstring nullable

Presigned URL for the event's alert-frame JPEG (the poster), top-level on the bundle rather than inside event because it is derived per request and has no column behind it. Derived from the clip key exactly as the History row's poster_url is. Null when the clip key does not derive one (demo/training/pre-cutover rows) or the presign failed. Presigned BLIND: a non-null URL does NOT mean the object exists — probe it. Valid 3600s. A null here does NOT predict POST /events/{event_id}/notify-owner with attach_snapshot=true: that path HEADs the object and answers 409 SNAPSHOT_UNAVAILABLE, which stays authoritative.

continued_poster_urlstring nullable

Presigned URL for the continued clip's poster frame (JPEG), valid 3600s. Null unless the alarm has a committed continued clip (event.has_continued_clip) still inside the clip retention window, or when the presign failed. Presigned BLIND: the ingestor writes the poster best-effort, so the object may be absent; fall back on a load error.

talk_down_availableboolean nullable

Whether an operator's Talk Down would resolve a mapped, usable speaker for this event's camera, as of this read. A snapshot, not a delivery guarantee: the POST's 409 SPEAKER_NOT_MAPPED stays authoritative, and an absent value means 'unknown — treat as available'.

talk_down_available_as_ofstring date-time nullable

Database statement time of the query that read talk_down_available (UTC). Compare two snapshots of the same event by this value and keep the newer one. Null or absent means no database read time is available, including camera-less events.

ai_announcement_availableboolean nullable

Whether an operator-started AI announcement (POST /events/{event_id}/voice-agent-call) would resolve a mapped, usable, SIP-enabled speaker for this event's camera, as of this read. A snapshot, not a delivery guarantee: the POST's 409 SPEAKER_NOT_MAPPED stays authoritative, and an absent value means 'unknown - treat as available'.

ai_announcement_available_as_ofstring date-time nullable

Database statement time of the query that read ai_announcement_available (UTC); the same read as talk_down_available_as_of. Null or absent means no database read time is available, including camera-less events.

voice_agent_callobject nullable

The AI voice announcement placed for this event, if any: {id, created_at, status, speaker_label, connected_at, announced_at, outcome}. status is placing | connected | ended | failed; outcome is subject_left | subject_stayed | no_response | human_requested | unknown, or null while the call is live. Null when no announcement has been placed.

ai_feedback_requestedboolean nullable

Legacy, read-only: true when THIS event id falls in the deterministic 1-in-10 hash sample (app.services.ai_feedback_sampling.is_ai_feedback_sampled). NOT the ask rule: since v2 (2026-10-01) the AI-feedback question is shown, and ai_feedback_asked recorded, on SUSPICIOUS_MONITOR close-outs only, never on FALSE_ALARM or ESCALATE_*; no client reads this field for the ask. Kept for compatibility; removal is a follow-up. Absent/null on an older backend or a demo/pre-feature event.

existingboolean required

False: this call created the alarm (HTTP 201). True: the camera already had an open operator-raised alarm, or this was a replay of the same idempotency key, and this is that alarm (HTTP 200). Join it rather than raising another.

Example response

{
  "event": {
    "id": "UUID Of some sort",
    "kvs_stream_name": "dev-home-cam01",
    "zone": "A",
    "clip_s3_url": "https://s3.amazonaws.com",
    "summary": "Emergency!",
    "source": "rtsp_motion",
    "monitoring_state": "monitored",
    "continued_clip_seconds": 10,
    "raised_incident_type": "INTRUSION",
    "operator_alarm_capture": {
      "status": "pending",
      "reason": "buffer_expired"
    }
  },
  "camera": {
    "camera_label": "SD Sewing Company",
    "camera_id": "cam01",
    "kvs_stream_name": "dev-home-cam01",
    "ip_address": "192.0.0.1",
    "port": "554",
    "username": "admin",
    "map_location_label": "NE corner — loading dock",
    "map_heading": 225
  },
  "site": {
    "name": "San Diego Sewing Company - 1",
    "address": "1234 Hairy Cherry Lane",
    "city": "Shanghai",
    "state": "CA",
    "zip_code": "78912",
    "timezone": "America/Los_Angeles",
    "police_nearest_precinct_name": "SD Police hehe",
    "police_non_emergency_line": "1231231234",
    "police_direct_dispatch_line": "32133",
    "police_jurisdiction_notes": "This is the police",
    "alarm_license_number": "127.001463",
    "alarm_license_issuer": "Illinois",
    "mobile_guard_contract": true,
    "agent_context": "Gated storage facility; groundskeeper drives a white pickup weekday mornings.",
    "agent_thread_window_minutes": 5,
    "heightened_exceptions": [
      {
        "description": "Navy uniforms with badge",
        "label": "Uniformed security"
      }
    ],
    "holidays": [
      {
        "rule": "thanksgiving"
      },
      {
        "date": "2026-12-24",
        "label": "Christmas Eve half day"
      }
    ],
    "stage": "test",
    "onboarding_marks": {
      "first_night": "2026-10-13T08:00:00.000Z"
    },
    "map_mode": "geo",
    "map_lat": 42.0442,
    "map_lng": -87.9816,
    "floorplan_width": 1200,
    "floorplan_height": 800,
    "has_floorplan": true,
    "disarm_until": "2026-09-05T21:15:00Z",
    "disarm_set_at": "2026-09-05T19:45:00Z",
    "disarm_by": "sms:+18015550142",
    "disarm_active": true,
    "arm_until": "2026-09-18T04:30:00Z",
    "arm_set_at": "2026-09-18T04:00:00Z",
    "arm_by": "operator:6f1c8f2a-...",
    "arm_active": true,
    "disarm_max_minutes": 120,
    "has_disarm_passcode": true,
    "disarm_passcode_hint": "92",
    "monitoring_now": "monitored",
    "monitoring_next_change_at": "2026-09-08T04:30:00Z",
    "contacts": [
      {
        "first_name": "1231231234",
        "last_name": "1231231234",
        "role": "Security Manager",
        "primary_phone_number": "1231231234",
        "afterhours_phone_number": "1231231234",
        "email": "JJ@example.com",
        "availability": [
          {
            "days": [
              0,
              1,
              2,
              3,
              4
            ],
            "end": "17:00",
            "start": "08:00"
          }
        ]
      }
    ]
  },
  "parent": {
    "id": "<UUID of some sort>",
    "company_name": "Joe",
    "primary_contact_name": "Joe Johnson",
    "primary_contact_phone_number": "1013217890",
    "primary_contact_email": "JJ@example.com",
    "notes": "Super important notes"
  },
  "operator_alarm_capture": {
    "status": "pending",
    "reason": "buffer_expired"
  }
}

Changes

    • info

      added the optional property // to the response with the status

    • info

      added the optional property // to the response with the status

    • info

      added the optional property // to the response with the status

    • info

      added the optional property // to the response with the status