- technical (default): operational or hardware events (e.g. robot stuck, calibration needed).
- business: business-process events (e.g. order delayed, SLA threshold exceeded).
- A robot needs calibration.
- A robot got stuck and needs remote takeover.
- A sensor reading is out of expected range.
- An order has exceeded its SLA deadline.
Model
Every alert belongs to a workspace and must be attached to at least one of: twin, environment, or workflow.Lifecycle
- Active: new alert, requires attention.
- Acknowledged: an operator has seen it but the issue is not yet fixed.
- Resolved: the root cause has been addressed (by edge device or operator).
- Silenced: suppressed workspace-wide without resolving the root cause.
Idempotent resolve and silence
ThePOST /api/v1/alerts/{uuid}/resolve and POST /api/v1/alerts/{uuid}/silence endpoints are idempotent:
- Resolving an already-resolved alert returns
200with the alert body (no-op). - Resolving a silenced alert is allowed — it transitions the alert to resolved.
- Silencing an already-silenced alert returns
200with the alert body (no-op). - Silencing a resolved alert returns
400.
Workflow-scoped alerts
Alerts produced by a workflow’ssend_alert node carry a workflow_uuid, and Alert.workflow is set on the backend. List alerts for a single workflow with:
client.publish_alert(..., workflow_uuid=WORKFLOW_UUID, workflow_node_uuid=..., workflow_execution_uuid=...). The SDK forwards workflow_uuid as a top-level field (sets the FK) and merges workflow_node_uuid / workflow_execution_uuid into metadata for full provenance.
When an alert is attributable to one node, the alert card also shows a Node: <name> (<type>) badge (from metadata.workflow_node_name / metadata.workflow_node_type), and a POST /activate rejection returns that same node identity in the response body so the editor can highlight the offending node directly instead of showing a generic failure message.
Source attribution: metadata.source_chain
Every alert raised by a workflow’s send_alert node carries an ordered source_chain under metadata describing each upstream node that fed into the decision. The chain is built generically — any node emitter that opts in by parking a _source_summary on its output dict contributes an entry, so the mechanism works equally well for camera-perception, audio-track, alert-triggered, manual, scheduled, or any future trigger source.
Each entry includes:
kind— short identifier (camera_frame,audio_track,alert_trigger,manual_trigger,schedule_trigger,call_model,detection_event_gate,conditional).node_uuid— the workflow node that contributed the entry.- Kind-specific fields. Examples:
twin_uuid+sensorfor camera/audio triggers;model_uuid+model_name+ a cappeddetections_sampleforcall_model;mode+matched_classes+cooldown_secondsfordetection_event_gate.
camera_frame → call_model → send_alert workflow:
- The chain is purely additive. Adding
_source_summarydoes not changededupe_hash(computed overname + description + alert_type + severity + status + twin_uuid), so dedupe behavior is unchanged. detections_sampleis capped to keep alert metadata bounded; the originaldetections_totalis preserved.- User-supplied static
metadatakeys always win on conflict — the source chain only fills inmetadata['source_chain']when not already provided.
Edge Core system alerts
Edge Core automatically raises technical alerts for operational issues. These arecategory: technical, source_type: edge unless noted otherwise.
edge_core_restart lifecycle alert
POST /api/v1/edges/{uuid}/restart-core creates a single edge_core_restart alert that tracks the restart end-to-end. The alert is scoped to the environment of the first bound twin (alerts always need a twin / environment / workflow anchor), with source_type: cloud because the request originates from the backend, not from the edge.
The current phase is carried on metadata.phase:
The same
alert_uuid is returned in the API response and included in the MQTT command payload, so edge-core can transition the alert without a lookup.
Restart-driven pre-resolution
Whenrestart-core is accepted, the backend also pre-resolves any active alerts on the requesting edge’s bound twins whose root cause a clean container relaunch genuinely fixes:
driver_start_failuredriver_restart_loopworker_start_failure
edge_core_restart alert’s own metadata gets pre_resolved_alert_uuids: [...] so the audit trail links both ways.
Other alert types are deliberately not pre-resolved (a restart doesn’t actually fix them, and silently closing them would lie to the operator). The authoritative allow-list and excluded set live on EDGE_CORE_RESTART_RESOLVABLE_ALERT_TYPES — change the constant and this section together.
The response schema is EdgeCoreRestartResponseSchema. Note that alert_uuid is null when no environment can be resolved for the edge (typically: no bound twin yet); the restart still happens, just untracked.
Frontend display contract
The workbench renders every alert through a singleAlertCard (cyberwave-frontend/components/alerts/alert.tsx). The generic path uses only these fields and needs nothing else from the producer:
The new
edge_core_restart alert deliberately uses only the generic path. The lifecycle is encoded in metadata.phase, so any UI that wants to surface “Restart in progress vs. completed vs. failed” can read that field without a dedicated component, and the audit trail (request_id, pre_resolved_alert_uuids, previous_phase, timed_out_at) is plain JSON.
Specialised renderers exist for a handful of historical alert types that need bespoke interactions; each one is a known cost, not a pattern to copy:
Adding a new alert_type
Before you introduce a new alert_type:
- Check this page. If an existing type fits — even loosely — extend its
metadatainstead of forking a new code. - Default to the generic path. Pick a sensible
severity, write a clearname+description, and put any structured state onmetadata. The generic card will render it correctly. - Only add a specialised renderer when the interaction itself is novel (e.g. a calibration step that needs custom MQTT commands). Generic
metadata.buttonscover most operator-confirms-something flows without new code. - Document the type in this page before merging — both the catalogue row above and, if specialised, the table in this section. A new
alert_typeconstant that does not have a row here should not pass review.
driver_starting progress metadata
Edge-core writes byte-aggregated docker pull progress directly onto the active driver_starting alert’s metadata, so the workbench renders a live "Downloading driver image (cyberwaveos/ugv-driver:dev) — 745 MB of 1.55 GB (47%)" line without any extra round-trip. The fields are:
The frontend renderer is in
cyberwave-frontend/components/alerts/alert.tsx — search for getDriverStartingDisplayText.