Home/Guides/Send with curl
Guide · curl

How to send a push notification with curl

If it's in a terminal, it can notify your phone. One curl POST to your Lert URL and you're buzzed within a second. This guide goes deep on the parts that trip people up: form encoding, plain text, quiet-but-safe flags, and checking success in a script.

What you'll need

curl (installed on virtually every Mac, Linux box, and modern Windows) and the Lert app on your iPhone. Copy your endpoint from the app — it looks like https://lert.dev/p/your-id. That URL is the authentication; there's no key or token to add. Keep it private and regenerate it in the app if it ever leaks.

Everything here is one HTTP request, which is why curl is the lowest-common-denominator way to reach your phone: it works in a Makefile target, a Git hook, a systemd ExecStopPost, a Dockerfile healthcheck, or pasted straight into a terminal. Learn the flags once and every script you own inherits them.

The minimal command

Send a JSON body. The title, body, and tag fields are all optional — send whatever subset you like.

curl · JSON
curl -X POST "https://lert.dev/p/your-id" \
  -H "Content-Type: application/json" \
  -d '{"title":"Backup done","body":"42 GB → S3","tag":"nightly"}'

The tag is a free-form label that appears in your in-app history, so you can tell a backup ping apart from a deploy ping later.

Plain text instead of JSON

If you only need a line of text, skip JSON. Lert accepts a raw body — the whole thing becomes the notification.

curl · plain text
curl -X POST "https://lert.dev/p/your-id" \
  -H "Content-Type: text/plain" \
  --data-binary "Server rebooted at $(date +%H:%M)"

The -d gotcha with = and &

Here's the one that bites everyone. curl -d defaults to application/x-www-form-urlencoded. That means an & in your text is read as a field separator and an = becomes a key/value split — so a message like result=ok & done arrives truncated or scrambled.

Two fixes. If you're sending JSON, you're already safe as long as you set the JSON content type. If you're sending plain text that might contain = or &, set Content-Type: text/plain and use --data-binary so curl transmits the bytes exactly as written:

curl · safe plain text
# WRONG — & and = get eaten by form encoding
curl -X POST "https://lert.dev/p/your-id" \
  -d "exit=0 & user=root"

# RIGHT — text/plain + --data-binary sends it verbatim
curl -X POST "https://lert.dev/p/your-id" \
  -H "Content-Type: text/plain" \
  --data-binary "exit=0 & user=root"

--data-binary also preserves newlines, which plain -d strips — useful if you want a multi-line body.

Quiet, but safe, for scripts

In a cron job or CI step you don't want curl's progress meter, but you do want to hear about real errors. The combination is -s -S: silent, but still show errors. Add -f so an HTTP 4xx/5xx makes curl exit non-zero.

curl · script-friendly flags
curl -fsS -X POST "https://lert.dev/p/your-id" \
  -H "Content-Type: application/json" \
  -d '{"title":"Job finished"}'
  • -s — silent, no progress bar.
  • -S — but still print an error message if something breaks.
  • -f — fail with a non-zero exit code on HTTP errors (404, 429, 5xx).

Two more flags earn their keep in unattended scripts. Add --max-time 10 so a stalled connection can't hang your job indefinitely, and --retry 2 so a transient network blip is retried automatically before curl gives up. Together with -fsS they turn a bare request into something you can trust in a pipeline that runs at 3am with nobody watching.

Branch on success in bash

Because -f sets the exit code, you can wrap the call in a plain if. That lets you notify a fallback channel — or just log — when the notification itself fails.

bash · success check
LERT_URL="https://lert.dev/p/your-id"

if curl -fsS -X POST "$LERT_URL" \
     -H "Content-Type: application/json" \
     -d '{"title":"Deploy live ✅"}' > /dev/null; then
  echo "notified"
else
  echo "lert failed (exit $?)" >&2
fi

Reading the response

A success is 200 with {"delivered":1}. Pipe to jq if you want to act on the field. Watch for these:

  • 200 {"delivered":0,"warning":"no device registered…"} — valid endpoint but no device yet; open the Lert app once on your phone.
  • 404 {"error":"unknown endpoint"} — wrong or regenerated URL.
  • 429 — rate limited (60/min per IP, 50/day on Free). Honor the Retry-After header.
curl · read delivered with jq
delivered=$(curl -sS -X POST "$LERT_URL" \
  -H "Content-Type: application/json" \
  -d '{"title":"ping"}' | jq '.delivered')

echo "delivered = $delivered"

Putting this in a job? See the cron guide for a reusable wrapper, or server monitoring and long-running scripts for real setups. Prefer a language? Python, Node.js, and Go all take the same one-liner.

Why it's this simple

There's no auth flow because the endpoint URL is the credential. curl already speaks HTTP, so "notify my phone from the shell" is a single command you can paste into any script, any pipeline, any box with network access.

Once you've got the flags down — -fsS for scripts, text/plain with --data-binary for messy text, and a plain if to branch on the exit code — the same three or four lines cover everything from a one-off build to a fleet of servers. That's the payoff of a primitive small enough to memorize.

FAQ

Why does my message get mangled when it contains = or &?

curl -d sends application/x-www-form-urlencoded by default, so = and & are treated as form separators. Set Content-Type: text/plain and use --data-binary to send the text exactly as written.

What's the difference between -d and --data-binary?

-d (--data) strips newlines and can trigger form encoding. --data-binary sends the payload byte for byte — what you want for plain-text messages and for files read with @.

How do I make curl fail on an HTTP error?

Add -f (--fail) so curl returns a non-zero exit code on 4xx/5xx, and -sS to stay quiet but still print real errors. Then branch on the exit code.

How do I read the delivered field from the response?

Pipe curl's output to jq. A success returns {"delivered":1}; a 200 with delivered:0 means no device is registered yet.

One command. Your phone buzzes.

Download Lert, copy your URL, paste a curl. Every script you own can now reach your pocket.

Get the app iOS