# approval

One card per row waiting for a decision, with Approve and Reject, such as an order an agent may not place on its own.

`source` names a source of the screen whose rows wait for a decision (at most 20 cards); `title` and `text` are the
columns each card shows. `approve` and `reject` each set one `column` to a `value` on that row (`label` defaults
to Approve and Reject). Filter the source with `where` so only the waiting rows show; once decided, a row leaves
it. Each decision is a write with the form token, and the card says what it will set before it does.

Category: action. Tier 1. Contract version 1.

## When to use

- An agent asked a person to decide, such as an order over $500.
- A change waits for a second person, such as raising an agent's budget.
- A short queue of requests one person works through, one card each.

## When to use another block

- Many rows changed in bulk. Use [`grid`](/catalog/grid.md).
- A message with nothing to decide. Use [`callout`](/catalog/callout.md).
- A new request typed in by a person. Use [`form`](/catalog/form.md).

## Props

- `id` (name: a-z, 0-9 and -): The block's id: its Markdown anchor and its actions' route (`/act/<id>/<action>`). Default `<type>-<n>`, n counting blocks of this type depth-first from 1.
- `source` (a source of the screen, required): The source whose rows wait for a decision (at most 20 shown).
- `title` (column, required): The column holding each card's title.
- `text` (column): The column holding each card's text.
- `approve` (mapping, required): The approve button: a set of one column.
  - `label` (text ≤ 40, default `Approve`): The button's words.
  - `column` (column, required): The column it sets.
  - `value` (text, number, true or false, required): The value it sets.
- `reject` (mapping, required): The reject button: a set of one column.
  - `label` (text ≤ 40, default `Reject`): The button's words.
  - `column` (column, required): The column it sets.
  - `value` (text, number, true or false, required): The value it sets.

## Data

Reads the first 20 rows of `source`.

Shape: one row per decision with an `id`, a title column, an optional text column, and the set column.

## Actions

- `set`: sets the column to the value on those rows. Needs a selection of rows, or the row; reversible; confirm page when declared; cost write.
  - `column` (column, required): The column it sets.
  - `label` (text ≤ 40, required): The button's words.
  - `value` (text, number, true or false): The value it sets; or `options`.
  - `options` (list of 1–30: text, number, true or false): The values the person picks from; or `value`.
  - `approval` (true or false, default `false`): Through the confirm page first.
  - `id` (name: a-z, 0-9 and -): The action's id; default `set-<column>`, plus `-<value>` when a value is given.

## States

- **ready**: the source's rows, drawn
- **empty**: the source has no rows: an Empty part saying why and what comes next
- **error**: the source failed: "We couldn't load …" naming the block's rows, the source's sentence and Try again; the page still answers 200

## Examples

### An order waiting for a manager

```yaml
approval:
  source: pending
  title: subject
  text: requester
  approve: { label: Approve the order, column: status, value: Approved }
  reject: { label: Send back, column: status, value: Needs a person }
```

As an agent reads it:

```markdown
<a id="approval-1"></a>

- **New starter laptop for R. Patel** — Procurement agent · sel=48231 · "Approve the order" #approval-1.approve · "Send back" #approval-1.reject

- #approval-1.approve "Approve the order": POST /a/catalog/{screen}/act/approval-1/approve · sel=the card's row id; sets status = Approved · approval: no · cost: write
- #approval-1.reject "Send back": POST /a/catalog/{screen}/act/approval-1/reject · sel=the card's row id; sets status = Needs a person · approval: no · cost: write

```

### Turning on a new agent

```yaml
approval:
  source: drafts
  title: name
  text: summary
  approve: { label: Turn on, column: state, value: 'On' }
  reject: { label: Keep as a draft, column: state, value: Draft }
```

As an agent reads it:

```markdown
<a id="approval-1"></a>

- **Facilities agent** — Books meeting rooms and logs broken devices · sel=7 · "Turn on" #approval-1.approve · "Keep as a draft" #approval-1.reject

- #approval-1.approve "Turn on": POST /a/catalog/{screen}/act/approval-1/approve · sel=the card's row id; sets state = On · approval: no · cost: write
- #approval-1.reject "Keep as a draft": POST /a/catalog/{screen}/act/approval-1/reject · sel=the card's row id; sets state = Draft · approval: no · cost: write

```

### A budget raise

```yaml
approval:
  source: raises
  title: agent
  text: reason
  approve: { column: state, value: approved }
  reject: { column: state, value: declined }
```

As an agent reads it:

```markdown
<a id="approval-1"></a>

- **Procurement** — Budget 80% used on the 9th; asks for $180 a month · sel=3 · "Approve" #approval-1.approve · "Reject" #approval-1.reject

- #approval-1.approve "Approve": POST /a/catalog/{screen}/act/approval-1/approve · sel=the card's row id; sets state = approved · approval: no · cost: write
- #approval-1.reject "Reject": POST /a/catalog/{screen}/act/approval-1/reject · sel=the card's row id; sets state = declined · approval: no · cost: write

```

## Invalid

```yaml
approval:
  source: pending
  title: subject
  approve: { column: status }
  reject: { column: status, value: Needs a person }
```

Answers: `approval.approve.value: missing; this key is required`

## Composition

- Sits in: screen, card
- Pairs with: markdown, timeline, kpis
- A few cards at a time; past five, a grid with a set action is quicker.
- Apple HIG: Make the consequence of each choice clear, and keep the destructive choice distinct.

## Telemetry

approval.view, approval.approve, approval.reject

JSON Schema: [/catalog/approval.schema.json](/catalog/approval.schema.json)
