API reference menu
Getting started
OverviewAuthentication and rate limitExamplesSending
Send a notificationPriority and repeatLinks, images and buttonsSourcesScheduled sendsWebhook templatesReceipts and replies
Receipts and callbacksRepliesTeams
Teams, roles and endpointsChannels and escalationWebhooks and emailAlert email and timelineTeam formsPlans, audit log and transferCompany sign‑in and SCIMThe web consoleAccount
Identity and moving phonesDevices, keys and historyThe Upgrade PassCompatibility
ntfy's publish APIProwl's APIAppendix
Endpoint indexErrors and status codesLimitsPriorities and fieldsGlossaryDevices, keys, history, sources
Used by the iOS app. Every one takes an account key, and every one is scoped to that account's own data. An id belonging to someone else returns 404, never their row.
| Method | Path | Notes |
|---|---|---|
POST |
/v1/devices |
{apns_token,. platform is ios, ipados, macos, watchos or visionos; left out, it is worked out from model, and the device list returns it either way. environment is production or sandbox; see below. The first device is free; more are part of the Upgrade Pass. Re-registering a token already on the account is an update, never a new device, so a reinstall or a restore never counts against the limit. |
GET/DELETE |
/ |
|
POST |
/v1/keys |
{"label": "…"} → the key, shown once. The first is free; more are part of the Upgrade Pass (402 upgrade_required). |
GET |
/v1/keys |
Never includes the key itself; only its prefix, usage and remaining calls |
PATCH |
/v1/keys/{id} |
Rename |
POST |
/ |
New key, same label, old one dead immediately |
DELETE |
/v1/keys/{id} |
Revoke |
GET |
/ |
?limit=&before=&unread_only=. Goes back as far as the account's chosen history length: 30 days by default, or 90, 180 or 365 with the Upgrade Pass. Older rows are deleted hourly, not hidden. |
POST |
/ |
{"ids": [...]} or {"ids": null} for all. Stops any repeat. |
POST |
/ |
Stops Repeat Until Opened without marking it read, for anyone who keeps unread as a to‑do list. Each notification in the list carries repeating: true while it is still repeating. |
DELETE |
/ |
|
GET |
/v1/sources |
What has been sending, with each source's colour and silent, and last_sent_at (null for a name registered and not used yet) |
PATCH |
/v1/sources/{id} |
{"priority_cap": 1}: the highest priority this source's notifications arrive at (-1, 0 or 1; 2 removes it). It only lowers, never raises, and applies from the next notification on; capping below 2 also stops one already repeating. {"muted": true}: still recorded, not pushed. Also {"colour": "violet"} (one of amber, blue, graphite, green, indigo, rose, violet, or "" to clear) (color is accepted as the same field), {"silent": true}, {"sound": "knock"} for a tone of its own (Upgrade Pass; "" goes back to the priority's), and {"repeat_every": 60, for its own Repeat Until Opened (Upgrade Pass; 0 turns it off for this source, -1 follows the account's). Each is independent: a field left out is unchanged, never cleared. |
DELETE |
/v1/sources/{id} |
Forget the name. History is untouched; it reappears if used again |
GET |
/health |
Includes apns_configured |
On sources: you never create one. The source you put in a send is recorded the first time
it is seen, so a new name simply appears in the app with a mute switch. The cost of that is a typo
creating a name that sits there forever, which is what DELETE /v1/sources/{id} is for.
On environment: a Development-signed build's device token is only valid against Apple's
sandbox APNs host, and an App Store build's only against production. Sending to the wrong one
returns BadDeviceToken, which naive servers read as "phone is gone" and deactivate, after which
notifications stop silently and permanently. The app reports which kind of build it is, and the
server retries the other host once on a refusal and writes the correct answer back to the device
row. A wrong guess costs one extra request, once, instead of every notification thereafter.