Open standard · questionprotocol.io
The Question Protocol.
A small, open standard for asking a human a question and getting one attributable answer back through the notification layer. No chat. No WebSocket. No dashboard.

The primitive
One question. One tap. One answer.
An agent (or any caller) asks a human a question: a prompt plus two to four predefined options, delivered as a push. The human answers with a single tap. The chosen option, who chose it, and when, route back to the asker. The first valid answer wins; every question has a deadline. It is a notification primitive, not a chat; answers are always a choice, never free text.
Deploy v1.4.0 to production?
The option shape
{
"value": "approve", // stable token
"label": "Approve", // human text
"primary": true, // affirmative
"destructive": false // cancel
}Two to four options. Values are unique, stable tokens that round-trip exactly; labels are the human text on the button. One option may be flagged affirmative, one cancel.
The state machine
Four states. Three distinct outcomes.
pending resolves once, one way, to a terminal state. The three outcomes stay distinguishable: a human who said no is different from one who never answered, which is different from a question that was withdrawn.
pending
Asked and awaiting an answer, before its deadline.
answered
An option was chosen. “Said no” is an answered question whose value is the negative option.
expired
No answer by the deadline. Reported as “no answer,” not a choice.
cancelled
The asker withdrew it while it was still pending.
Who answers, and from where
Asks you, or asks the room.
One named person. Only they can answer.
Anyone eligible in the room. The first valid answer wins; later taps are clean no-ops.
From the notification
Options render as tappable actions on the lock screen. Two options sit side by side with no expansion. A tap resolves in the background. No app to open.
From the app
The same question is a cell in the in-app feed: inline buttons for two options, a “choose an answer” sheet for three or four, and a resolved state once answered.
Both surfaces resolve to the identical action; neither is privileged. A late tap on an already-resolved question is a clean “already answered,” never an error.
The wire
A human decision, as a shell gate.
Delivery is push; waiting is long-poll; expiry is a scheduled sweep. An SDK wraps ask / await / fetch / cancel with a binary helper; a CLI turns the answer into an exit code, so a human decision drops straight into a pipeline.
# A human decision as a shell gate.
if [ "$(pingroom ask "$ROOM" \
"Deploy $SHA to production?" \
--yes-no --wait)" = "approve" ]; then
./deploy.sh
fiThe exit code encodes the outcome: answered, expired, or cancelled. See the full wire contract →
Built to be adopted
A standard, not a feature.
Two options are the fast path
Binary questions resolve from the lock screen with no expansion. Make yes/no the default; treat 3–4 options as the escape hatch.
Identity is never claimed
The responder on an answer is the authenticated actor, never caller-supplied. A tap cannot answer for someone else.
Versioned and additive-only
Field names, the four states, and the answer envelope are stable. New fields are added; existing ones are never renamed or removed.
Implement it from the doc alone
Speak the same protocol.
A standalone home lives at questionprotocol.io. The reference implementation is PingRoom.