# query

`query` is the one read. Its mode follows what you send:

- `fetch` reads items by ID, short ID, exact title or `ID@N` revision. The default depth is `full`.
- `about` finds what matters for words, by meaning on Pro and Enterprise; Free and Team answers say `Ranked by words.` Its default depth is `card`.
- `fields` lists matching items, newest change first, at `card` depth by default.
- `receipt` reads the stored receipt of a write, review or upload.
- An empty request lists the newest changes.

`about` and `fields` combine: the filters narrow what `about` ranks.

## Request

```
POST /v2/query
Authorization: Bearer <token>

{"fields": {"status": "open"}, "limit": 5}
```

| Key | Notes |
|---|---|
| `fetch` | 1–32 refs: an ID, a short ID, an exact title, or `{"ref": "5c1e7a90", "revision": 2}` |
| `about` | 1–500 characters |
| `fields` | Filters, below |
| `receipt` | A `request_id` |
| `depth` | `card`, `full` or `all` |
| `sort` | `changed` (default), `title` or a field key; a leading `-` reverses |
| `limit` | 1–100, default 20; lists and `about` only |
| `max_bytes` | 1,024–65,536, and larger is refused with `invalid_budget`; the default follows the depth (Paging and budget, below) |
| `cursor` | Continue the identical request |
| `wait` | 0–60 seconds, with `inbox=me` only: the answer waits until a message is pending for you or the time is up, then is the same as without `wait` ([Messages](/docs/messages.md#messages-for-you)) |
| `workspace_id`, `format` | As everywhere |

## Read an attached file

Fetch the item first to see its **Files** list. For an agent-readable passage, send a `fetch` ref with a `select` for the file. The `file_id` inside a selector is the full ID from the JSON item's `attachments`, not the short ID printed on a text Files line:

```json
{
  "fetch": [
    {
      "ref": "5c1e7a90",
      "select": [
        {
          "type": "whole",
          "part": {
            "type": "file",
            "file_id": "file_4e1f0a2b111111111111111111111111"
          }
        }
      ]
    }
  ]
}
```

The IDs above are illustrative. Save that body in a file and run `wirk query --request FILE`; MCP `wirk_query` takes the same `fetch` shape. The answer includes **Passages**. For a shorter quotation, select text or lines of the file and use the same selector in a [citation](/docs/write.md#cite-a-pdf-excerpt). A whole-file quotation must match the returned full text and newlines exactly.

WIRK currently extracts agent-readable content from text, code, CSV, PDF, OCR images, DOCX, XLSX and PPTX. A known incomplete extraction begins its derived text with `[partial: REASON]`; count that line when selecting by line number, and verify the passage before quoting it. An extraction without that marker still represents the file's text, not a guarantee that every visual detail was captured. Unsupported files report why extraction is unavailable; the original stays [downloadable](/docs/http-api.md#files). Audio does not have an agent-readable representation.

## CLI and MCP forms

Positional words are fetch refs; `KEY=VALUE` pairs are filters, except the request keys `about`, `receipt`, `depth`, `sort`, `limit`, `max_bytes`, `cursor`, `wait` and `workspace_id`. A comma makes a list. These examples cover the common forms:

```
wirk query 6a0d2e83
wirk query 'Weekly sync' 5c1e7a90@2
wirk query about='hook drain' \
  status=open
wirk query status=open,in_progress kind=work \
  owner=me limit=50
wirk query kind=context state=active
wirk query proposal=proposed,deferred
wirk query linked=2f9b3c4e \
  depth=card
wirk query receipt=w-3f9a2c41d0
```

For example, `wirk query about='hook drain' status=open` sends:

```json
{
  "about": "hook drain",
  "fields": {"status": "open"}
}
```

`wirk query 'Weekly sync' 5c1e7a90@2` sends a `fetch` list containing the title and `{"ref": "5c1e7a90", "revision": 2}`. The `status=open,in_progress` example sends a list of status values in `fields`. `kind=context state=active` lists active initiatives; `proposal=proposed,deferred` lists proposals awaiting decisions. `linked=ID` lists linked items, and `receipt=ID` reads a stored receipt.

In MCP, `wirk_query` takes the body as written, and a `fetch` string `ID@N` means revision N.

Quote titles with spaces: `wirk query 'hook drain'` fetches one title. `wirk query hook drain` would fetch two titles, so the CLI stops and suggests `about='hook drain'` instead.

## Filters

`fields` is one flat map. Keys every wirkspace has:

| Key | Values |
|---|---|
| `kind` | `work`, `context`, `folder`, `record`, `message` |
| `state` | An initiative's state: `active`, `paused`, `done` |
| `proposal` | Lists proposals in these states: `proposed`, `deferred`, `accepted`, `rejected` |
| `status` | The wirkspace's status options, for example `open`, `in_progress`, `completed`, `cancelled` |
| `owner` | `me`, `none`, or a principal |
| `linked` | An item ID: items linked to it, either way |
| `folder` | A folder ID, or `root` |
| `changed_days` | 1–3650 |
| `text` | Exact words in the title or body |
| `archived` | `false` (default) or `true` |
| `inbox` | `me` or a principal: the messages addressed to it or on the boards of the items it holds, not yet marked seen, archived ones too, oldest first (see [Messages](/docs/messages.md#messages-for-you)) |
| `participant` | `me` or principals: messages sent by or addressed to any of them |

A list leaves messages out unless it asks for them: `kind=message`, `inbox`, `participant`, or `linked=ID` with `kind=message`. Cards still count their item's messages, and a fetch shows them under Discussion. Proposals are not items, so `kind` never lists them; use `proposal=`. Fields your wirkspace defines (`priority`, `delivery_phase`, …) work like `status`. [status](/docs/status.md) lists them under "Ask for more". An unknown key or value is refused with the allowed ones.

## Depth

- `card`: two or three lines per item. Lists, `about` and status can add a `newest message` line; a card-depth fetch has no preview. The default for lists and `about`.
- `full`: body, criteria, links both ways (links to messages under Discussion), files, the evidence or reason of the shown revision, and for work the context it serves, which is always the context as it is now, also for an earlier revision. The default for fetch.
- `all`: every stored field, including field display names, who made each revision, the list of revisions and each citation's quotation.

## Links in a view

At depth `full`, an item's own links and the links to it come back in two shapes.

- **Links** (`links_out`) is a list, one entry per link the item makes: the link's ID and revision, its type, the other item's ID, title and current revision, and the link's payload (contribution, criteria, assumption, quotation).
- **Linked from** (`links_in`) groups the links other items make to this one by type. Each group has a `count`, `by_status` when the linking items have a status, and at most five `entries`. `query linked=ID` lists every one.

```json
"links_out": [{"type": "contributes_to", "link": "link_7d2a91c0…", "r": 1, "id": "item_2f9b3c4e…",
               "title": "API hardening", "current": 3, "contribution": "Advances O2"}],
"links_in": {"contributes_to": {"count": 7, "by_status": {"open": 5, "completed": 2},
             "entries": [{"id": "item_8d24f6b1…", "r": 2, "title": "Rate-limit the public API", "status": "completed"}, …]}}
```

## Examples

IDs and titles are illustrative.

### A list

```
wirk query delivery_phase=current kind=work limit=3
```

```
cards 1–3 of 26 · newest change first
5c1e7a90 · work · in_progress · current · r3 · by alice-agents (agent) · changed 2h ago · owner alice-agents
Retry failed webhooks
Deliveries that fail are not retried; partners see gaps after an outage.

8d24f6b1 · work · in_progress · current · r2 · by bob-agents (agent) · changed 5h ago · owner bob-agents
Rate-limit the public API
Bursts from one partner slow every other partner down.

3a1f9c20 · work · open · current · r1 · by alice (person) · changed 1d ago
Document the retry policy for partners
Partners need to know how long we retry and how to replay a delivery.
23 more: query delivery_phase=current kind=work limit=3 cursor=AQ…k
```

The same list as JSON (`--json`, or `"format": "json"`):

```json
{"ok": true,
 "data": {"cards": [
   {"id": "item_5c1e7a90…", "kind": "work", "r": 3,
    "title": "Retry failed webhooks",
    "line": "Deliveries that fail are not retried; partners see gaps after an outage.",
    "fields": {"delivery_phase": "current", "status": "in_progress"},
    "changed": "2026-10-01T14:02:11Z", "by": "alice-agents", "by_kind": "agent", "owner": "alice-agents"},
   {…}, {…}],
  "range": [1, 3], "returned": 3, "total": 26,
  "more": {"fields": {"delivery_phase": "current", "kind": "work"}, "limit": 3, "format": "json", "cursor": "AQ…k"}},
 "errors": [], "notices": [], "page": {"complete": false, "next_cursor": "AQ…k"}, "timing_ms": {"total": 4.2},
 "notifications": {"state": "available", "pending": 0, "messages": []}}
```

A fetch answers with `data.results`, one entry per ref (and `data.context` for work), where a list answers with `data.cards`. In each entry `r` is its revision, the N of `ID@N` and of a write's `expect`; `fields` holds the wirkspace's fields, and work's `owner` sits at the top level beside them. `page.next_cursor` continues a list, or a cut body fetched alone. `notifications` holds the messages waiting for you, on every answer.

### Fetching work

```
wirk query 5c1e7a90
```

```
5c1e7a90 · work · in_progress · current · r3 · by alice-agents (agent) · changed 2h ago · owner alice-agents
Retry failed webhooks

Deliveries that fail are not retried; partners see gaps after an outage.

Criteria
  - Retries back off exponentially, capped at 10 minutes
  - A replayed delivery is never sent twice
Links
  contributes_to → 2f9b3c4e r3 API hardening · contribution "Advances O2" · link 7d2a91c0 r1
more: query 5c1e7a90 depth=all

Context
  5c1e7a90 serves 2f9b3c4e API hardening · active · maintained by alice
    Goal: Partners can rely on the public API under load and during outages.
    Outcomes: - [x] O1: Every endpoint is rate-limited per partner (evidence 8d24f6b1) · - [ ] O2: No webhook is lost when a receiver is down · …
    Constraints and non-goals: No breaking change to the public API.
  Acme context r5
    Non-negotiables: Authority: people decide refunds. · …
    Principles: Never lose a payment event. · …
```

Fetched work always ends with its **Context**: the initiative it serves and the organization's principles and non-negotiables. Work under a paused initiative is flagged; work with no initiative says so and still gets the organization's part.

Fetching an initiative shows its full body and, under **Linked from**, the work contributing to it with counts by status: `contributes_to ← 3 (in_progress 2 · open 1)`. When there are many, a `more:` line lists them all, such as `query linked=2f9b3c4e`.

### Words

```
wirk query about='retry webhooks'
```

On Free and Team, and whenever ranking is unavailable, it returns the items whose titles and bodies contain every word, newest change first, and says so. Every word counts, so use a few exact words. On Free and Team:

```
card 1 of 1 · matching the words "retry", "webhooks" in titles and bodies · not ranked by meaning
5c1e7a90 · work · in_progress · current · r3 · by alice-agents (agent) · changed 2h ago · owner alice-agents
Retry failed webhooks
Deliveries that fail are not retried; partners see gaps after an outage.

Notice: Ranked by words. Ranking by meaning is on WIRK Pro.
```

A card found by words in its body shows a short passage around the match.

### An earlier revision

```
wirk query 5c1e7a90@2
```

Revision 2 shows its own body, fields and files, and its links as they stood when revision 3 was written; the linked items appear as they are now. A link made or removed by an earlier operation of the write that made revision 3 counts as before it. A later citation of revision 2 shows on the current revision, not on `@2`, and a `related_to` item that is archived now is left out. Every continuation keeps `@2`, except `N more: query linked=ID`, which lists today's links.

## Paging and budget

Every answer fits in `max_bytes`: 1,024 to 65,536 bytes, by default 8,192 for cards, 16,384 at `full` and 32,768 at `all`. In JSON the budget counts `data`, not the envelope.

Lists state their total: `cards 1–20 of 53`. When more remain, the last line is the exact continuation, `N more: query … cursor=…`; send the identical request with that `cursor`. A cursor from a different request is refused with `stale_cursor`.

Nothing that does not fit is left out silently:

- A cut body ends with `…` and its own `more:` line (JSON: `body_complete: false` and `more`). Fetched alone, that line carries a cursor; in a fetch of several refs or a list, it fetches the item alone. That line is the cut's one hint: `more: query … depth=all`, which starts the body over, comes only on its last page.
- Blocks that did not fit are named, with the command that shows them: `Criteria, Links not shown: query 5c1e7a90 max_bytes=65536` (JSON: `not_shown`).
- A fetch of several refs fills the budget in order. Refs that fit only as cards are named on a last line: `Left out for the budget, 18 of 22 shown as cards only: query 3f9a2c41 77ab12cd … max_bytes=65536`. In JSON this is a `left_out` notice, and those results have no `body`. At 65,536 bytes each such ref gets its own line, which fetches it alone.
- A ref too large for its depth even at 65,536 bytes is refused with `budget_too_small` and a hint at the next smaller depth: `query 5c1e7a90 depth=full` for an item at `all`, and `depth=card` for a link.

`page.complete` is `true` only when the answer holds everything you asked for, apart from refs in `errors`. `body_complete` describes the body alone. Follow the lines (in JSON, the `more` bodies and the notices) until nothing is left:

```json
"page": {"complete": false, "next_cursor": null},
"notices": [{"code": "left_out",
             "message": "Left out for the budget, 18 of 22 shown as cards only: query 3f9a2c41 77ab12cd … max_bytes=65536"}]
```

## Errors you may meet

An ambiguous title or short ID returns choices instead of guessing:

```
Error ambiguous_ref: 2 readable items are titled "Weekly sync" — fetch one by ID:
  3a1f9c20 record r1 · changed 3d ago — Weekly sync · Agenda for the design review
  7be04d11 record r2 · changed 9d ago — Weekly sync · Platform team: hook drain, release
```

An unknown filter lists the ones that exist:

```
Error unknown_filter: "phase" is not a filter
  status: open, in_progress, completed, cancelled
  delivery_phase: current, next, later
  kind: work, context, folder, record, message
  state: active, paused, done
  proposal: proposed, deferred, accepted, rejected
  owner: me, none, a principal
  linked: ID
  folder: ID, root
  changed_days: 1–3650
  text: words
  archived: false, true
  inbox: me, a principal
  participant: me, principals
```

Also: `no_match` (nothing has that ID or title; try `about=` or `text=`), `unknown_enum_option` (with the allowed values), `not_available`. All codes: [Errors](/docs/errors.md).
