---
title: "Send notification to recipient (synchronous)"
method: POST
path: "/api/v1/notifications/send"
tags: ["Notifications"]
---

# Send notification to recipient (synchronous)

`POST /api/v1/notifications/send`

## 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/send`
- Auth: API key in header `X-API-Key`

**200 does not always mean this recipient was reached.** When no channel could carry the alert to them, the umbrella guarantee fires: the company owner is emailed that delivery failed, and THAT delivery is what the success body describes. It is reported in `owner_fallback` — see `OwnerFallbackNotice`. Check for that object before telling a human the alert arrived; without it the response is an ordinary delivery.

## Request body

- NotificationSendRequest
  - `recipient_uuid` string, uuid, required: Who to wake up. The channel is NOT chosen here — it comes from that person's notification settings for the priority below. Three failures worth telling apart, because they answer differently. A uuid that exists nowhere is a 422. A uuid belonging to **another company** passes the existence check and then fails to resolve inside your tenant, so it comes back as **404** — same as a typo would look, and not the 403 you might expect. And a recipient of yours who is **deactivated is not refused at all**: the send proceeds, and whether anything leaves depends on the routes their settings produce. Do not read a 2xx here as "this person is reachable"; read `GET /recipients/{recipient}/availability` for that.
  - `message` string, required: The alert text, as the person will read it. On a voice channel it is also what gets spoken: the text is synthesised to speech and cached, so the same wording costs the synthesiser only once. Keep it self-contained — a human woken at 4am sees this and nothing else.
  - `priority` 'low' | 'normal' | 'high' | 'critical': Which of the recipient's four notification settings applies — that is what actually picks the channel, the escalation ladder, and whether quiet hours are bypassed. Not a label: `critical` and `low` can route to entirely different providers. Defaults to `normal` when omitted.
  - `audio_file` string, nullable: Path to a ready-made audio file in this installation's storage, for voice channels. An API client normally has no such path and should omit this: the voice is then synthesised from `message`. A path that does not resolve is not an error — it is logged and the text is synthesised instead, so a typo here costs a delay, not a lost alert.
  - `max_attempts` integer: How many delivery attempts one channel may make before the step is considered failed. Defaults to 3 when omitted. This is retrying the SAME channel, not escalation: handing the alert to the next person is the escalation ladder's job and is configured on the recipient, not here.

## Response `200`

Notification sent — or, when `owner_fallback` is present, the alert reached nobody and the account owner was emailed instead. Successful responses carry a `delivery` object describing where this company stands against its plan quota — see `DeliveryOverageState`. Exceeding that quota never blocks a send; only the non-tariff daily ceiling does, and that answers 429 with `code: DELIVERY_CEILING_REACHED`.

- object
  - `delivery` DeliveryOverageState: Where this company stands against the notification quota of its plan, attached to every successful send response while unmetered delivery is switched on. Absent from the body when the feature is off — treat a missing `delivery` object as "no information", not as "within quota". **The quota does not stop delivery.** `over_plan: true` means alerts are still going out and the company is past what its plan includes; it is a billing signal, not a failure. Surface it, do not retry on it. The one thing that DOES stop delivery is `ceiling`: a non-tariff daily ceiling, identical on every plan and far above the largest one, that exists to catch a sender stuck in a loop. Crossing it answers 429 with `code: DELIVERY_CEILING_REACHED` instead of a success body, and the company owner is emailed. It resets at midnight.
    - `over_plan` boolean: True when this send took the company past its plan quota, or it already was. Delivery happened either way.
    - `scope` 'daily' | 'monthly', nullable: Which quota is exceeded. `monthly` wins when both are: the daily counter clears overnight, the monthly one does not. Null when nothing is exceeded.
    - `used` integer: Notifications counted against `scope` so far in its period (the daily counter when nothing is exceeded).
    - `limit` integer, nullable: The plan quota for `scope`. Null means the plan does not cap it.
    - `over_by` integer: How far past `limit` this send leaves the company. 0 when within quota.
    - `ceiling` integer: Daily anti-abuse ceiling in notifications. 0 means no ceiling is configured.
    - `ceiling_reached` boolean: Always false in a success body — a send that hit the ceiling answers 429 instead. Present so one schema describes both outcomes.
    - `daily_used` integer: Notifications sent today, owner-fallback rescue emails included — those are real messages on the wire. The denominator for `ceiling`.
  - `owner_fallback` OwnerFallbackNotice: Present on a send response only when the alert reached NOBODY it was addressed to and the umbrella guarantee fired instead: the company owner was emailed that delivery failed. **This is the one case where `success: true` does not mean somebody was paged.** The response reads as a success because the alert was rescued rather than lost, and because retrying it would only mail the owner a second time — but the person it was addressed to received nothing. Never report the alert as delivered when this object is present; report that delivery failed and the owner was told. `reason` says what to repair, and it is the same vocabulary the rest of the API uses for delivery verdicts: `no_deliverable_route` (no channel could carry it — `GET /recipients/{recipient}` and `GET /capabilities` say which of the recipient, their contacts or the installation is the blocker), `delivery_failed` (routes existed and every attempt failed at send time), `escalation_exhausted` (the chain ran through every step and reached nobody), `group_unreachable` (a group fan-out reached none of its members). What the email itself contains is an operator setting, not an API one: either the alert text, or — with the redacted mode switched on — only the fact, the reason and a link to the alert in the dashboard. Either way it is a real outbound email: it counts toward the daily anti-abuse ceiling, and it is reported separately from alerts in the usage report, because it is the consequence of a failed delivery rather than traffic the account asked for.
    - `engaged` boolean: Always true when the object is present.
    - `delivered` boolean: Whether the email to the owner itself went out. False means the alert was lost outright — the loudest failure this API can report.
    - `reason` 'no_deliverable_route' | 'delivery_failed' | 'escalation_exhausted' | 'group_unreachable': Why the intended delivery did not happen.
    - `intended_recipient_uuid` string, uuid: The recipient that got nothing. This is the one to repair.
    - `alert_notification_id` string, uuid: UUID of the original alert — the one that failed. Poll THIS through `GET /notifications/{notification}/status`; it stays `failed`. The top-level `notification_id` of the response names the owner email instead, and that one is `completed`.

## Other responses

- `401`: API key missing, invalid, revoked or expired.
- `403`: API key lacks the notifications.send ability.
- `404`: The recipient uuid exists, but not in this company. Deliberately indistinguishable from a typo — the API never confirms that somebody else's recipient exists. Note it is a 404 and not a 403: nothing here says the uuid is real.
- `422`: Validation error
- `429`: Throttled, or the anti-abuse delivery ceiling was reached (`code: DELIVERY_CEILING_REACHED`). The plan quota alone never produces a 429 here.

---

[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)
