# Workflow.Inbox&lt;T&gt; (what is waiting for me)

> The current principal's queue: every slot they can act on, across every run of every workflow that tracks T. Rows carry the tracked entity typed, so a screen renders it without a read per row.

<!-- id: workflow-inbox · area: workflow · stability: stable · html: https://osysharp.com/reference/workflow/inbox/ -->

## Summary        {#summary}
Every other way of reading a workflow starts from one row: *what is the state of this invoice?* An inbox asks the
opposite question — *what is waiting for me?* — and it crosses every run.

**`Workflow.Inbox<T>()`** answers it for whoever is asking right now.

## Signature      {#signature}
```osy syntax
Workflow.Inbox<TrackedEntity>()
```
No arguments: it is the CURRENT principal's queue. Filter, order and count it like any other list.

## Description    {#description}
### What is in it   {#contents}
A slot is in your queue when it is **assigned to you**, or it is **open and you satisfy its `Candidates`**. Slots
that are waiting on something else are included too, carrying their status — so a screen can grey out *"CFO, waiting
for Manager"* rather than hiding work that is coming.

`T` is the entity a workflow **`Tracks`**. Asking for an entity no workflow drives is a compile error, because a
queue is a thing a workflow produces.

### The row   {#row}
| member | what it is |
|---|---|
| `Item` | the tracked entity, **typed** — an `Invoice`, not an id |
| `SlotAlias` | which slot is asking |
| `WorkflowName` | the workflow it belongs to |
| `Status` | the slot's live status |
| `OpenedAt` / `BreachesAt` | when it arrived, and when it runs out of time |
| `RunId` / `SlotId` | the run and the slot themselves |

**The alias is not decoration.** *"Waiting on you as their manager"* and *"waiting on you as finance"* are different
asks, and the same person can hold both on different items. A queue that could not tell them apart would be showing
one list where there are two.

**`BreachesAt` is the sort key.** For a workflow that rejects on breach, *"3 hours left"* is the most actionable
thing on the row. It is null when a slot has no deadline — absence, not a far-future date to filter around.

**`OpenedAt` is when the slot opened, not when the run reached the state.** For a slot held closed by
[`After`](https://osysharp.com/reference/workflow/slot-dependencies/) those are different moments — it waits `Pending` while its predecessors run —
and the row reports the later one. So *"waiting since"* means waiting **on you**, and it agrees with `BreachesAt`,
whose clock starts at the same moment.

### Reading through `Item`   {#item}
`Item` is the tracked entity, so a screen reads it directly — that is the point of the queue being typed rather than
a list of ids. Navigating it needs it [included](https://osysharp.com/reference/query/include/), as any reference does:

```osy title="the morning screen" test app=workflow-inbox
enum ClaimStage { Filed, Approved, Rejected }

[Principal] entity Employee {
  [Required] [MaxLength(80)] string DisplayName;
  security {
    allow read   when IsAuthenticated;   // a principal row is not public — say who may read it
    allow create when IsAuthenticated;
  }
}

entity Invoice {
  [Required] [MaxLength(120)] string Title;
  [Required] decimal Amount;
  [Required] Employee Owner;
  ClaimStage Stage;
  security {
    allow read, update when IsAuthenticated;
    allow create       when IsAuthenticated;
  }
}

workflow ExpenseApproval {
  Tracks    = Invoice.Stage;
  Autostart = true;
  Initial   = Filed;

  event Decide(bool approved);

  state Filed {
    subscribe Decide(bool approved) as Manager { Assignee = this.Item.Owner; }
    on Manager(bool approved) {
      when (approved) { goto Approved; }
      default { goto Rejected; }
    }
  }

  terminal success Approved { }
  terminal error   Rejected { Message = "rejected"; }
}

int WaitingOnMe() {
  return Workflow.Inbox<Invoice>()
                 .Include(r => r.Item.Owner)
                 .OrderBy(r => r.BreachesAt)
                 .Count();
}
```

### Whoever is asking   {#principal}
The queue is defined against the **current principal**, and it follows `runas`. That is deliberate: a workflow can
put an agent in a slot as readily as a person, so *"what is waiting on this principal"* stays one question rather
than growing a second surface for the non-human case.

An anonymous caller has an empty queue — nothing is waiting on nobody. That is an answer, not an error, so a public
page still renders.

### It is live   {#live}
The queue is re-read, not remembered. Act on a slot and it leaves your queue; it does not linger as a row that does
nothing when clicked. One person acting does not change anyone else's queue.

### Answering a row   {#answering}
[`Workflow.Deposit(row, Decide(true))`](https://osysharp.com/reference/workflow/inbox-act/) answers the slot a row is, and `Workflow.Claim(row)` /
`Workflow.Release(row)` take and hand back unassigned work. The event is named at the call site because a queue's rows
are heterogeneous — which slot a row turned out to be is known only once the queue has been read.

## Examples       {#examples}
Only the work of one kind, most urgent first:

```osy title="one queue, one question" syntax
Workflow.Inbox<Invoice>()
  .Include(r => r.Item)
  .Where(r => r.SlotAlias == "Finance")
  .OrderBy(r => r.BreachesAt)
```

## Notes          {#notes}
**Authorization is the workflow's, not the queue's.** Membership is decided by the same `Candidates` evaluation that
governs claiming and depositing — the queue does not get its own rules. A queue is exactly the place a second,
looser answer would otherwise appear, and there is deliberately nowhere for one to live.

**It runs on the server.** Deciding what a principal may act on is not a question a client can be trusted to answer
about itself.

## See also       {#see-also}
- [Workflow.WorkByItem&lt;T&gt; (one row per item — the board read)](https://osysharp.com/reference/workflow/work-by-item/) — one row per ITEM rather than per slot: the BOARD read, with the clock that governs across an item's slots
- [Tracks and Initial (the field a workflow drives)](https://osysharp.com/reference/workflow/tracks/) — the `Tracks` declaration that makes a queue exist at all
- [Candidates (slot)](https://osysharp.com/reference/workflow/candidates/) — who is eligible for a slot
- [Include (pre-loading relations)](https://osysharp.com/reference/query/include/) — loading `Item` before you read through it
