Skip to content

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.

A push notification on a phone lock screen asking a question with Approve and Deny answer buttons.

A question, answered from the lock screen: one tap, no app to open.

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.

Ops · now

Deploy v1.4.0 to production?

Approve
Deny

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.

direct · “asks you”

One named person. Only they can answer.

room · “asks the room”

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.

ci-gate.sh
# A human decision as a shell gate.
if [ "$(pingroom ask "$ROOM" \
        "Deploy $SHA to production?" \
        --yes-no --wait)" = "approve" ]; then
  ./deploy.sh
fi

The 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.