ntfy's Query-String API Is More Powerful Than the Docs Make It Look

Coding#ntfy#webhooks#api

Most of this Pi's alerting runs through ntfy already. Turns out you can do a lot more with it than I expected — without writing a receiving server at all.

I already run most of this Pi’s alerting through ntfy — boot/shutdown notifications, uptime checks, Lighthouse CI score regressions, all of it hitting one topic via a tiny curl wrapper. So when a client asked for a push notification whenever a sale comes through on their WordPress site, ntfy was the obvious answer. What surprised me was how much you can do with it without writing a receiving server at all — just query parameters and, if you want client-side confirmation, a plain fetch() call.

The zero-infrastructure version

Most WordPress e-commerce plugins (WooCommerce included) have a native webhook feature: pick an event, give it a URL, it POSTs there. Point that straight at an ntfy topic and you already have a working sale notification — no server of your own required:

https://ntfy.sh/your-topic-name

The catch: WooCommerce’s native webhook only sends the full order as a raw JSON body, with no templating. The push notification you get just shows a blob of JSON as the message text. Not exactly “New sale: $49 from Jane.” Usable as a bare “something happened” signal, but not great.

Where the query-string API comes in

This is the part I hadn’t really appreciated until I went looking: ntfy accepts a bunch of its options as either HTTP headers or query parameters — ?title=, ?priority=, ?tags=, ?click=. That means even without touching the message body, you can make a webhook-triggered push meaningfully more readable just by putting those in the delivery URL:

https://ntfy.sh/your-topic?title=New+Sale&priority=high&tags=moneybag

Now instead of a wall of JSON with no context, you get a title that actually says what happened, a priority level that controls whether it interrupts you, and a tag that renders as an emoji in the notification. The body’s still raw order data, but the header of the notification — the part you actually read at a glance — is exactly what you want it to say.

click is the underrated one

The parameter that ends up mattering most once you’re actually living with these notifications day to day is click — it makes the entire notification a tap target that opens a URL, rather than requiring you to open the app and find a link buried in the message text. Since ntfy.sh sends Access-Control-Allow-Origin: * and answers CORS preflight requests properly, you can even trigger a POST with all of these set directly from client-side JavaScript — a plain fetch() call from a web page, no backend involved at all:

fetch('https://ntfy.sh/your-topic', {
  method: 'POST',
  headers: {
    'Title': 'New Sale',
    'Tags': 'moneybag',
    'Priority': 'high',
    'Click': 'https://your-order-admin-url/'
  },
  body: 'Order #1234 — $49.00'
});

Tap the notification, land directly on the order. No app-switching, no hunting.

If you want real templating

For an actual “New sale: $49 from Jane” message with real order fields substituted in, you need something willing to build the message body itself — a webhook-formatting WordPress plugin, an automation tool like Zapier, or a small receiver of your own that reads the order JSON and posts a formatted string to ntfy. That’s a reasonable next step if the bare title/tags version stops being enough. But it’s worth knowing the zero-code version exists first — for a lot of “just tell me when X happens” cases, query parameters alone get you most of the way there.