Sending

One address, one simple JSON shape — anything that can make an HTTP POST is a sender. Start with a line of text and add structure only when you need it.

Just a line of text

Send a plain string and it shows up as a basic notification. Putting the token in the URL is the quickest way to test from a script:

curl
curl -d "Nightly backup complete" \
  https://notify-server.saasflare.dev/v1/send/ntfy_...

A structured card

Send JSON to get a real card — a title, a status, buttons. For anything beyond a quick test, pass the token in the Authorization header so it stays out of URLs and logs.

POST /v1/send
curl -X POST https://notify-server.saasflare.dev/v1/send \
  -H 'Authorization: Bearer ntfy_...' \
  -H 'Content-Type: application/json' \
  -d '{
    "category": "deploy",
    "template": "deploy",
    "data": {
      "title": "Build finished",
      "branch": "main",
      "commit": "a1b2c3d",
      "status": "ok",
      "duration": "2m13s"
    },
    "actions": [{ "label": "View logs", "url": "https://ci.example.com/run/42" }]
  }'

The fields

Every field is optional. The only thing you really need is something to say — everything else just makes the card richer.

fielddefaultwhat it does
category"general"Groups messages in the sidebar and drives unread counts. Any short label you like.
template"text"Which card to draw (text, deploy, alert). An unknown name just falls back to text — your message is never dropped.
dataThe fields that fill in the card. Missing or odd values degrade gracefully.
styleSmall tweaks: accent (hex color), icon (emoji), urgency (low / normal / critical).
actionsUp to 8 link buttons { label, url }. Only http(s) links show; they open in your browser.

What you get back

statusbodymeaning
200{ id, delivered: "online" }Pushed straight to your desktop.
200{ id, delivered: "queued" }Desktop was offline — saved and delivered when you reconnect.
400{ error, issues }Something in the payload was off; issues says exactly what.
401{ error }Token is missing or revoked.