---
title: "Draw a box around a person the AI missed"
method: POST
path: "/events/{event_id}/missed-person-boxes"
tags: ["events"]
---

# Draw a box around a person the AI missed

`POST /events/{event_id}/missed-person-boxes`

## 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}/missed-person-boxes`
- Auth: HTTP bearer

Saves one box, pinned to one frame time, with full provenance snapshotted from the event at save time. Staff-only and tenant-scoped, same gates as the list route. class_label is 'person' only in phase 1.

## Path parameters

- `event_id` string, required

## Request body

- CreateOperatorMissBoxRequest: One box an operator drew around a person the AI missed, on the frame where it's visible. See `app/models/operator_miss_boxes.py` for the storage shape and design of record. `clip_s3_url` is deliberately NOT a field here: the server snapshots it from the event's current `clip_s3_url` at write time rather than trusting a client-supplied S3 key. EVERY NUMERIC FIELD IS `strict=True` (PR #276 review, Greptile P2): pydantic's default (lax) mode coerces a JSON boolean to `0`/`1` and a numeric string to its number, so `"x1": true` or `"x1": "0.5"` would silently become a different annotation from the one an operator actually submitted, and pass validation, and be stored and exported that way. Strict mode refuses both -- only a genuine JSON number is accepted.
  - `frame_timestamp_ms` integer, required: Milliseconds into the clip, read from the browser's decoded playback clock at the moment of Save (prefer requestVideoFrameCallback's mediaTime; see frame_capture_method). Never a frame index -- camera fps is per-camera and some cameras are variable frame rate, so a timestamp is the only stable frame identity. Bounded to the storage column's INTEGER range (2^31-1) so an out-of-range value is a 422, not a 500 at commit. Strict: a JSON boolean or a numeric string is rejected, not coerced.
  - `frame_capture_method` 'rvfc' | 'currentTime', required: Which browser signal produced frame_timestamp_ms: 'rvfc' (requestVideoFrameCallback's metadata.mediaTime, the presented frame) or 'currentTime' (the requestAnimationFrame fallback when rVFC is unavailable). Recorded for the export's drift-bound accounting, never used to gate the write.
  - `x1` number, required: Left edge of the box, normalized 0-1 as a fraction of the frame's width (same convention as `detections`). Strict: a JSON boolean or a numeric string is rejected, not coerced.
  - `y1` number, required: Top edge of the box, normalized 0-1 as a fraction of the frame's height. Strict: a JSON boolean or a numeric string is rejected, not coerced.
  - `x2` number, required: Right edge of the box, normalized 0-1. Must be greater than x1 (checked below). Strict: a JSON boolean or a numeric string is rejected, not coerced.
  - `y2` number, required: Bottom edge of the box, normalized 0-1. Must be greater than y1 (checked below). Strict: a JSON boolean or a numeric string is rejected, not coerced.
  - `class_label` 'person': Phase 1 supports 'person' only; the column itself is a plain string so a later phase can add more without a migration.

## Response `200`

Successful Response

- OperatorMissBoxModel: The per-event API's shape. `event_metadata_id`/`camera_id` stay REQUIRED here even though the underlying column is nullable (`ON DELETE SET NULL`, PR #276 review) -- every box this model ever serializes was found by filtering `OperatorMissBoxes.event_metadata_id == event.id` against a live, loaded event, so a null value here can never actually reach this model. The PERMANENT provenance (`source_event_id`/`source_camera_id`, which survive a hard delete) is not exposed on this live per-event surface -- it exists for the offline export, not this API.
  - `id` string, uuid, required: The box's own id.
  - `event_metadata_id` string, uuid, required: The event this box was drawn on. See the class docstring.
  - `camera_id` string, uuid, required: The camera the event's clip came from. See the class docstring.
  - `site_id` string, uuid, required: Audit snapshot of the camera's site at draw time.
  - `parent_id` string, uuid, required: Audit snapshot of the camera's client (parent) at draw time.
  - `frame_timestamp_ms` integer, required: Milliseconds into the clip
  - `frame_capture_method` 'rvfc' | 'currentTime', required: Which browser signal produced frame_timestamp_ms: 'rvfc' (requestVideoFrameCallback's metadata.mediaTime) or 'currentTime' (the requestAnimationFrame fallback).
  - `x1` number, required: Left edge of the box, normalized 0-1.
  - `y1` number, required: Top edge of the box, normalized 0-1.
  - `x2` number, required: Right edge of the box, normalized 0-1.
  - `y2` number, required: Bottom edge of the box, normalized 0-1.
  - `class_label` string, required: Always 'person' in phase 1.
  - `label_source` string, required: Always 'operator_miss' -- how this box was made.
  - `operator_id` string, uuid, nullable: Who drew it. Null if that operator account was later deleted.
  - `created_at` string, date-time, required: When the box was drawn.
  - `updated_at` string, date-time, nullable: When the box was last moved or resized. Null if never edited.

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