Inbox and approvals
Last updated October 5, 2026
On this page
- What lands in your inbox
- Unread and Everything
- Marking items read
- Waiting on you and Waiting on others
- The approval card
- Who may decide
- What happens when you approve
- What happens when you reject
- Expiry
- Questions a run waits on
- Try it in the demo: refuse, then approve the refund
- Ask for the refund
- See a wrong person refused
- Refuse it
- Approve it
- Confirm the run resumed
- The email approval: one click, and who else may decide
- Not possible yet
- For developers
- API
- Events
- CLI
Your inbox is where OrchKernel tells you about work that involves you: an agent that needs your approval, a question a run is waiting on, a mention in a thread, a task handed to you. An approval is the moment a person decides whether an agent may go ahead with something the company's policy holds for a person, such as a refund over the cap or an email to a customer. Until someone decides, the agent's run (one attempt by an agent at a task, with its steps; see Asking an agent) is parked: it stops and waits.
What is not possible yet is listed at the end.
What lands in your inbox#
| Item | Shown as | When you get it | Clicking it opens |
|---|---|---|---|
| Approval | Churn-risk watcher needs approval: Issue a refund or credit | An agent's action waits for you. | The thread it was asked in |
| Question | Support Agent asks: Which invoice should I look at? | A run is parked until you answer. | Nothing; a click marks it read (see Questions) |
| Mention | mentioned in thread: @tom can you check the Northwind ticket?, from the person who mentioned you | Someone @ mentioned you in a thread. |
The thread |
| Update | Completed '...', Declined '...', Ada Park added you to '...' as observer | A run you started finished or was refused, a thread owner added or removed you, a new artifact version in your thread, and other notes from OrchKernel. | The thread, when the note names one. Completed notes name none and open nothing. |
| Task | Task: ... | A task was assigned to you or to your team. | Work, with the task open |
| Escalation | Escalated: ... (SLA breached), with a red escalation badge | A task you are responsible for passed its deadline. | Work, with the task open |
| Review | Schema proposal on ..., Steward proposes on ..., Change proposed: ... | A schema change, or a skill, agent or policy change, waits for your review. | Governance › Schema (also for Change proposed; see Not possible yet) |
| Delegation | Pat Kim delegated to you: ... | Someone delegated authority to you. See Delegations. | Governance › Delegations |
Items from OrchKernel itself (updates, questions) show the OK badge and "OrchKernel" as the sender; the others show who sent them.
The Inbox link in the sidebar shows how many unread items you have, approvals waiting on you included.
Unread and Everything#
The Inbox page has two tabs.
- Unread (the default) lists items you have not read yet. Approvals are not repeated in this list: the ones you decide appear as full cards under Waiting on you at the top. When there is nothing, it says "Nothing here."
- Everything lists every item you ever received, read or not, newest first. Approvals appear here as one line with where they stand, for example Issue a refund or credit: approved with an Approved badge, or Issue a refund or credit: rejected with Rejected.
Marking items read#
An unread item has a dot at its right. Press the dot to mark it read. An item that opens nothing (such as "removed you from a thread", a Completed note or a question) is marked read when you click it.
Some items are marked read for you:
- an approval, once you decide it;
- a question, once it is answered.
Opening an item that leads somewhere (a mention, for example) does not mark it read.
Pressing the dot has no visible effect: the row and the sidebar count stay the same until you reload the page. The item is marked read; after a reload it is gone from Unread and the count is one lower. This is a known bug.
Waiting on you and Waiting on others#
Above the items list, the Inbox shows pending approvals as cards:
| Section | What it holds |
|---|---|
| Waiting on you | Pending approvals you may decide, each with Approve and Reject. |
| Waiting on others | Folded, with a count; click it to open. Pending approvals you can see but someone else decides ("Approvals you can see but someone else decides."). |
An admin sees every pending approval in the company, so Ada's Waiting on others holds, for example, the follow-up email that only Alice may approve. Anyone else sees only the approvals addressed to them. An approval in your own conversation that someone else decides (such as Maya's refund, which waits for Ada) shows on the conversation, not in your inbox.
Governance › Approvals lists the same pending approvals as cards, without the other inbox items. When there are none it says "No pending approvals."
The approval card#
The same card appears in the inbox, on Governance › Approvals and in the conversation where the agent was asked. For the demo refund it reads:
Issue a refund or credit · Pending Churn-risk watcher asks to issue a refund or credit on behalf of Maya Patel. Amount $250.00 · Invoice inv-2026-09-1042 · Reason Double charge on the September invoice (ticket T-1042) Goodwill credits by agents over the cap of 100.00 in the account currency per call need an admin, whatever the trust tier · See the run · 4:58 PM
| Part | What it tells you |
|---|---|
| Title and badge | The action by its tool's name, and its state: Pending, Approved, Rejected (and Expired or Withdrawn for an outside agent's action). |
| "asks to ... on behalf of ..." | Which agent asks, and the person it acts for. The "on behalf of" part is left out when the person it acts for is also the one asked: Alice's email card reads "Sales Assistant asks to send email." |
| Payload | Exactly what will run if you approve: the amount and invoice of a refund; the recipient, subject and full text of an email. Amounts are shown in the company's currency. Nothing has run yet. |
| Footer | The rule's own words for why approval is needed, See the run (opens the run's steps in a panel), and when it was asked. |
| "Waiting for ..." | Shown to anyone who cannot decide it: "Waiting for an admin (Ada Park)." or "Waiting for Alice Chen." |
| Why am I asked? | For whoever can decide (others see Why does this need approval?). Opens the checks as they were when the agent asked: the delegation it holds, no pause or kill switch in the way, the agent is active, the skill declares the tool, the delegation's limit, the agent, task and thread budgets, the rule that asked, and who would be asked. It ends with who the policy asks, such as "The policy asks anyone with the admin role." See How the gate decides. |
| Policy now | If the policy changed since the agent asked, a red line says so: "Policy changed since this was asked: it now asks ...", or that it would now be allowed without approval. |
| Reject and Approve | Only for people who may decide it. |
After a decision the card says who decided and when: "Approved by Ada Park · 5:00 PM", or "Rejected by Ada Park · 4:59 PM: Billing is already refunding this by hand".
Who may decide#
| Approval asks for | Who may decide it |
|---|---|
| A role (the demo refund asks for the admin role) | Anyone who holds that role, and no one else. |
| The person the agent acts for (the demo's follow-up email asks Alice) | That person, or the agent's manager: the agent's reports_to, else its team's lead (Sam for the Sales Assistant). |
| A named person, the thread or task owner, or the agent's manager | That person, or the agent's manager. |
| An outside agent's action, addressed to the person it acts for | Only that person approves; the agent's manager may only reject. |
Only a person can decide. Being an admin is not enough on its own: Ada sees
Alice's email card but cannot approve it (the API answers 403
ada may not decide approval ...). Who is asked comes from the company's
policy, not from the thread; see Policy, rules and kill switches.
The agent's manager has no place in the workspace to decide: the approval is not in their inbox, and they cannot open the person's conversation. Sam can decide Alice's email only through the API (below).
Who can see an approval: admins, the agent that asked, the approver, the person the agent acts for, anyone who may decide it, and anyone who can see its run, task or thread. To anyone else it does not exist (the API answers 404).
What happens when you approve#
- A confirmation, for money. For a refund or other financial action, Approve first shows "Issue a refund or credit of $250.00? It runs as soon as you approve." with Cancel and Approve $250.00. Other actions, such as an email, are approved with one click.
- The re-check. Before anything runs, OrchKernel checks again:
- the approval is still pending and you may decide it;
- neither the agent nor the tool is paused (a paused module counts);
- what the policy says now. If a rule now refuses the action, approving is refused and the approval stays pending so it can be rejected. If the policy now asks someone else, only that person may approve.
- The action runs. That one action is carried out, exactly as the payload shows. The toast says Approved.
- The run resumes from the next step. The agent does not start over: it carries on with the rest of its plan from where it parked. The ticket and the account it read before the approval are not read again.
- The result lands where it was asked. In the demo, Maya's
conversation shows "Refunded after an admin approved it." with the
refund id
re_3Rk0a1Lq, and Maya gets a Completed update in her inbox.
While a module is paused (see Modules), its approvals stay pending. The card shows a Module paused badge, the line "CRM is paused. This waits until an admin resumes the module; then it can be approved. You can still reject it.", and, next to a greyed-out Approve, "Approve is off while CRM is paused."
What happens when you reject#
Reject opens a reason box. For a financial action the box is labeled "Reason (required)" and Confirm reject stays greyed out until you write one; otherwise the reason is optional.
- The action never runs. The toast says who hears of it: "Issue a refund or credit: rejected · Maya Patel was told".
- The run stops. In the conversation its result reads "Rejected by Ada Park ... What the run did before the rejection is under Details." with a Declined badge, and its task shows Cancelled.
- The person the agent acted for gets an inbox update: "Declined 'Northwind wants a refund for the double charge': Ada Park rejected "Issue a refund or credit": Billing is already refunding this by hand".
A decision is final. Deciding an approval that is already decided changes nothing.
Expiry#
Approvals asked by OrchKernel's own agents do not expire: the run waits until someone decides. An outside agent's approval expires after that agent's approval time (24 hours unless an admin set another); its card shows "Expires in ..." and, afterwards, "nobody decided it in time, so the action did not run." See Outside agents.
Questions a run waits on#
When an agent needs something from a person before it can go on (a choice, a missing detail), it asks a question and the run parks. The question goes to the person the run acts for, and lands in their inbox ("Support Agent asks: Which invoice should I look at?") and in the run's thread, with any suggested answers ("Options: September, August"). A run may ask at most three questions.
The demo's stub planner never asks a question; you need a real model (see Models) to see one.
Today the web workspace cannot answer a question: the inbox row opens
nothing (and a click marks it read although it is not answered), and the
thread shows the question with no reply box. Answer with ok answer or the
API (see For developers); the run then resumes with your
answer, and the question is marked read. This is a known bug.
Try it in the demo: refuse, then approve the refund#
This uses the demo company from the Quickstart. With no model key, the demo's stub planner plans the refund; a real model plans its own steps, but the approval works the same way.
Start with a fresh demo (ok demo --force, then ok demo serve), so ticket
T-1042 has not been refunded yet. The workspace is at
http://127.0.0.1:8080 unless you started the server with another
--bind.
Ask for the refund#
- Sign in as maya (support lead). Click Threads, then Churn-risk
watcher under Talk to an agent. Type
Northwind wants a refund for the double chargeand press Ask agent. - The conversation shows the approval card Issue a refund or credit,
Pending, for $250.00 against
inv-2026-09-1042. Maya is the person the agent acts for, but there is no Approve button: the card says "Waiting for an admin (Ada Park)." In the side column, Tasks shows the task as Parked. - Maya's own Inbox says "Nothing here.", and Governance › Approvals says "No pending approvals.": the refund is not hers to decide.
See a wrong person refused#
The screen hides the buttons from anyone who may not decide, and the server
refuses them too. You need the approval's id: it is the id in Ada's list
of pending approvals.
curl -H "Authorization: Bearer <ada's token>" \
http://127.0.0.1:8080/api/approvals
Then try to approve with Maya's token:
curl -X POST -H "Authorization: Bearer <maya's token>" \
-H 'content-type: application/json' -d '{"approved": true}' \
http://127.0.0.1:8080/api/approvals/<approval id>
It answers 403 {"error":"not_approver","message":"maya may not decide approval <id>"}. Tom, Alice or Fiona, who cannot see the approval at all,
get 404 not_found. Rejecting ({"approved": false}) is refused the same
way.
Refuse it#
- Sign out and sign in as ada (admin). The sidebar's Inbox shows 1.
- Click Inbox. Under Waiting on you is the refund card. Open Why am I asked? to see the checks, ending with "The policy asks anyone with the admin role."
- Press Reject. Type a reason in Reason (required), such as
Billing is already refunding this by hand, and press Confirm reject. The toast says "Issue a refund or credit: rejected · Maya Patel was told". - Waiting on you is gone. Sign in as maya: the conversation shows the card Rejected with "Rejected by Ada Park", the watcher's result with Declined, and the task Cancelled. Maya's sidebar Inbox shows 1: the Declined update. No refund was made.
Approve it#
- As maya, in the same conversation, ask again:
Northwind wants a refund for the double charge. A new card appears, Pending, waiting for Ada. - As ada, open Inbox, press Approve on the refund card, then Approve $250.00. The toast says Approved and Waiting on you is gone. Under Everything, the item now reads Issue a refund or credit: approved with an Approved badge.
- As maya, open the conversation. The card says "Approved by Ada
Park", and below it the watcher's result: "Refunded after an admin
approved it.", amount $250.00, invoice
inv-2026-09-1042, refundre_3Rk0a1Lq, Done. Maya's inbox has a Completed update.
Confirm the run resumed#
- On the approved card, press See the run. A Run panel opens beside the conversation. Its Trace lists, in order: Read data (Read Tickets: 1 row), Tool (Billing account Ok), Approval (Asked for approval), Decision (Approval Approved), Tool (Issue a refund or credit Ok), and the model and round steps around them. The ticket and the billing account were read once, before the approval; nothing before the approval ran twice.
Asking for the same refund a third time is refused before anyone is asked. In the run's Trace, a Refused step reads "Refused: policy denied Issue a refund or credit: Invoice inv-2026-09-1042 was already refunded (re_3Rk0a1Lq on ...)" ("already has a refund waiting for approval" while one is pending). With the stub planner the conversation still says "Refunded after an admin approved it." and Done, with no refund id, which is wrong: no refund was made. This is a known bug in the demo's stub planner.
The email approval: one click, and who else may decide#
- Sign in as alice (sales rep). Click Threads, then Sales
Assistant. Ask
Email a follow up to my contacted lead. - The card Send email, Pending, shows the recipient
dana@litware.example, the subject "Following up on our demo" and the full text, with Approve and Reject. It is also in Alice's inbox under Waiting on you. - As ada, open Inbox. Waiting on others 1 is folded; click it. The card says "Waiting for Alice Chen." and has no buttons.
- As alice, press Approve. There is no confirmation. The toast
says Approved; the conversation shows "Approved by Alice Chen" and
the result with Sent
msg-...and Done.
Sam, who leads Sales, may also decide Alice's emails, but only through the API. Ask once more as Alice, then, with Sam's token and the new approval's id:
curl -X POST -H "Authorization: Bearer <sam's token>" \
-H 'content-type: application/json' \
-d '{"approved": false, "note": "Not this week"}' \
http://127.0.0.1:8080/api/approvals/<approval id>
The approval comes back with "state": "rejected" and "decided_by": "sam", and Alice gets "Declined 'Email a follow up to my contacted lead':
Sam Ortiz rejected "Send email": Not this week".
Not possible yet#
- Answering a run's question from the web workspace (use
ok answeror the API). - Seeing the effect of the Mark read dot without reloading the page.
- An agent's manager, who may decide an approval addressed to someone else, finding it in their inbox or deciding it in the workspace: only the API works.
- Delegating or reassigning a single approval to another person.
- Changing a decision once made.
- Opening a Change proposed review item on the Changes tab: it opens Governance › Schema; go to Changes from there.
- A time limit on approvals asked by OrchKernel's own agents.
For developers#
API#
All under /api, with Authorization: Bearer <token>.
| Method and path | Body | Notes |
|---|---|---|
GET /inbox |
Your unread items, newest first. ?all=true for every item. Paged. Each item: id (approval:<id>, question:<id>, mention:<id>, task:<id>, note:<id> ...), at, title, kind, read, data, plus the kind's fields (approval, thread and from, task, question and run, proposal). |
|
POST /inbox/:id/read |
Marks one of your items read. {"ok": true} when it was found, {"ok": false} otherwise. |
|
GET /approvals |
Pending approvals: every one for an admin, else those addressed to you. Paged. | |
GET /approvals/:id |
One approval: requester, acting_for, approver, approver_kind (role, acting_for, owner, actor, manager), action, reason, payload, run, task, thread, state, decided_by, decided_at, note, result (what the approved action returned), trail, matched_rule, approver_rule, policy_hash. 404 if you may not see it. |
|
GET /approvals/:id/explain |
{ asked, now, changed }: why it was asked and what the policy says now. See Policy. |
|
POST /approvals/:id |
{ approved, note? } |
Decide. Returns the approval. Deciding one already decided returns it unchanged. |
POST /runs/:id/answer |
{ answer } |
Answer the question a run waits on; the run resumes. |
Errors from POST /approvals/:id:
| Status and code | When |
|---|---|
403 not_approver |
You may not decide it ("maya may not decide approval ..."). |
403 denied |
The agent or tool is paused ("policy denied approve(...): crm is paused"), the policy now names another approver you do not satisfy, or an outside agent's action that only the person it acts for may approve. |
404 not_found |
You may not see it. |
409 now_denied |
The policy now refuses the action. The approval stays pending. |
Events#
| Event | When |
|---|---|
approval_requested |
An action waits for approval: approval, approver, action. |
run_parked, run_resumed |
The run stopped for the approval or question, and carried on. |
approval_resolved |
Someone decided: approval, by, approved. |
question_asked, question_answered |
A run asked a person, and got the answer. |
CLI#
The CLI opens the state file directly, so stop the server first (on the demo,
--state orchkernel-demo/state.db). Without a model key, each command first
prints a "warning: no model provider is configured" line.
ok --state <state.db> --as ada inbox # unread items: * title item-id
ok --state <state.db> --as ada inbox --all # every item
ok --state <state.db> --as ada approve <approval-id>
ok --state <state.db> --as alice approve <approval-id> --reject --note "Not this week"
ok --state <state.db> --as maya answer <run-id> "September"
ok inbox prints item ids such as approval:4474d1d0-...; pass ok approve
only the part after approval:, or it answers "Error: approval
approval:... not found". ok approve prints the decided approval as JSON. A
wrong person gets Error: maya may not decide approval <id> and exit
status 1. Approving from the CLI runs the action from the CLI process, so on
the demo, where the tools are local fakes started by ok demo serve,
approve from the workspace or the API instead and use the CLI to reject.