Skip to content

Sending / Sending notifications

Sending notifications

One POST request queues a notification for every browser connected to a recipient.

POST/v1/notifications
Auth Bearer API key (sk_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

Authorization: Bearer sk_live_…Content-Type: application/json
curl https://api.meslzy.com/push/v1/notifications \  -H "Authorization: Bearer $PUSH_API_KEY" \  -H "Content-Type: application/json" \  -d '{    "recipient": "me",    "title": "Deploy finished",    "body": "api@4f2c1a is live in production",    "url": "https://example.com/deploys/4f2c1a"  }'
  • 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-Key header 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

HTTP/1.1 202 AcceptedContent-Type: application/jsonX-Request-Id: req_0199b1c27a4e7c3d9f1e2b6a8d4c5e11
{  "id": "0199b1c2-7a4e-7c3d-9f1e-2b6a8d4c5e10",  "status": "queued",  "attempts": 2,  "mode": "live"}
FieldMeaning
idThe notification id. Read it back with GET /v1/notifications/{id}.
statusAlways queued in this response. Each attempt then moves on by itself; see Statuses.
attemptsHow many connected browsers the notification was queued for. Each one is a separate attempt with its own status.
modelive 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, 201 and 202 mean the push service accepted the message. Every other answer that is not listed below, including network errors, timeouts, 429 and 5xx, 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 becomes failed. Retries never cost anything.
  • 400, 403 and 413 from the push service are permanent: the attempt becomes failed.
  • 404 and 410 mean the browser is gone: the attempt becomes invalid_subscription and 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 queued or processing for 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:

  1. The API key. A missing or invalid key first counts against the per-IP limit for invalid keys (429 RATE_LIMITED once it is used up), then fails with 401 UNAUTHORIZED.
  2. The project (403 PROJECT_SUSPENDED).
  3. A live key sent with an Origin header, which every browser adds (403 FORBIDDEN). Test keys skip this check.
  4. Rate limits for the key, the project and the account (429 RATE_LIMITED).
  5. The Idempotency-Key header (400 VALIDATION_ERROR).
  6. The body: size, JSON and every field (400 VALIDATION_ERROR).
  7. The size of the encoded notification (400 VALIDATION_ERROR).
  8. A replay of an earlier request with the same idempotency key (200 OK or 409 IDEMPOTENCY_CONFLICT).
  9. The recipient and its connected browsers (422 INVALID_RECIPIENT).
  10. Usage and credits (402 QUOTA_EXCEEDED).

Sending from the dashboard#

WarningSend test on a recipient's page is a real live send, not a test-key send. It goes out with urgency high and a TTL of 86,400 seconds, and it counts toward usage like any other live attempt.
NoteEvery error has the same JSON shape. See Errors.