How Pidge works
Channels
Section titled “Channels”One channel per agent. The channel holds the bearer key (hld_…), the identity the human
sees (name, color, avatar), and the human’s delivery policy for that agent. Everything an
agent sends and receives is scoped to its channel — two agents never see each other’s traffic.
Every send has two axes
Section titled “Every send has two axes”You pick one type (how much it may intrude — axis 1), then orthogonally decide the response (buttons? wait or not? — axis 2).
Axis 1 — the type
Section titled “Axis 1 — the type”| Type | For | Clears when |
|---|---|---|
message |
just inform, no action needed | the human opens it |
important ⭐ |
a pendency they should resolve — the recommended default | Done |
urgent |
wake them now (rare, real); can escalate to an alarm | Done (cuts the alarm) |
event |
a thing with a known time — countdown Live Activity | passed / Done |
live |
track something live (progress card, no banner) | you end it |
Each type maps to a delivery profile the human configured: message is a quiet banner that
respects silent mode and Focus; important is time-sensitive (breaks Focus, no alarm); urgent
unanswered for N minutes becomes a real AlarmKit alarm that rings through silent mode and Focus.
The fuse is shown to the human up front, and answering before it fires defuses the alarm
entirely — no ring, no vibration. Timing lives on the server, so the defuse works even with the
app closed.
What makes message self-clearing is a knob called Done when read, on by default for message
only and off for every other type. It lives in the app under Settings → Delivery profiles →
Message; turn it off and a message stays pending until the human marks it Done, like the rest.
Axis 2 — the response
Section titled “Axis 2 — the response”Composes with any type:
--actions yes,no/--custom-actionadd buttons (free-text reply is always available).--waitblocks until the human answers. Without it the send is fire-and-forget — the answer arrives later vialisten --all, polling, or your webhook.- Two shortcuts bundle both:
ask=important --wait, andapproval=important+ Approve (Face ID) / Reject +--wait.
The human owns loudness — degrade, never reject
Section titled “The human owns loudness — degrade, never reject”The human sets a per-channel urgency ceiling. An over-ceiling send is delivered at the
allowed level instead, and the agent is told why: the API answers 201 with degraded: true
and a degrade_reason (the CLI narrates it on stderr). Nothing is silently dropped, and nothing
is rejected for being “too loud” — that’s the human’s policy working. Don’t retry harder.
Answers are one-and-done
Section titled “Answers are one-and-done”Every response is terminal: it closes the notification. The two exceptions are snooze and a reschedule to a time, which re-fire later. A genuine follow-up question is a new notification, never a second answer on the same one. This is what keeps agent loops simple: one send, at most one answer.
Reschedule speaks natural language: the human types “20 min” or “tomorrow 9 AM” into the banner’s text field and the notification comes back exactly then — parsed in their timezone, re-fired by a durable server-side job.
Timing lives on the server
Section titled “Timing lives on the server”Snooze re-fires, scheduled sends (--deliver-at), event countdowns and urgent-escalation are
durable server-side jobs. They survive agent restarts, phone reboots and network drops — the
agent never has to track a timer.
Fast, and offline-proof
Section titled “Fast, and offline-proof”Most answers never open the app: long-press the banner and the real buttons (or a text field) are right there on the lock screen. Measured tap-to-agent latency is about one second. A reply typed on the lock screen even survives airplane mode — it’s queued on the phone and delivered automatically once the network is back, so the human can answer from a plane and the agent still hears it.
Bilateral state
Section titled “Bilateral state”Both sides see what happened. The agent sees delivered / seen / answered on each send; the human sees WhatsApp-style ✓✓ when the agent read a message and “listening now” while an agent is blocked waiting — people type more when the light is on.
Realtime, with an honest fallback ladder
Section titled “Realtime, with an honest fallback ladder”Answers and messages ride a WebSocket (wss://api.pidge.sh/cable) when the runtime has one
(Node ≥ 22): sub-second delivery, and a long listen survives server deploys by reconnecting.
Everything durable still goes over HTTP (backlog reads and acks), so a dropped socket costs
latency, never data:
WebSocket → HTTP long-poll (?wait=, capped ~25 s) → plain GETs every ~45 sThe canonical contract
Section titled “The canonical contract”GET https://api.pidge.sh/api/v1/manifest is the self-documenting, versioned machine spec — built
from the live models so it can’t drift. With a channel key it also returns that channel’s
config and a whats_new changelog. Point your agent at it.
