Skip to content

Sending

All examples use npx pidge-cli (npm: pidge-cli, binary pidge, Node ≥ 18, zero dependencies). The CLI reads PIDGE_URL/PIDGE_TOKEN from the environment or ~/.config/pidge/env — see Quickstart for the key-free setup.

Terminal window
# Just inform — fire-and-forget, clears when the human opens it (unless they turned "Done when read" off):
pidge message --title "Build green" --body "2m12s"
# A pendency they should resolve — the DEFAULT type ("waiting for you" card):
pidge important --title "Review PR #42" --url "https://github.com/…/pull/42"
# Urgent — breaks through silent/Focus; --escalate forces an alarm if unanswered:
pidge urgent --title "Balance dropped below $5k" --escalate
# A thing with a known time — push at T−lead + a lock-screen countdown:
pidge event --title "Team meeting" --event-at "2026-07-14T15:00:00" --lead-minutes 30
# Track something live — Live Activity progress card, no banner:
pidge live --title "Deploying api" --status "Building…"
# then: pidge live <cid> --step 3/5 · pidge live <cid> --end --outcome "Deployed ✓"

Old names still work as aliases: fyimessage, reportimportant, alerturgent.

Terminal window
pidge ask --title "Approve deploy?" --actions yes,no --timeout 600

ask = important --wait: send and block until the human answers, then print the chosen action as JSON on stdout (exit 0). A timeout exits 3 — that means no answer yet, not failure. ask requires a way to answer (--actions, --custom-action or a template).

Need a typed answer? Use --actions reply alone — a decision + reply in one send is refused (one tap on reply would dodge the decision).

ask prints correlation_id=<cid> as its first stderr line, so even a killed ask leaves a handle behind: pidge wait <cid> resumes the wait instead of re-sending.

Two things can end an ask besides the buttons you offered — handle both (details in Waiting & receiving):

  • action_id: "done" — the server appends a neutral Done to every ask; the human cleared the pendency without deciding. Terminal; not an approval.
  • A typed composer message — while ask blocks, a message the human types in the channel wakes it too (CLI ≥ 0.32) and is printed as kind: "human_message"; handle it, ack, and re-wait on the cid.
Terminal window
# Go/no-go with Face ID — important + Approve/Reject + --wait:
pidge approval --title "Deploy to production?"
# → chosen_action.action_id is "grant" (approved) or "deny" (rejected)
# Gate YOUR OWN risky action behind a human Face-ID tap — deny-default EXIT CODES:
pidge approve "Run the schema migration?" --body "Drops legacy_orders" --timeout 300
# exit 0 ONLY on explicit allow; deny/timeout/error → non-zero

approve is built for permission hooks (fail closed) — full treatment in Approval gates.

Terminal window
# From the built-in catalog:
pidge important --title "Ship it?" --actions yes,no,later
# Your own labels (repeatable):
pidge important --title "DB migration ready" \
--custom-action "run:Run now:destructive:biometric:terminal" \
--custom-action "defer:Tonight"
  • Catalog ids: yes,no,approve,reject,accept,decline,later,done,snooze,reschedule,reply,mute. snooze/done/reschedule/mute/reply are handled by Pidge; the rest come back to you as the chosen action_idyou interpret the meaning.
  • Custom-action spec: id:label[:destructive][:confirm][:biometric][:terminal].
  • Gated actions never appear as lock-screen buttons. Anything destructive/confirm/ biometric (and every custom action) shows only on the tap-through detail screen, behind a confirm or Face ID gate — a banner tap can never fire a risky action.
  • Free-text reply is always available to the human, on any notification.
Terminal window
pidge important --title "Nightly report" --body-markdown-file report.md # tables; "-" reads stdin
pidge message --title "Chart ready" --image ./chart.png # local path → uploaded for you
pidge important --title "Report" --file ./report.xlsx # real artifact → Quick Look → Files (≤25 MB)
pidge important --title "Prod token rotated" --copy "tok_9f3…" # tap-to-copy value
pidge important --title "Review PR" --url "https://github.com/…" # deep link on tap

--body is the banner text; --body-markdown is the rich detail screen the human taps through to. An --image that’s already an https URL is passed through as-is.

Terminal window
pidge important --title "Stand-up in 10" --deliver-at "2026-07-14T09:50:00" # server-side schedule
pidge message --title "Queue: 12 jobs" --collapse-key queue-status # replaces the prior one
pidge cancel <correlation_id> # withdraw a still-scheduled send (409 once it reached the phone)

--correlation-id makes sends idempotent (same id → same notification, never a duplicate) and is the handle you poll, wait on, and get echoed back on every reply. Set it so you can match answers to tasks; it’s auto-generated if omitted. While a scheduled send hasn’t fired, re-POSTing the same correlation id edits it in place (full upsert — send the whole desired shape) and pidge cancel withdraws it; the human also sees scheduled sends in the app and can delete them before they fire.

Terminal window
pidge important --title "Step 2 ready" --thread build-42 # same id ⇒ one strand on the phone
pidge ask --title "Decision 2/3" --actions yes,no --after <cid-of-decision-1>

--thread groups related sends as one conversation strand; --after <cid> holds a notification until that one is answered, so the human sees one decision at a time (a snooze doesn’t advance the queue).

  • Fire-and-forget sends print the raw 201 JSON on stdout — trust the echo: it shows exactly what was accepted, including degraded: true + degrade_reason when the human’s channel policy lowered the loudness (see How Pidge works).
  • --wait sends print the chosen_action JSON.
  • Everything human-facing (warnings, the correlation id, snooze notices) goes to stderr — stdout is always machine-readable.

The full flag list is in the command reference.