Skip to content

Event endpoints

All endpoints live under /api/v1/events. Events are scoped by the JWT’s org_id; viewers are additionally filtered by site-access (D78). There is no dedicated “acknowledge” or “dismiss” verb — acknowledge is a PATCH setting acknowledged: true; “dismiss” does not exist in the current API and is modelled as DELETE /api/v1/events/{id} (admin-only, soft).

Event payloads use the D95 universal taxonomy (migration 000053): every event carries an event_category, trigger_topic, and trigger_source rather than the pre-D95 trigger_type enum. See Universal ONVIF events for the full category list and topic conventions.

GET /api/v1/events

List events, optionally filtered.

Since: v1.0 Required role: viewer (site-scoped)

Query parameters

NameTypeDefaultConstraints
camera_iduuid—Filters to one camera
site_iduuid—Filters to one site
date_fromstring—RFC3339 or YYYY-MM-DD
date_tostring—RFC3339 or YYYY-MM-DD
event_categoryenum—One of the 10 D95 categories: motion, intrusion, line_crossing, tamper, tampering_scene, analytics_object, io, audio, schedule, other. See event_category values.
trigger_topic_likestring—SQL LIKE pattern matched against events.trigger_topic (e.g. %LineDetector%).
trigger_sourceenum—One of onvif, software, manual, scheduled.
ai_statusenum—pending, completed, skipped, failed
starredbool—Only starred events when true
cursorstringnullPagination cursor
limitint201-100

Response 200

{
"data": [
{
"id": "c1d2e3f4-5a6b-4c7d-8e9f-0a1b2c3d4e5f",
"camera_id": "550e8400-e29b-41d4-a716-446655440000",
"camera_name": "Loading Dock East",
"camera_deleted": false,
"event_category": "analytics_object",
"trigger_topic": "",
"trigger_source": "software",
"triggered_at": "2026-04-21T13:45:22Z",
"summary": "Person carrying package approaches dock door",
"tags": ["person", "package"],
"ai_status": "completed",
"acknowledged": false,
"starred": false,
"snapshot_url": "https://storage.googleapis.com/bkt-cloud-ai/..."
}
],
"next_cursor": null,
"total": 1
}

Error responses

CodeBody.errorMeaning
401auth_requiredMissing or invalid token
403forbiddenViewer outside site scope
429rate_limitedSee /developer/rate-limits

Examples

Terminal window
curl -H "Authorization: Bearer sk_live_abc123" \
"https://novavms.novalien.com/api/v1/events?event_category=analytics_object&date_from=2026-04-20"
const events = await novavms.events.list({
eventCategory: 'analytics_object',
dateFrom: '2026-04-20',
});
events = client.events.list(event_category="analytics_object", date_from="2026-04-20")

GET /api/v1/events/{id}

Return a single event with full detail (narrative, clip/snapshot URLs, prev/next navigation).

Since: v1.0 Required role: viewer (site-scoped)

Response 200

{
"id": "c1d2e3f4-5a6b-4c7d-8e9f-0a1b2c3d4e5f",
"camera_id": "550e8400-e29b-41d4-a716-446655440000",
"camera_name": "Loading Dock East",
"camera_deleted": false,
"event_category": "analytics_object",
"trigger_topic": "",
"trigger_source": "ai",
"raw_source": "vlm",
"raw_data": {"tags": ["person", "package", "vest"], "confidence": 0.91},
"triggered_at": "2026-04-21T13:45:22Z",
"clip_start_at": "2026-04-21T13:45:12Z",
"clip_end_at": "2026-04-21T13:45:42Z",
"duration_sec": 30,
"summary": "Person carrying package approaches dock door",
"narrative": "A courier in an orange vest walked to the dock...",
"tags": ["person", "package", "vest"],
"ai_status": "completed",
"acknowledged": false,
"acknowledged_by": null,
"acknowledged_at": null,
"starred": false,
"notes": null,
"clip_codec": "h264",
"snapshot_url": "https://storage.googleapis.com/bkt-cloud-ai/...",
"clip_url": "https://storage.googleapis.com/bkt-cloud-ai/...",
"recording_source": "gateway_ring_buffer",
"prev_event_id": null,
"next_event_id": null
}

Error responses

CodeBody.errorMeaning
401auth_requiredMissing or invalid token
403forbiddenViewer outside site scope
404not_foundEvent not in caller’s org

Examples

Terminal window
curl -H "Authorization: Bearer sk_live_abc123" \
https://novavms.novalien.com/api/v1/events/c1d2e3f4-5a6b-4c7d-8e9f-0a1b2c3d4e5f
const ev = await novavms.events.get('c1d2e3f4-5a6b-4c7d-8e9f-0a1b2c3d4e5f');
ev = client.events.get("c1d2e3f4-5a6b-4c7d-8e9f-0a1b2c3d4e5f")

PATCH /api/v1/events/{id}

Acknowledge, star, or annotate the event. Setting acknowledged: true records the caller’s ID and the current timestamp server-side.

Since: v1.0 Required role: operator

Request body

NameTypeDefaultConstraints
acknowledgedbool—true to acknowledge; false to clear
starredbool——
notesstring—Up to 2000 chars

Response 200

Returns the updated event — same shape as GET /api/v1/events/{id}.

Error responses

CodeBody.errorMeaning
400validation_errorEmpty body or unknown field
403forbiddenRole insufficient
404not_foundEvent not in caller’s org

Examples

