Skip to content

Wrist Webhooks

Wrist Webhooks give you a private URL that turns anything that can make an HTTP request into a notification on your wrist. A backup script, a CI pipeline, Uptime Kuma, a Grafana alert, an ESPHome sensor, a cron job, even a plain email — curl it and your watch buzzes. Home Assistant is not in the loop, so a webhook keeps working even when HA itself is down (a UPS on battery is the classic case).

It’s the inbound twin of HTTP Actions: HTTP Actions fire requests from your wrist, Wrist Webhooks deliver alerts to it. One general-purpose primitive, hundreds of uses.

  • Alert from a scriptcurl one line at the end of a backup, deploy, or batch job and get a buzz when it finishes or fails.
  • Wire up a monitor — point Uptime Kuma, Grafana, or Healthchecks at your URL and get outages on your wrist.
  • Ask a question and wait — send a notification with Ship it / Abort buttons and have your script block until you tap one.
  • Buzz from a device — an ESPHome node or an old router that can only do a GET can still reach you.
  • Send by email — give your webhook an email address and forward any message to your wrist.

Open Settings → Tools & Automations → Wrist Webhooks and tap Create My Webhook URL. Provisioning takes one of two paths automatically:

  • With Home Assistant connected — the URL is provisioned over your existing secure Home Assistant channel, which also registers your watch so it can receive on every route.
  • Without Home Assistant — the relay sends a silent verification push to your device and the app echoes it back to prove the wrist is yours. No Home Assistant required; just keep the app open until it finishes.

Either way you end up with a private URL of the shape:

https://push.wrist-assistant.com/w/<your-token>/<topic>

The app shows the full URL with Copy, Share, and a QR code. The token is the only credential — treat the URL like a password (see Security).

The last path segment is the topic — you invent it on the spot (backups, ci, alerts); there’s no need to create it first. Topics are lowercase (a-z, 0-9, _, -).

The simplest possible push — the request body becomes the message:

Terminal window
curl -d "Backup finished" "https://push.wrist-assistant.com/w/<your-token>/backups"

A notification has a handful of fields, and you can send them two equivalent ways: as headers, or as a JSON body.

As headers (the body is the message; everything else is a header):

Terminal window
curl -H "Title: Deploy" -H "Priority: 4" -H "Tags: rocket" \
-d "Shipping 1.2.3 to production" \
"https://push.wrist-assistant.com/w/<your-token>/deploys"

As JSON (handy for senders that already emit JSON, like Grafana):

Terminal window
curl -H "Content-Type: application/json" \
-d '{"title":"Deploy","message":"Shipping 1.2.3","priority":4,"tags":["rocket"]}' \
"https://push.wrist-assistant.com/w/<your-token>/deploys"

The fields you can set:

  • Message — the main text. Send it as the request body, the message JSON key, or the Message header.
  • Title — an optional bold line above the message.
  • Priority15, or a name (min, low, default, high, max). 12 are silent, 34 play a sound, 5 is time-sensitive and buzzes through a Focus.
  • Tags — small labels. Many render as an icon or emoji in the notification (warning, rocket, backup, disk, …).
  • Click — an http/https link opened when you tap the notification. (Links open on the iPhone — the watch has no browser.)
  • Collapse — a key that replaces an earlier notification with the same key, so a job can stream progress (47% → 80% → done) instead of stacking up banners.
  • Cache — set to no to deliver the message without storing it in the inbox.
ValueNameOn the device
1minsilent
2lowsilent
3defaultsound
4highsound
5maxtime-sensitive (buzzes through Focus)

For devices that can only make a GET request, append /publish and pass fields as query parameters:

https://push.wrist-assistant.com/w/<your-token>/alerts/publish?message=Hello&title=Heads%20up&priority=4

URL-encode the values. This form is fire-and-forget.

Attach up to three buttons to a notification and act on it without opening anything. There are four kinds, and they’re available on a free webhook:

