Skip to content

Alert and alert-rule endpoints

The API splits alert history (fired notifications) under /api/v1/alerts from alert-rule configuration (the conditions that fire alerts) under /api/v1/alert-rules. Both are scoped by the JWT’s org_id.

Alert-rule bodies use the D95 universal taxonomy (rule_kind + event_category + trigger_topic_pattern + source_filter + system_trigger). The legacy trigger_type column was dropped by migration 000060 and is not part of the request or response contract. The current JSON decoder is permissive: an extra trigger_type property is silently ignored, while a body that relies on it still fails because rule_kind is required. Event payloads nested inside alerts (the “what triggered this alert” sub-object) are pure D95: event_category, trigger_topic, trigger_source. See Alert rule schema for the full field reference.

GET /api/v1/alerts

List fired alerts for the caller’s org.

Since: v1.0 Required role: viewer

Query parameters

NameTypeDefaultConstraints
rule_iduuid—Filters to one rule
statusenum—new, acknowledged, resolved
date_fromstring—RFC3339 or YYYY-MM-DD
date_tostring—RFC3339 or YYYY-MM-DD
cursorstringnullPagination cursor
limitint201-100

Response 200

{
"data": [
{
"id": "a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
"rule_id": "f5e4d3c2-b1a0-4f9e-8d7c-6b5a4e3d2c1b",
"rule_name": "After-hours dock motion",
"event_id": "c1d2e3f4-5a6b-4c7d-8e9f-0a1b2c3d4e5f",
"camera_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "new",
"fired_at": "2026-04-21T02:14:00Z",
"acknowledged_at": null
}
],
"next_cursor": null,
"total": 1
}

Error responses

CodeBody.errorMeaning
401auth_requiredMissing or invalid token
429rate_limitedSee /developer/rate-limits

Examples

Terminal window
curl -H "Authorization: Bearer sk_live_abc123" \
"https://novavms.novalien.com/api/v1/alerts?status=new"
const alerts = await novavms.alerts.list({ status: 'new' });
alerts = client.alerts.list(status="new")

GET /api/v1/alerts/{id}/timeline

Return a chronological timeline of state changes for a single alert: fired, acknowledged, status transitions.

Since: v1.1 Required role: viewer

Response 200

{
"alert_id": "a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
"entries": [
{"at": "2026-04-21T02:14:00Z", "action": "fired", "actor": null},
{"at": "2026-04-21T02:18:42Z", "action": "acknowledged", "actor": "Jordan Park"},
{"at": "2026-04-21T03:05:00Z", "action": "status_changed", "actor": "Jordan Park", "to": "resolved"}
]
}

Error responses

CodeBody.errorMeaning
404not_foundAlert not in caller’s org

Examples

Terminal window
curl -H "Authorization: Bearer sk_live_abc123" \
https://novavms.novalien.com/api/v1/alerts/a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d/timeline
const timeline = await novavms.alerts.timeline(
'a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d',
);
timeline = client.alerts.timeline("a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d")

GET /api/v1/alert-rules

List alert rules in the caller’s org.

Since: v1.0 Required role: viewer

Response 200

{
"data": [
{
"id": "f5e4d3c2-b1a0-4f9e-8d7c-6b5a4e3d2c1b",
"name": "Dock motion",
"rule_kind": "camera_event",
"event_category": "motion",
"trigger_topic_pattern": "VideoSource/MotionAlarm",
"source_filter": ["onvif"],
"scope_type": "all_cameras",
"schedule_type": "always",
"cooldown_sec": 300,
"enabled": true
}
],
"total": 1
}

Error responses

CodeBody.errorMeaning
401auth_requiredMissing or invalid token

Examples

Terminal window
curl -H "Authorization: Bearer sk_live_abc123" \
https://novavms.novalien.com/api/v1/alert-rules
const rules = await novavms.alertRules.list();
rules = client.alert_rules.list()

POST /api/v1/alert-rules

Create an alert rule.

Since: v1.0 Required role: operator

Request body

All match-axis fields use the D95 universal taxonomy. The legacy trigger_type column was dropped by migration 000060 and the field is ignored if supplied as extra JSON. It does not substitute for required D95 fields; a legacy-only body still returns 400 VALIDATION_ERROR because rule_kind is missing. Full schema at Alert rule schema.

