Skip to content
Nowlark
API reference menu

Devices, 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, environment, name, model, platform, os_version, app_version}. 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
/v1/devices[/{id}]
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
/v1/keys/{id}/rotate
New key, same label, old one dead immediately
DELETE
/v1/keys/{id}
Revoke
GET
/v1/notifications
?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
/v1/notifications/read
{"ids": [...]} or {"ids": null} for all. Stops any repeat.
POST
/v1/notifications/{id}/ack
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
/v1/notifications/{id}
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, "repeat_for": 10800} 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.