KindWhat it doesWhere it works
respondAnswers you back through the relay — no server of your own needediPhone + watch
httpFires an HTTP request from your deviceiPhone + watch
actionRuns a named HTTP Action from your own librarywatch
viewOpens a URLiPhone (the watch has no browser)

The header grammar separates buttons with ; and fields with ,:

Terminal window
curl -H "Title: Deploy 1.2.3?" \
-H "Actions: respond, Ship it, ship; respond, Hold, hold" \
-d "Approve the production deploy?" \
"https://push.wrist-assistant.com/w/<your-token>/deploys"

For anything with commas, bodies, or headers, the JSON form is clearer:

Terminal window
curl -H "Content-Type: application/json" -d '{
"title": "Garage left open",
"message": "Open for 20 minutes",
"actions": [
{ "kind": "action", "label": "Close it", "name": "Close Garage" },
{ "kind": "view", "label": "Camera", "url": "https://ha.local/lovelace/cameras" }
]
}' "https://push.wrist-assistant.com/w/<your-token>/home"
  • Icons & colors — give a button a leading glyph with icon=<name> (any SF Symbol name, or a friendly shortcut like check, xmark, rocket, lock) and tint it with color=<name> from a curated palette (red, green, blue, …). An unrecognized icon falls back to text-only; an unrecognized color falls back to the default fill.
  • Confirm gate — add confirm=true to arm a destructive button on the first tap (“tap again to confirm”) and fire it on the second.

A respond button sends your choice back to the publisher with no server of its own — perfect for a cron job or CI step that has nowhere to receive a callback. The publish response includes a response_ws_url; connect a WebSocket to it and the relay holds the connection (for free, hibernated) until you tap:

Terminal window
resp=$(curl -s -w '\n%{http_code}' -H "Actions: respond, Ship it; respond, Abort" \
-d "Deploy 1.2.3?" "https://push.wrist-assistant.com/w/<your-token>/deploys")
code=$(printf '%s' "$resp" | tail -n1)
body=$(printf '%s' "$resp" | sed '$d')
if [ "$code" = 201 ]; then
ws=$(printf '%s' "$body" | jq -r .response_ws_url)
answer=$(websocat -1 -n -U "$ws" | jq -r .answer.value)
[ "$answer" = "Ship it" ] && ./deploy.sh
else
echo "publish rejected ($code): $body"
fi

curl can publish but can’t speak WebSocket, so waiting needs a WS client like websocat. The Command Builder (below) generates one for you in its Script section, under WebSocket (waits for answer). The snippet checks the publish returned 201 first — a rejected publish (most often the 429 daily cap — free sends 10 pushes/day, Pro 10× more at 100/day) has no response channel, so it prints the error and stops rather than hang. In-app sends from the app don’t count against that cap; external scripts like this do.

Every webhook also has an email address — forward a message to it and it lands on your wrist:

<your-token>@in.push.wrist-assistant.com

The subject becomes the title, the body becomes the message, and the sender becomes a tag. Target a specific topic with a plus-suffix:

<your-token>+alerts@in.push.wrist-assistant.com

The app surfaces the email address next to your URL (an Email tile in the Wrist Webhooks settings).

Two tools in the Wrist Webhooks settings get you going without touching a terminal:

  • Send a Test Push — fires a sample notification (to the test topic) so you can confirm the whole loop end to end in about ten seconds, buttons and all.
  • Build a Command — a form for the message, title, topic, priority, sound, tags, a link, and up to three buttons, with a live iPhone-and-watch preview so you see exactly how it’ll look. Pick a Starter Template, then open the Script section to copy a ready-to-run command — Simple curl (fire and forget), or WebSocket (waits for answer) when a button replies to you. An encrypted webhook instead gets a single self-contained Node command that does both (needs Node 22.4+).

Compose a notification in the Command Builder and Save it to keep it as a reusable preset on your iPhone. Each row shows its topic and message; tap to edit, tap the paper-plane to fire it, or long-press to rename or delete.

Saved Commands hold no secrets — the URL and any key are applied only when you send — so they back up safely. Deleting your webhook clears your saved commands.