Terminal window
curl -X PATCH https://novavms.novalien.com/api/v1/events/c1d2e3f4-5a6b-4c7d-8e9f-0a1b2c3d4e5f \
-H "Authorization: Bearer sk_live_abc123" \
-H "Content-Type: application/json" \
-d '{"acknowledged":true,"notes":"Confirmed courier, no action needed"}'
await novavms.events.update('c1d2e3f4-5a6b-4c7d-8e9f-0a1b2c3d4e5f', {
acknowledged: true,
notes: 'Confirmed courier, no action needed',
});
client.events.update(
"c1d2e3f4-5a6b-4c7d-8e9f-0a1b2c3d4e5f",
acknowledged=True,
notes="Confirmed courier, no action needed",
)

DELETE /api/v1/events/{id}

Soft-delete the event. This is the closest analogue to “dismiss”; see the note at the top of this page.

Since: v1.0 Required role: admin

Response 204

Empty body.

Error responses

CodeBody.errorMeaning
403forbiddenRole insufficient
404not_foundEvent not in caller’s org

Examples

Terminal window
curl -X DELETE https://novavms.novalien.com/api/v1/events/c1d2e3f4-5a6b-4c7d-8e9f-0a1b2c3d4e5f \
-H "Authorization: Bearer sk_live_abc123"
await novavms.events.delete('c1d2e3f4-5a6b-4c7d-8e9f-0a1b2c3d4e5f');
client.events.delete("c1d2e3f4-5a6b-4c7d-8e9f-0a1b2c3d4e5f")

GET /api/v1/events/export

Stream a CSV export of events matching the same filter set as GET /api/v1/events. Response is text/csv; charset=utf-8 with Content-Disposition: attachment.

Since: v1.1 Required role: operator

Query parameters

Same as GET /api/v1/events. Server caps results at EVENT_EXPORT_MAX_ROWS (default 10000).

Response 200

id,camera_id,camera_name,event_category,trigger_topic,trigger_source,triggered_at,duration_sec,summary,tags,ai_status,acknowledged,starred
c1d2e3f4-5a6b-4c7d-8e9f-0a1b2c3d4e5f,550e8400-e29b-41d4-a716-446655440000,Loading Dock East,analytics_object,,ai,2026-04-21T13:45:22Z,30,Person carrying package approaches dock door,"person,package",completed,false,false

Error responses

CodeBody.errorMeaning
403forbiddenRole insufficient
413payload_too_largeFilter matches more rows than the export cap

Examples

Terminal window
curl -H "Authorization: Bearer sk_live_abc123" \
"https://novavms.novalien.com/api/v1/events/export?date_from=2026-04-01&date_to=2026-04-21" \
--output events-april.csv
const csv = await novavms.events.export({
dateFrom: '2026-04-01',
dateTo: '2026-04-21',
});
csv_bytes = client.events.export(date_from="2026-04-01", date_to="2026-04-21")

Sub-routes under /events/{id} (pointer)

The events handler exposes additional live endpoints not yet broken out into per-section detail on this page. All require a Bearer access token; viewer site-access checks mirror GET /events/{id} (D78 IDOR gate). Authoritative shapes live in docs/arch-api-spec.md §16 Events and §17 AI:

  • GET /api/v1/events/{id}/clip-url, GET /api/v1/events/{id}/snapshot-url — short-TTL signed storage URLs. Handlers: cloud/internal/events/handler.go:395-396.
  • GET /api/v1/events/{id}/similar — embedding-based “find similar events” (Phase G, MobileCLIP2-B HNSW). Returns {data: EventSearchResultDTO[], source_event_id, limit}. Handler: cloud/internal/search/similar_handler.go:65. Note: a duplicate legacy registration exists in cloud/internal/events/handler.go:397 (Phase 2 GAP-2-03) — chi v5 silently overrides; the search-package handler is live.
  • GET /api/v1/events/{id}/objects — v5 evidence: detected object classes + bboxes. Handler: cloud/internal/ai/objects_handler.go:66.
  • GET /api/v1/events/{id}/faces — face detections + matched profile IDs. Handler: cloud/internal/events/handler.go:399.
  • GET /api/v1/events/{id}/ai-evidence — 7-tab evidence payload (EventAIEvidencePanel). Handler: cloud/internal/ai/evidence_handler.go:107.
  • GET /api/v1/events/{id}/analysis — v5 stage-status + AI result. Handler: cloud/internal/ai/ai_handler.go:166.
  • POST /api/v1/events/{id}/analyze — re-trigger AI analysis (admin+). Handler: ai_handler.go:165.
  • GET /api/v1/events/timeline — bucketed event volume by camera (zoom + bucket_size params). Handler: cloud/internal/events/handler.go:389.
  • POST /api/v1/events/{eventId}/feedback, GET /api/v1/events/{eventId}/feedback — operator feedback (Phase 6 corpus). Handler: cloud/internal/events/feedback_handler.go.
  • POST /api/v1/events/chat — cohort Ask AI (NOT per-event chat — that was removed in L12). Handler: cloud/internal/search/chat_handler.go:41.
  • POST /api/v1/events/{eventId}/shares, GET /api/v1/events/{eventId}/shares, DELETE /api/v1/events/{eventId}/shares/{shareId}, GET /api/v1/events/{eventId}/shares/{shareId}/audit — clip sharing (D61). Handler: cloud/internal/sharing/handler.go:28-40.
  • GET /api/v1/object-categories — list canonical object categories for chip filters. Handler: cloud/internal/events/object_categories_handler.go:37.

The OpenAPI yaml at /openapi.yaml does not yet inventory these routes (see Phase 8 GAP-8-17).