NameTypeDefaultConstraints
namestring—3-100 chars
rule_kindenum—Required D95 discriminator: camera_event or system_event.
event_categoryenum—D95. One of the 10 categories (list). Allowed only when rule_kind=camera_event. NULL = match any.
trigger_topic_patternstring—D95. SQL LIKE pattern matched against events.trigger_topic (e.g. %LineDetector%).
source_filterstring[]—D95. Subset of {onvif, software, manual, scheduled}. NULL = match any.
system_triggerenum—Required when rule_kind=system_event: camera_offline or gateway_offline. gateway_reconnect is rejected.
custom_tagsstring[][]Legacy CRUD-only field. A rule carrying tags is skipped by the immediate evaluator; use a compiled rule for AI predicates.
min_confidencefloat—Legacy CRUD-only field, 0.0-1.0. A rule carrying it is skipped by the immediate evaluator.
scope_typeenumall_camerasall_cameras, site, or cameras. Live legacy evaluators fire only all_cameras and cameras; site is accepted/persisted but skipped.
site_idsuuid[][]Required when scope_type=site; referenced sites must belong to the org.
camera_idsuuid[][]Required when scope_type=cameras; referenced cameras must belong to the org.
zones_filterstring[][]Legacy CRUD field; the live legacy evaluators do not apply a zone gate.
schedule_typeenumalwaysalways or business_hours. Only always fires in the live legacy evaluators.
cooldown_secint30060-3600; enforced by the immediate and supported system-event history inserts.
aggregation_window_secondsint0Must be 0; non-zero legacy aggregation is rejected.
delay_secondsint0Must be 0; non-zero legacy delay is rejected.
conditions_jsonobject{}Must be empty or null; non-empty legacy condition trees are rejected.
recipientsobject[][]Up to 20 recipients, each {user_id, channels}. No lower bound is enforced.

Response 201

{
"id": "f5e4d3c2-b1a0-4f9e-8d7c-6b5a4e3d2c1b",
"name": "Dock motion",
"rule_kind": "camera_event",
"event_category": "motion",
"trigger_topic_pattern": "VideoSource/MotionAlarm",
"source_filter": ["onvif"],
"scope_type": "cameras",
"camera_ids": ["550e8400-e29b-41d4-a716-446655440000"],
"schedule_type": "always",
"cooldown_sec": 300,
"enabled": true,
"created_at": "2026-04-21T14:15:00Z"
}

Error responses

CodeBody.errorMeaning
400validation_errorInvalid name, rule shape, scope, schedule, cooldown, or recipient reference
403forbiddenRole insufficient
404not_foundReferenced camera or site not in caller’s org

Examples

Terminal window
curl -X POST https://novavms.novalien.com/api/v1/alert-rules \
-H "Authorization: Bearer sk_live_abc123" \
-H "Content-Type: application/json" \
-d '{"name":"Dock motion","rule_kind":"camera_event","event_category":"motion","trigger_topic_pattern":"VideoSource/MotionAlarm","source_filter":["onvif"],"scope_type":"cameras","camera_ids":["550e8400-e29b-41d4-a716-446655440000"],"schedule_type":"always","cooldown_sec":300,"recipients":[{"user_id":"660e8400-e29b-41d4-a716-446655440001","channels":["in_app","email"]}]}'
const rule = await novavms.alertRules.create({
name: 'Dock motion',
ruleKind: 'camera_event',
eventCategory: 'motion',
triggerTopicPattern: 'VideoSource/MotionAlarm',
sourceFilter: ['onvif'],
scopeType: 'cameras',
cameraIds: ['550e8400-e29b-41d4-a716-446655440000'],
scheduleType: 'always',
cooldownSec: 300,
recipients: [{ userId: '660e8400-e29b-41d4-a716-446655440001', channels: ['in_app', 'email'] }],
});
rule = client.alert_rules.create(
name="Dock motion",
rule_kind="camera_event",
event_category="motion",
trigger_topic_pattern="VideoSource/MotionAlarm",
source_filter=["onvif"],
scope_type="cameras",
camera_ids=["550e8400-e29b-41d4-a716-446655440000"],
schedule_type="always",
cooldown_sec=300,
recipients=[{"user_id": "660e8400-e29b-41d4-a716-446655440001", "channels": ["in_app", "email"]}],
)

PATCH /api/v1/alert-rules/{id}

Update fields on an existing rule. Partial updates are supported.

Since: v1.0 Required role: operator

Request body

Any subset of fields accepted by POST /api/v1/alert-rules.