You never have to create a topic to publish to it. Add one in Settings → Tools & Automations → Wrist Webhooks → Topics only when you want to manage it:

  • Mute — silence a topic for 1 hour, 8 hours, or 24 hours (or off). Muted topics still collect in your inbox; they just don’t buzz.
  • Max Priority — cap a topic at a priority level, so a chatty source can never send you a time-sensitive 5.

Both are enforced at the relay, at publish time — a muted or capped message never wastes a push.

The relay keeps every notification for 48 hours (unless you sent it with Cache: no), so a buzz you missed isn’t lost:

  • On iPhone, the Recent Messages list pulls the latest on open — pull to refresh, and answered or dismissed messages show their state. It’s read-only history.
  • On the watch, add a Wrist Webhooks Inbox tile in the editor — for all topics or a single one — to read recent messages and unread counts at a glance.

How a notification reaches your wrist is an account-wide choice in the Wrist Webhooks settings:

  • Phone & Watch (default) — delivered to your iPhone and mirrored to the watch, fast (about a second) and rich. Needs your iPhone reachable.
  • Watch Only — goes straight to the watch, so it works with your iPhone off or in another room. Delivery is slower (around 15 seconds) and does not reach your iPhone.
  • Watch Instant — sub-second delivery straight to the watch, using the companion Wrist Notifier app (below). Falls back to mirroring automatically if the Notifier isn’t paired. Also does not reach your iPhone.

Watch Instant delivery is powered by Wrist Notifier, a small companion watch app you install separately from the App Store. Once installed:

  1. Open Wrist Notifier on the watch and tap Pair with iPhone to get a code.
  2. Enter the code in Settings → Tools & Automations → Wrist Webhooks → Pair Notifier.

Paired, Wrist Notifier shows its connection status, your last alert, an inbox, and an Unpair button, and unlocks Watch Instant delivery. If you use encryption, your key is sealed to the Notifier at pairing time so it can decrypt too.

Turn on End-to-End Encryption (in the Wrist Webhooks settings, under Encryption) and the relay cannot read your messages. The key is generated on your device and never sent to the server; you reveal it in Settings (copy or QR) to hand to your publishers alongside the URL.

  • What stays private — the message body, tags, click link, and buttons. These appear only once your own devices decrypt them.
  • What’s still visible — the topic, priority, timing, and the message title. The title is cleartext by your choice per message: the lock-screen banner is drawn by the OS before the app can decrypt, so a title you want glanceable must be sent in the clear. Omit it for a fully generic banner.
  • Publishing — a plain curl is rejected (encryption_required). Use the Node command the Command Builder generates, which encrypts on your machine. (The email gateway is unavailable on an encrypted webhook.)

The Wrist Webhooks settings have a Security section with two controls:

  • Rotate Publish Token — your leak-recovery button. The old URL stops working immediately and any script using it must be updated to the new URL. Everything else — your topics, inbox, encryption, delivery setting, and Notifier pairing — is preserved.
  • Delete Webhook — revokes everything at the relay (irreversible) and clears the credentials from your iPhone and watch.

The URL is the only credential. If it leaks, Rotate — you don’t need to start over.

Every field can be sent as a header (with or without an X- prefix), a JSON key, or a GET query parameter.

FieldHeader (aliases)JSON keyGET param
MessageMessage / X-Message (or the body)message / bodymessage / m
TitleTitle / X-Titletitletitle / t
PriorityPriority / X-Priorityprioritypriority / p
TagsTags / X-Tagstags (array or CSV)tags
ClickClick / X-Clickclickclick
CollapseCollapse / X-Collapse (or Tag)collapsecollapse
CacheCache / X-Cachecache (bool)cache
ActionsActions / X-Actionsactions (array)actions
EncryptionEncryption / X-Encryption
  • HTTP Actions — fire requests from your wrist; a webhook’s action button runs one of these.
  • Notifications — Home Assistant–originated notifications and their delivery options.