notifi

API reference

notifi API documentation

One endpoint, seven parameters, no SDK. Everything on this page is generated from the same file that generates /openapi.json and the client collections, so the three cannot disagree.

View as Markdown

Quickstart Authentication Request Parameters Response Errors Rate limits Clients and import Machine-readable Recipes

Quickstart

Install notifi on iPhone or iPad or on the Mac, allow notifications, open the Keys tab and copy the Default key. It starts with nk_ and delivers only to the device that made it.

curl -X POST https://notifi.it/send \
  -H "Authorization: Bearer $NOTIFI_KEY" \
  -d "title=Backup complete" \
  -d "message=4.2 GB in 3m 11s"

There is no endpoint that creates a key: keys are minted on the device by a request signed with a private key that never leaves it. A coding agent has to ask you for one. Keep it in the environment, not in a file that gets committed.

Authentication

Authenticate with a bearer token. A key parameter also works, but it is written to edge logs, shell history and any proxy in between.

MethodSent asNotes
Bearer token Authorization: Bearer nk_yourkey The send key from the app’s Keys tab, as Authorization: Bearer nk_yourkey. Preferred: a header is not written to edge logs or shell history.
Parameter key=nk_yourkey The send key as a parameter. Convenient for a one-off request, but it appears in edge logs, in shell history and in any proxy in between.

Request

POST https://notifi.it/send, JSON, form-encoded or multipart. GET takes the same parameters in the query string and is there for a quick test. Query parameters win over body fields when both are present.

POST /send HTTP/1.1
Host: notifi.it
Authorization: Bearer nk_yourkey
Content-Type: application/json
Accept-Language: en-GB

{"title":"Backup complete","message":"4.2 GB in 3m 11s","link":"https://console.internal/backups"}

Parameters

key is required unless the request carries a bearer token. An image is fetched server-side and must be https, PNG, JPEG or GIF, 5 MB at most.

NameTypeRequiredLimitDescription
key string conditional nk_… The send key, if it is not sent as a bearer token. Required unless sent as a bearer token. The key picks the device the notification lands on, so which key a script holds decides where its notifications go.
title string required 1–200 chars The notification title. A longer title is delivered cropped, with a warning in the response.
message string optional ≤ 16,000 chars The notification body, in Markdown. The push shows a short preview; the app renders the full text. A longer body is delivered cropped, with a warning.
link string (uri) optional ≤ 2,048 chars URL opened when the notification is tapped.
image string (uri) optional ≤ 2,048 chars https URL of a PNG, JPEG or GIF up to 5 MB. One that cannot be fetched is dropped, with a warning, and the notification still arrives.
occurred_at integer optional unix ms When the event actually happened, as unix milliseconds. For a queued or retried send. Only changes the timestamp shown in the app; defaults to arrival time.
is_critical boolean optional Breaks through Focus. The key must also have critical alerts switched on in the app, or an ordinary notification is delivered and the response carries a warnings array.

Response

A send answers 202. That means the server accepted it, not that it was delivered — delivery is best-effort, as the terms describe. Every status the endpoint can answer with is here:

accepted
HTTP/1.1 202 Accepted
Content-Type: application/json; charset=utf-8

{"ok":true}

A warnings array is present only when the notification was delivered differently from what was asked: a cropped title or body, a dropped image, or a critical alert downgraded to an ordinary one.

Errors

Every error nests the code one level down. Read error.code, not code. The message is in the language negotiated from Accept-Language and is meant for a human, so match on the code.

Statuserror.codeMeaning
400 invalid_request A parameter is missing or malformed.
401 unknown_key The key is unknown or has been revoked.
422 invalid_content The device is set to refuse a notification it cannot deliver as written.
429 rate_limited Over the hourly device limit or the per-minute IP limit. Carries a Retry-After header with the seconds until the window resets.
404 not_found No such path.
500 internal_error Something broke on our side.

Rate limits

A 429 carries Retry-After in seconds. The device limit is 60 an hour across all 5 keys; the address limit is 100 requests a minute and covers every endpoint.

Clients and import

The collection and the OpenAPI document are generated from the same source as this page. Set NOTIFI_KEY and send.

postman
# Import → Link, then paste this. Postman keeps it in sync from there.
https://notifi.it/notifi.postman_collection.json

Machine-readable

Every page on this site is also served as Markdown: send Accept: text/markdown on the same URL, or append .md. The source covers the app, the API and the cryptography.

There is no MCP server, no webhook API and no OAuth. One endpoint and a bearer token is the whole integration surface, and anything claiming otherwise is not us.

Recipes

The same request from everywhere it tends to get sent from — 13 of them, the same block the home page carries. Each one wants NOTIFI_KEY in the environment.

send.sh
curl -X POST https://notifi.it/send \
  -H "Authorization: Bearer $NOTIFI_KEY" \
  -d "title=Backup complete" \
  -d "message=4.2 GB in 3m 11s" \
  -d "link=https://console.internal/backups"

Questions

The FAQ covers cost, limits, what the server can read and what happens when you delete the app. Anything else goes to contact.