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

# Send notification to recipient (async, queued)

`POST /api/v1/notifications/send-async`

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

## 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 `202`

Notification queued. 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`.

## 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.

---

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