Sending notifications
One POST request queues a notification for every browser connected to a recipient.
/v1/notificationssk_live_… or sk_test_…)Send a JSON body with the recipient's name and a title. Those are the only required fields; every other option is listed in Message options.
The request#
Every request carries two headers:
⫻
headers
- The body may be at most 16,384 bytes. The notification itself has a smaller limit; see size limits.
- The body is strict: an unknown field, at the top level or inside an action, fails with
400 VALIDATION_ERROR, so a typo fails loudly instead of being ignored. - Add an
Idempotency-Keyheader to make retries safe. See Idempotency.
The response#
A new notification is answered with 202 Accepted as soon as it is stored and queued:
⫻
202 Accepted
| Field | Meaning |
|---|---|
| id | The notification id. Read it back with GET /v1/notifications/{id}. |
| status | Always queued in this response. Each attempt then moves on by itself; see Statuses. |
| attempts | How many connected browsers the notification was queued for. Each one is a separate attempt with its own status. |
| mode | live or test, taken from the API key that sent it. |
What happens next#
- Push creates one attempt per browser connected to the recipient at that moment.
- A sender hands each live attempt to the browser vendor's push service. Each request waits up to 10 seconds for an answer.
200,201and202mean the push service accepted the message. Every other answer that is not listed below, including network errors, timeouts,429and5xx, is retried: up to 6 tries per send job, with exponential backoff starting at 5 seconds. When the last try of a job fails, the attempt becomesfailed. Retries never cost anything.400,403and413from the push service are permanent: the attempt becomesfailed.404and410mean the browser is gone: the attempt becomesinvalid_subscriptionand the browser is disconnected from the recipient, so later sends skip it.- About every 60 minutes, Push queues again any attempt that has sat in
queuedorprocessingfor more than 15 minutes, for example after a sender stopped. The new job gets a fresh budget of 6 tries; this repeats until the attempt reaches a final status or its TTL runs out.
Sending with a test key#
A sk_test_… key runs the same checks as a live key, except two: it is not refused from a browser (403 FORBIDDEN for an Origin header) and it is never charged (402 QUOTA_EXCEEDED). It then records one attempt per connected browser, marked accepted at once. Nothing is sent to any browser and nothing counts toward usage. The recipient still needs at least one connected browser.
When nothing can be sent#
A recipient that does not exist, or has no connected browsers, returns 422 INVALID_RECIPIENT. Nothing is stored and nothing is charged. If the free allowance is used up and the credit balance cannot cover the send, it fails with 402 QUOTA_EXCEEDED, also without storing or sending anything.
Order of checks#
Push checks a request in this order and stops at the first problem:
- The API key. A missing or invalid key first counts against the per-IP limit for invalid keys (
429 RATE_LIMITEDonce it is used up), then fails with401 UNAUTHORIZED. - The project (
403 PROJECT_SUSPENDED). - A live key sent with an
Originheader, which every browser adds (403 FORBIDDEN). Test keys skip this check. - Rate limits for the key, the project and the account (
429 RATE_LIMITED). - The
Idempotency-Keyheader (400 VALIDATION_ERROR). - The body: size, JSON and every field (
400 VALIDATION_ERROR). - The size of the encoded notification (
400 VALIDATION_ERROR). - A replay of an earlier request with the same idempotency key (
200 OKor409 IDEMPOTENCY_CONFLICT). - The recipient and its connected browsers (
422 INVALID_RECIPIENT). - Usage and credits (
402 QUOTA_EXCEEDED).
Sending from the dashboard#
high and a TTL of 86,400 seconds, and it counts toward usage like any other live attempt.