Skip to content
Nowlark
API reference menu

Errors and status codes

The error shape, and each HTTP status the API returns, with the line that explains it.

Errors#

Every error is JSON with a machine-readable error and a human message.

Status error Means
400
invalid_request
Missing source, no title and no body, a non-http URL, priority out of range, an image_id this account did not upload or that has expired
401
auth_required
No key sent
401
invalid_key
Unknown or revoked key
401
wrong_credential
You sent an account key (nla_) where an API key (nlk_) belongs
402
upgrade_required
The send used a feature this account has not unlocked, e.g. image
409
image_store_full
This account already holds 50 MB of uploaded images
413
image_too_large
An uploaded image is over 5 MB
415
unsupported_image
An uploaded file is not a PNG or a JPEG
429
rate_limited
1000/hour exceeded
429
too_many_scheduled
200 scheduled sends are already waiting
403
wrong_credential
A channel key was used for something other than sending to its channel

Errors from the account endpoints arrive wrapped as {"detail": {"error": …, "message": …}}, which is FastAPI's shape. The bundled clients unwrap both.

Status codes#

Each row is the line in which the status appears, and the section it comes from.

StatusWhere it is said
200
The same answer sent again (an offline retry) is 200 with "already":true; a different one is 409 with the answer that stands.In Replies
200
That is one conditional update on the thread, so two members tapping together cannot both win: the other gets 409 with the answer that stands (200 with "already":true if they tapped the same verb).In Replies
200
Two members at the same moment get one 200 and one 409 already_claimed naming who has it.In Team forms
400
A body that is not that tool's shape is a 400, never a 500.In Webhook templates
400
A literal non-public address is a 400 when sent; a name that resolves privately later is simply never called.In Receipts and callbacks
400
It replaces actions rather than sitting beside them (400 if both are sent).In Replies
400
A reply to a notification with no reply set is 400, a verb outside the set is 400, and another account's notification is 404.In Replies
400
callback_url and callback_secret are still refused on a channel send (400); the receipt is the way back, as for any channel send.In Replies
400
invalid_request
A link is something a phone opens on a tap, so the scheme is not a remote sender's to choose: anything else is refused 400 invalid_request.In Links, images and buttons: Links
400
invalid_request
An id another account uploaded, or one that has expired, is refused 400 invalid_request, and so is a send with both image and image_id.In Links, images and buttons: Images
400
callback_url is refused (400), not ignored: read the receipt instead.In Channels and escalation: Sending to a channel
400
unknown_template
An unknown name is a 400 unknown_template.In Team forms
400
Finding the company: POST /v1/sso/discover {"email": "pat@yourcompany.com"} (no credential; 30 lookups an hour from one IP address) returns {"org": "<sign-in name>", "name": "<team name>"} when the email's domain is one a team has verified (Email domains, below) and that team has company sign‑in set up, and {"org": null} for any other well-formed address, so the answer does not tell anyone which domains have a team (a malformed one is 400, too many lookups 429).In Company sign‑in and SCIM: Company sign‑in (Okta and other OpenID Connect providers)
400
PUT /v1/me/history: One of history_choices: 30 for everyone, 90, 180 or 365 with the Upgrade Pass (402 upgrade_required without it, 400 for a length not on offer).In Identity and moving phones: Identity
400
PUT /v1/me/sounds: Choosing a tone is part of the Upgrade Pass (402 upgrade_required); a name not on offer is 400.In Identity and moving phones: Identity
400
PUT /v1/me/repeat: every is one of repeat_every_choices (0 is off), for one of repeat_for_choices; anything else is 400.In Identity and moving phones: Identity
401
401 if the key is unknown or revoked.In Authentication and rate limit: GET /v1/key/check
401
Errors are SCIM errors; a bad token is one 401 for every reason; a duplicate userName is a 409 uniqueness.In Company sign‑in and SCIM: Automatic provisioning (SCIM 2.0)
402
upgrade_required
image: Part of the Upgrade Pass; without one the send is refused 402 upgrade_required.In Send a notification: POST /v1/notify
402
Refused now, not later. A bad field, a feature the account has not unlocked (402), or too many waiting (429 too_many_scheduled, 200 per account) is the response to the call that asked, never a silent failure at the time it was due.In Scheduled sends: Scheduled sends
402
upgrade_required
Without it, POST /v1/images returns 402 upgrade_required.In Links, images and buttons: Images
402
upgrade_required
PUT /v1/me/history: One of history_choices: 30 for everyone, 90, 180 or 365 with the Upgrade Pass (402 upgrade_required without it, 400 for a length not on offer).In Identity and moving phones: Identity
402
upgrade_required
PUT /v1/me/sounds: Choosing a tone is part of the Upgrade Pass (402 upgrade_required); a name not on offer is 400.In Identity and moving phones: Identity
402
upgrade_required
POST /v1/keys: The first is free; more are part of the Upgrade Pass (402 upgrade_required).In Devices, keys and history
403
forbidden
A team you are not on answers 404, the same as one that does not exist, and a role too low answers 403 forbidden.In Teams, roles and endpoints
404
DELETE /v1/scheduled/{id} cancels one that has not fired; once it has been picked up it is on its way and the answer is 404.In Scheduled sends: Scheduled sends
404
Any key on the account may ask, only for its own account's notifications; another account's id is 404, the same as one that does not exist.In Receipts and callbacks
404
A reply to a notification with no reply set is 400, a verb outside the set is 400, and another account's notification is 404.In Replies
404
A team you are not on answers 404, the same as one that does not exist, and a role too low answers 403 forbidden.In Teams, roles and endpoints
404
invalid_code
POST /v1/teams/join {"code"}: Redeem an invite. 20 attempts an hour; every failure is one 404 invalid_code.In Teams, roles and endpoints
404
A suspended or unknown form is the same 404.In Team forms
404
Who sees it. Admins see every form; anyone else only forms announcing to a channel they are on, and a form they may not see is a 404, the same as one that does not exist.In Team forms
404
A 404 or 410 means the webhook was deleted on the service's side and is not retried.In Team forms: Post to Slack, Microsoft Teams, Discord or a webhook
404
invalid_handoff
A handoff is single use, expires after two minutes, and is refused (404 invalid_handoff) for any account but the one that began the sign‑in.In Company sign‑in and SCIM: Company sign‑in (Okta and other OpenID Connect providers)
404
An id belonging to someone else returns 404, never their row.In Identity and moving phones
409
later is an answer, not a snooze: it closes the question like any other, so a later approve gets 409.In Replies
409
The same answer sent again (an offline retry) is 200 with "already":true; a different one is 409 with the answer that stands.In Replies
409
That is one conditional update on the thread, so two members tapping together cannot both win: the other gets 409 with the answer that stands (200 with "already":true if they tapped the same verb).In Replies
409
too_many_sources
409 too_many_sources past 500 names on one account.In Sources: POST /v1/sources
409
image_store_full
50 MB of images per account. Past that, POST /v1/images returns 409 image_store_full.In Links, images and buttons: Images
409
sso_required
POST /v1/teams/join {"code"}: 409 sso_required or 409 provisioned when the team adds people through its company sign‑in or its identity providerIn Teams, roles and endpoints
409
provisioned
POST /v1/teams/join {"code"}: 409 sso_required or 409 provisioned when the team adds people through its company sign‑in or its identity providerIn Teams, roles and endpoints
409
escalation_needs_any
Needs a channel whose ack_policy is any (409 escalation_needs_any otherwise): on all every member must open it, so there is no "nobody has" to escalate on.In Channels and escalation: Escalation
409
email_not_configured
Email needs the operator to configure a relay; without one, adding an email destination is 409 email_not_configured.In Webhooks and email
409
already_claimed
Two members at the same moment get one 200 and one 409 already_claimed naming who has it.In Team forms
409
already_exists
POST /v1/teams/{id}/connections: 422 bad_url for an address that is not that service's webhook, 409 already_exists for one already connected, 409 too_many_connections past 20 per team (any number of rules can share one)In Team forms: Post to Slack, Microsoft Teams, Discord or a webhook
409
too_many_connections
POST /v1/teams/{id}/connections: 422 bad_url for an address that is not that service's webhook, 409 already_exists for one already connected, 409 too_many_connections past 20 per team (any number of rules can share one)In Team forms: Post to Slack, Microsoft Teams, Discord or a webhook
409
connection_in_use
DELETE /v1/teams/{id}/connections/{connection_id}: 409 connection_in_use, naming the rules, while a rule posts to itIn Team forms: Post to Slack, Microsoft Teams, Discord or a webhook
409
hours_in_use
PUT /v1/teams/{id}/hours: 409 hours_in_use, naming the rules, for a time zone alone while a rule tests business_hoursIn Team forms: Business hours
409
hours_in_use
DELETE /v1/teams/{id}/hours: 409 hours_in_use, naming the rules, while any rule tests business_hours, time_of_day or weekday, since they would move to UTCIn Team forms: Business hours
409
stale_order
PUT /v1/teams/{id}/rules/order: A list that misses or repeats one is 409 stale_order (someone may have just added a rule): read the list again and resendIn Team forms: Business hours
409
too_many_rules
A team has up to 50 rules (409 too_many_rules past that).In Team forms: Business hours
409
no_test
While no test runs, the three admin routes answer 409 no_test, and starting a second one answers 409 test_running.In Team forms: Two versions of a form
409
test_running
While no test runs, the three admin routes answer 409 no_test, and starting a second one answers 409 test_running.In Team forms: Two versions of a form
409
no_google_secret
If GA4's validation server reports a problem with the event, the answer is "sent":false with Google's description in message; without a saved secret it is 409 no_google_secret.In Team forms: Google Analytics and Tag Manager
409
plan_active
409 plan_active while the team already has a live subscription.In Plans, audit log and transfer: Plans and billing
409
trial_unavailable
409 trial_unavailable when the account has had its trial, the team already has a plan, or the owner's Upgrade Pass already covers the team.In Plans, audit log and transfer: Plans and billing
409
Refused with 409: plan_active while a merchant subscription is live (it belongs to whoever pays it: cancel, hand over, subscribe again) and new_owner_cannot_carry_it while billing is not open and the new owner's own Upgrade Pass would not let the team send.In Plans, audit log and transfer: Ownership transfer
409
Without app=1, the same address signs a browser in to the console and links the company account to the phone that made the code, so the phone is the member and gets the alerts; a company account already linked to another Nowlark account is refused (409).In Company sign‑in and SCIM: Company sign‑in (Okta and other OpenID Connect providers)
409
record_not_found
409 record_not_found until it is (DNS changes can take a few minutes).In Company sign‑in and SCIM: Email domains
409
domain_taken
A domain is verified for one team only: once one team has verified it, another gets 409 domain_taken.In Company sign‑in and SCIM: Email domains
409
uniqueness
Errors are SCIM errors; a bad token is one 401 for every reason; a duplicate userName is a 409 uniqueness.In Company sign‑in and SCIM: Automatic provisioning (SCIM 2.0)
409
plan_active
DELETE /v1/me: 409 plan_active while a team it owns has a live subscription with the merchant (the team would go and the charges would not): cancel that first.In Identity and moving phones: Identity
410
expired
After reply_within the answer is refused (410 expired).In Replies
410
A 404 or 410 means the webhook was deleted on the service's side and is not retried.In Team forms: Post to Slack, Microsoft Teams, Discord or a webhook
413
A body over 256 KB is 413.In Webhook templates
413
image_too_large
Over 5 MB is 413 image_too_large.In Links, images and buttons: Images
429
too_many_scheduled
Refused now, not later. A bad field, a feature the account has not unlocked (402), or too many waiting (429 too_many_scheduled, 200 per account) is the response to the call that asked, never a silent failure at the time it was due.In Scheduled sends: Scheduled sends
429
Exceeding it returns 429 with {"error": "rate_limited"}.In Authentication and rate limit: Rate limit
429
Finding the company: POST /v1/sso/discover {"email": "pat@yourcompany.com"} (no credential; 30 lookups an hour from one IP address) returns {"org": "<sign-in name>", "name": "<team name>"} when the email's domain is one a team has verified (Email domains, below) and that team has company sign‑in set up, and {"org": null} for any other well-formed address, so the answer does not tell anyone which domains have a team (a malformed one is 400, too many lookups 429).In Company sign‑in and SCIM: Company sign‑in (Okta and other OpenID Connect providers)
500
A body that is not that tool's shape is a 400, never a 500.In Webhook templates
501
Groups answers an empty list and refuses writes (501): roles still come from the groups in the sign‑in.In Company sign‑in and SCIM: Automatic provisioning (SCIM 2.0)

Prowl's status codes#

Answered on Prowl's own API. Prowl's, not HTTP's obvious ones. A client written against Prowl branches on exactly these:

Code Meaning
200
Accepted
400
Bad request: no application, no event or description, a non-http url, priority out of range
401
Unknown or missing key. One bad key in a comma-separated list refuses the whole call and sends nothing
406
The key's 1000/hour is spent. Not 429: Prowl used 406 and clients back off on it
500
Something unexpected

ntfy's errors#

Answered on ntfy's publish API. Errors are ntfy's JSON ({"code": 40101, "http": 401, "error": "…"}), and a key that does not work is always 401, which is what a tool's "Test" button reports as needing authorization.