Response 200

Returns the updated rule — same shape as POST.

Error responses

CodeBody.errorMeaning
400validation_errorField value fails schema
404not_foundRule not in caller’s org

Examples

Terminal window
curl -X PATCH https://novavms.novalien.com/api/v1/alert-rules/f5e4d3c2-b1a0-4f9e-8d7c-6b5a4e3d2c1b \
-H "Authorization: Bearer sk_live_abc123" \
-H "Content-Type: application/json" \
-d '{"cooldown_sec":600}'
await novavms.alertRules.update('f5e4d3c2-b1a0-4f9e-8d7c-6b5a4e3d2c1b', {
cooldownSec: 600,
});
client.alert_rules.update(
"f5e4d3c2-b1a0-4f9e-8d7c-6b5a4e3d2c1b",
cooldown_sec=600,
)

DELETE /api/v1/alert-rules/{id}

Delete an alert rule. Historical alerts fired by the rule remain accessible via /api/v1/alerts.

Since: v1.0 Required role: operator

Response 200

{"message":"alert rule deleted"}

Error responses

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

Examples

Terminal window
curl -X DELETE https://novavms.novalien.com/api/v1/alert-rules/f5e4d3c2-b1a0-4f9e-8d7c-6b5a4e3d2c1b \
-H "Authorization: Bearer sk_live_abc123"
await novavms.alertRules.delete('f5e4d3c2-b1a0-4f9e-8d7c-6b5a4e3d2c1b');
client.alert_rules.delete("f5e4d3c2-b1a0-4f9e-8d7c-6b5a4e3d2c1b")

Sub-routes under /alerts and /alert-rules (pointer)

The alerts surface exposes additional live endpoints not yet broken out into per-section detail on this page. All require a Bearer access token; site-access checks apply. Authoritative shapes live in docs/arch-api-spec.md §21 Alerts and §22 Alert Rules.

Alert history sub-routes:

  • GET /api/v1/alerts/stats — aggregate stats. Handler: cloud/internal/alerts/alerthistory_handler.go:56.
  • PATCH /api/v1/alerts/{id}/acknowledge — operator acknowledges an alert (D68 lifecycle). Handler: alerthistory_handler.go:65.
  • PATCH /api/v1/alerts/{id}/status — transition status open → in_progress → resolved. Handler: alerthistory_handler.go:66.

Alert-rule sub-routes:

  • GET /api/v1/alert-rules/{id} — return a single rule. Handler: cloud/internal/alerts/alertrule_handler.go:28.
  • POST /api/v1/alert-rules/{id}/test — fire a test notification through the rule’s recipients. Handler: alertrule_handler.go:38.
  • POST /api/v1/alert-rules/{id}/recipients, GET /api/v1/alert-rules/{id}/recipients, DELETE /api/v1/alert-rules/{id}/recipients/{recipientId} — recipient sub-management. Handler: alertrule_handler.go:41-43.
  • POST /api/v1/alert-rules/{id}/activate — activate / publish a draft rule. Handler: alertrule_handler.go:52.
  • GET /api/v1/alert-rules/{id}/nbd, PATCH /api/v1/alert-rules/{id}/nbd — per-rule No-Big-Deal knobs. Handler: cloud/internal/alerts/nbd_handler.go:91-92.

Compiled-rule sub-routes (Phase 6c.1 NL alert compiler):

  • POST /api/v1/alert-rules/compile, POST /api/v1/alert-rules/test-compiled, POST /api/v1/alert-rules/{rule_id}/compiled-versions, GET /api/v1/alert-rules/{rule_id}/compiled-versions, GET /api/v1/alert-rules/{rule_id}/compiled-versions/active, POST /api/v1/alert-rules/{rule_id}/compiled-versions/{version_id}/activate, DELETE /api/v1/alert-rules/{rule_id}/compiled-versions/{version_id}. Handler: cloud/internal/alerts/compiled_rule_handler.go:135-143.

Budget sub-routes:

  • GET /api/v1/alert-rules/budget, PATCH /api/v1/alert-rules/budget — Gemini judge daily budget (D82). Handler: cloud/internal/alerts/judge_budget_handler.go:30-34.

Notification-group sub-routes (admin):

  • GET /api/v1/admin/notification-groups, GET /api/v1/admin/notification-groups/{id}/decisions. Handler: cloud/internal/alerts/group_handler.go:37-38.

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

See also