---
title: "Get notification details"
method: GET
path: "/api/v1/notifications/{notification}"
tags: ["Notifications"]
---

# Get notification details

`GET /api/v1/notifications/{notification}`

## Request

- Base URL: `https://wardenpoint.com` (the document declares no server; this is the origin it was published from)
- URL: `https://wardenpoint.com/api/v1/notifications/{notification}`
- Auth: API key in header `X-API-Key`

The same body as `/status` on the same notification — one card with the delivery state, the channels tried, the escalation it started, and, once somebody has taken the alert, `acknowledged_at` and `acknowledged_via`.

## Path parameters

- `notification` string, uuid, required

## Response `200`

Notification details.

- NotificationDetail: One notification, in the single shape every read of one returns: the listing, the card and the status poll (Taiga #559). They used to disagree — the listing handed back the database row, internal numeric ids and `deleted_at` included, while the card returned this. Nothing client-facing is addressed by a numeric id anywhere in this API, and the listing was the one place that leaked them.
  - `uuid` string, uuid
  - `status` 'pending' | 'processing' | 'completed' | 'failed' | 'cancelled' | 'acknowledged' | 'escalating': Lifecycle state of a notification. FOUR of the seven are terminal and will never change again: `completed`, `failed`, `cancelled`, `acknowledged`. Poll until you see one of those four — waiting only for `completed` or `failed` hangs until your own timeout on every alert a human confirms. `pending` — created, waiting for a worker. Not terminal. `processing` — a worker is delivering it right now. Not terminal. `escalating` — delivery happened, nobody confirmed in time, and the escalation chain is walking to the next person. Not terminal: it still ends in `acknowledged` or `failed`. `completed` — delivered. TERMINAL. `failed` — every channel and attempt was exhausted without delivery. TERMINAL, and the only status `retry` accepts. `cancelled` — stopped before delivery, by the cancel endpoint. TERMINAL. `acknowledged` — a human confirmed it and the escalation chain stopped. TERMINAL, and the outcome the product exists for: an alert that reaches this has done its job.
  - `priority` 'low' | 'normal' | 'high' | 'critical'
  - `message` string: The text as it was sent.
  - `audio_file` string, nullable
  - `recipient` object, nullable: Null when the recipient has been deleted since.
    - `uuid` string, uuid
    - `name` string
    - `is_system` boolean: True only for the internal "Owner (fallback delivery)" recipient that carries rescue emails. Such a recipient is deliberately absent from `GET /recipients` — it is not a person anybody configured and there is nothing to fix about it; it exists so a rescue email travels the ordinary delivery pipeline and gets routes, attempts and a status like every other send. A name here that you cannot find in the recipient list is this, not a deletion.
  - `is_owner_fallback` boolean: True when this notification IS the rescue email to the account owner rather than an alert of yours. The alert it was sent about is named by `original_notification_uuid` in `metadata`, and `owner_fallback_reason` there says why delivery failed.
  - `channels` object[]: One entry per channel attempted, in the order they were attempted. A notification that reads `completed` can still carry a failed channel here — that is the fallback working as intended.
    - `type` string: Channel slug: text_message, voice_message, voice_call, email, sms, …
    - `provider` string: Provider slug: telegram, platform_asterisk, smtp, …
    - `status` string: Per-channel outcome. Independent of the notification status above.
    - `attempts` integer
    - `max_attempts` integer
  - `total_attempts` integer: Delivery attempts across all channels.
  - `external_id` string, nullable: Provider-side identifier of the delivered message or call, when the provider gives one.
  - `error_message` string, nullable: Last delivery error. Present on `failed`, and possibly on a notification that later succeeded through a fallback.
  - `metadata` object, nullable
  - `escalation` object, nullable: The escalation chain this alert started, or null when it started none. Null is the ordinary case rather than a fault: a chain opens only when the rule governing the alert has timeout rungs to walk. An observability alert matched by a `message` routing action never opens one — only `escalate` does. This is the answer to "what happens next", and until now the API had none: the card said nothing about escalation at all, and the listing exposed only the chain's internal numeric id, which no other endpoint accepts. The notification's own step counter is deliberately not surfaced — it writes 0 both when a chain starts and when its first rung runs, so the number cannot tell those apart. Count from here instead.
    - `uuid` string, uuid: The chain's UUID.
    - `status` 'active' | 'acknowledged' | 'completed' | 'failed' | 'cancelled': `active` while rungs are still coming. `acknowledged` when a human took the alert, `completed` when the ladder ran out, `cancelled` or `failed` otherwise — all four are terminal and nothing further will be paged.
    - `steps_taken` integer: How many rungs of the ladder have already run. Zero means none has yet — the alert was paged and the first rung is waiting for `next_step_at`. One unit in every state, deliberately: it means the same thing whether the chain is still active or has stopped, and it only ever grows. A field that counted "the rung now running" while active and "rungs that ran" once stopped would show 1 and then 0 for a chain acknowledged before its first rung, and a client rendering progress would watch it go backwards.
    - `total_steps` integer: Rungs in the ladder. `steps_taken` equal to this means the ladder is spent and nobody is left to page, whatever the status says.
    - `next_step_at` string, date-time, nullable: When the next rung was scheduled to fire. Null on a stopped chain, and null on an active one whose next rung is not scheduled yet. **It is a schedule, not a promise, and it can be in the past.** The value is written when the rung is queued and is not rewritten while it waits, so a busy escalation queue leaves behind an instant that has already gone. Read a past value as "due, not yet run": a countdown built on it will go negative, and that is the queue running late rather than the chain being broken.
    - `started_at` string, date-time, nullable
    - `completed_at` string, date-time, nullable: When the chain stopped, for whatever reason. Null while it runs.
  - `created_at` string, date-time
  - `completed_at` string, date-time, nullable: Set once the notification reaches a terminal status; null while it is still moving.
  - `acknowledged_at` string, date-time, nullable: When a human took the alert. Null on anything that was never acknowledged. This is the moment the escalation chain stopped, so `acknowledged_at - created_at` is the response time of this incident.
  - `acknowledged_via` string, nullable: The label recorded with the acknowledgement — the `source` you sent to the acknowledge endpoint (default `api`), or the surface the platform acknowledged from on its own: `telegram_callback`, `dtmf`, `dashboard`, `email_link`, and so on. Null on anything that was never acknowledged. Together with `acknowledged_at` this is the audit trail the acknowledge endpoint promises; nothing else exposes it.

## Other responses

- `401`: API key missing, invalid, revoked or expired.
- `403`: API key lacks the notifications.read ability.

---

[API](https://skmtc.dev/wardenpoint/apis/wardenpoint-api.md) · [All operations](https://skmtc.dev/wardenpoint/apis/wardenpoint-api/llms.txt) · [OpenAPI document](https://skmtc.dev/wardenpoint/apis/wardenpoint-api/revisions/259ee445728e?raw)
