Alerts
Endpoint: /api/alerts/
An alert is one row per episode, open or resolved—see Alert history for how the console shows the same rows. This API is read-only over the alerts themselves: you can list them, read one, and acknowledge one, but nothing here creates, edits, or deletes an alert—every alert is raised and resolved by the platform.
Unlike the alert rules API, this endpoint isn’t gated by the Metrics extension: offline and security alerts are unaffected by it either way. The one exception is a metric alert raised by an extension rule—anything beyond the seeded core disk-usage rule—which drops out of the list, and 404s on its own detail and acknowledge routes, while the workspace’s Metrics extension is off.
List
List alerts
Retrieve a paginated list of alerts. Without resolved, the list answers open alerts only; pass resolved=true to read the resolved history instead.
Request
GET /api/alerts/Query parameters
| Parameter | Type | Description |
|---|---|---|
alert_type | string | Filter by alert type: metric_threshold, server_disconnected, unauthorized_access, insecure_port_open, system_error, unknown_device, device_network_moved, system_user_conflict, agent_upgrade_failed |
severity | string | Filter by severity (critical, warning, info) |
server | UUID | Filter by server |
server_name | string | Filter by server name, case-insensitive substring match |
rule | UUID | Filter by the alert rule that raised it (metric alerts only) |
resolved | boolean | Resolution state. Omitted, the list answers open alerts only. true returns the resolved history instead; false is the explicit spelling of the default |
acknowledged | boolean | Whether you’ve acknowledged the alert—checked or dismissed either count |
dismissed | boolean | Whether you specifically dismissed the alert, narrower than acknowledged |
added_at__gte / added_at__lte | datetime | Only alerts raised at or after / at or before this instant |
search | string | Search across title, description, and server name |
ordering | string | Sort order, over acknowledged, added_at, updated_at, severity. Default: acknowledged,-added_at |
page | integer | Page number |
page_size | integer | Results per page (default: 15, max: 100) |
Response
{
"count": 1,
"current": 1,
"next": null,
"previous": null,
"last": 1,
"results": [
{
"id": "9a1c0b62-9f3a-4a3d-8a1e-2b0f2c6d5e41",
"alert_type": "metric_threshold",
"server": {
"id": "7c5e2a41-6b90-4c33-a7d8-0e5f1b2c3d44",
"name": "web-01",
"os": "ubuntu",
"platform": "linux",
"is_connected": true,
"is_deleted": false
},
"severity": "warning",
"title": "CPU usage 92% ≥ 80% for 5 minutes",
"description": "CPU usage has stayed at or above 80% since 12:00.",
"dedup_key": "metric:cpu-usage:",
"device": "",
"rule": { "id": "4f2b18d0-5c77-4a0b-9d2e-1f7a3c9b4e05", "name": "cpu-high" },
"condition": "threshold",
"is_acknowledged": false,
"action_type": null,
"added_at": "2026-09-01T12:00:00Z",
"updated_at": "2026-09-01T12:04:00Z",
"resolved_at": null,
"resolution_cause": "",
"deliveries": [
{
"edge": "raised",
"channel": "email",
"state": "sent",
"recipients": 3,
"at": "2026-09-01T12:02:00Z"
},
{
"edge": "raised",
"channel": "slack_channel",
"state": "sent",
"recipients": null,
"at": "2026-09-01T12:02:00Z"
}
]
}
]
}| Field | Description |
|---|---|
id | The alert’s ID |
alert_type | See the values under alert_type above |
server | The server the alert is about: id, name, os, platform, is_connected, is_deleted |
severity | critical, warning, or info |
title / description | The alert’s headline and longer explanation, rendered in your own language when a translation exists |
dedup_key | Identifies the alert’s subject within (server, alert type)—so a repeat condition updates this row instead of raising a duplicate. Empty for an alert type that doesn’t dedup |
device | The disk, partition, or network interface this alert is about, when it’s scoped to one. Empty when the alert is about the server as a whole |
rule | The alert rule that raised this alert, as {id, name}. null for an alert no rule raises (only metric alerts carry one), and once the rule has been deleted |
condition | Which condition of its rule this alert is about: threshold or no_data. Empty for an alert no rule raises |
is_acknowledged | Whether you’ve acknowledged this alert—checked or dismissed either count |
action_type | checked or dismissed, whichever you acknowledged it with. null if you haven’t |
added_at | When this alert (this episode) was first raised |
updated_at | When this row was last written. A sustained condition refreshes the same row rather than raising a new one, so this moves without a new episode starting |
resolved_at | When this alert was observed to have cleared. null while it’s still open. A resolved row is purged 30 days after this instant |
resolution_cause | Why this alert was resolved, in a vocabulary owned by whichever app resolved it. For a metric alert, see the cause values under Alert history; other alert types carry their own. Empty while the alert is open, and on a row resolved before this field existed |
deliveries | What this episode’s raise and resolution actually sent, one entry per edge and channel—see the shape below. Alert types that never send an interrupt carry an empty list |
deliveries[].edge | raised or resolved |
deliveries[].channel | The channel sent on: email, push, or slack_channel. null for an entry that names no channel—the raise’s absence when nothing was sent |
deliveries[].state | sent, failed (the send was attempted for that channel and failed—an email dispatch error, or a Slack post that failed after its retry; a sent row on the same edge and channel outranks it), held (still inside the hold), resolved_in_hold (the condition cleared before the hold ended, so neither edge sent), suppressed (the rule’s own destinations narrowed the channels), or unrecorded (dispatched, but nothing recorded a send—a fully muted fan-out, an empty recipient set, or a Slack post skipped because the installation was gone) |
deliveries[].recipients | How many people this channel addressed, or, for failed, didn’t reach. null for a Slack channel post, which addresses a room, and for an entry that sent nothing |
deliveries[].at | The earliest send on this edge and channel—or, for failed, the earliest attempt (an email’s dispatch time; a Slack post’s give-up time, after its retry). For unrecorded, the alert’s notified_at—when its raise was claimed for sending. null for held, resolved_in_hold, and suppressed |
A failure past the mail hand-off—inside the mail worker, past the SMTP step—isn’t visible here and still reads as sent: this field reports what the sender could see, not final inbox delivery.
Required scope: alert:read
Example
curl -X GET "https://your-workspace.us1.alpacon.io/api/alerts/?resolved=true&severity=critical" \
-H "Authorization: token=\"alpat-xxxxxxxxxxxxxxxxxx\""Error responses
| Status | Error code | Description |
|---|---|---|
| 400 | alert_already_acknowledged | You already acknowledged this alert |
| 401 | UNAUTHORIZED | Invalid or missing authentication |
| 403 | FORBIDDEN | Insufficient permissions |
| 404 | NOT_FOUND | Alert not found, or it’s a metric alert from an extension rule and the Metrics extension is off |