Inbox
Messages routines post for people, with read, done and gated Approve and Reject. What each control writes.
What it is
The Inbox (#/inbox) holds every message a run wrote to inbox/: match, alert, proposal and approval. Filters: All, Unread, Done.
Why it exists
A run should only interrupt people for what matters. The Inbox is that channel, capped by max_inbox_per_run, with the decision controls next to the message.
How it works
| Control | Writes |
|---|---|
| Read / unread | data/users/<viewer>/state/read/<id> (delete to unread) |
| Done | data/users/<viewer>/state/done/<id> |
| Approve, Reject (gated message) | decisions/<gateId> {decision: "approve" or "reject", at, by} |
| Accept (L1 proposal) | The setting in settings/<producer>, then decisions/<messageId> {decision: "accept", key, at, by} |
| Dismiss (proposal) | decisions/<messageId> {decision: "dismiss", key, at, by}; learning counts it as a rejection |
A gated message shows “Gate by
Read and done are per viewer. Without a viewer id they are hidden with “Read and done are not available for this visit.”
Since 0.5.0: mark all read, and snooze for 1, 3 or 7 days or until a date, stored at
data/users/<viewer>/state/snooze/<id>{until, at, by}. Suggestions become a tab of the Inbox, with inline suggestions shown under each list setting on Settings. Gated approvals move to their own Approvals page.
Worked example
Hiring Signals posts “Northwind Goods is hiring a Senior Shopify Engineer” (match) and an intro note for approval (approval, gate by email, 48 hours). The person reads the match, marks it done, and approves the intro note. decisions/<gateId> gets {"decision": "approve", "at": "...", "by": "<viewer>"}.
Common pitfalls
- Treating Approve as a send. It records a decision. Acting on it is a later run’s job, which is not piloted.
- Expecting read state to be shared. It is per viewer; decisions are shared.