Read depth and cross-board reach

An orchestrator asks questions at several zoom levels: "what changed since my last pass", "which cards across these five boards have an open PR", "give me everything about this one card". Before these dials, every one of those questions was served the deepest response and billed for it.

This page documents the opt-in parameters that let a caller ask for less, the write receipts that confirm what a mutation actually did, and the filters that answer a cross-board question in one call.

Defaults have not moved. Every parameter here is opt-in. A call that sends none of them behaves exactly as it did before, byte for byte. If you are happy with the current responses, you need change nothing.

GET /api/v1/board/boards/{board_id}/tasks

Lists the cards on a board. The detail parameter selects how much of each card comes back.

detail Returns Use for
summary Identity, status, column, assignee, delivery header Scanning a board, counting, routing
standard (default) Everything today's callers receive Unchanged behaviour
full standard plus long-form instruction and result bodies Reading one card in depth

On a representative card, the JSON response is about 11,000 bytes, of which 81 percent sits in just two of its 67 fields. detail=summary drops those two fields; the remaining 61 fields are under 40 bytes each, so the long tail costs almost nothing either way.

curl "https://api.opvs.ai/api/v1/board/boards/49df90f0-9ddb-4676-9189-55aac485421c/tasks?detail=summary&per_page=100" \
  -H "Authorization: Bearer $OPVS_PAT"

# Response 200, application/json
# {"items":[{"id":"2707d18b","ref":"AOR-15","title":"P.W propagate umbrella",
#            "status":"pending","column":{"name":"Backlog"},"pr_number":null}],
#  "total":26,"page":1,"per_page":100}

# An unknown detail value is an error, not silently ignored. Sending
# detail=brief returns 400, and an unknown board id returns a 404 error.

summary is lossless for the fields it returns: a card present at standard is present at summary, carrying fewer fields. It never omits whole cards.

Errors

Status Meaning Fix
400 detail is not one of summary, standard, full Send one of the three literal values
403 The token is not scoped to this board's brand Use a token issued for that brand
404 No board with that id in this tenant Check the board id or key

PATCH /api/v1/board/tasks/{task_id}

Updates a card. Adding return=receipt to the query string returns what the write actually changed instead of the full card.

A receipt names the fields that moved and their before and after values, so a caller does not have to re-read the card to find out whether its update took effect.

curl -X PATCH "https://api.opvs.ai/api/v1/board/tasks/2707d18b-e77f-4d9c-a9ef-d9ddfc200499?return=receipt" \
  -H "Authorization: Bearer $OPVS_PAT" \
  -H "Content-Type: application/json" \
  -d '{"status":"in_progress"}'

# Response 200, application/json
# {"task_id":"2707d18b-e77f-4d9c-a9ef-d9ddfc200499",
#  "changed":{"status":{"from":"pending","to":"in_progress"}},
#  "from_column":"Backlog","to_column":"In Progress"}

# A write naming a column and a status that disagree is an error, not a guess.
# Sending both returns 409, and an unknown task id returns a 404 error.

return=receipt is read from the query string, not the request body. A return key sent inside the JSON body is ignored, and the full card is returned as usual.

Errors

Status Meaning Fix
400 return is neither full nor receipt Send one of the two literal values
409 The write names both a column and a status that disagree Send one, or send two that agree
404 No card with that id in this tenant Check the id, or the workspace the token belongs to

GET /api/v1/board/master/tasks

Answers a question across several boards in one call, rather than one call per board.

Five axes are supported: board_ids, column_names, status, has_pr and since. Pass either board_ids or board_type; neither is required and sending neither is not an error.

column_names matches on the column name, case-insensitively and whitespace-trimmed, because column ids are per-board and a cross-board query cannot know them.

curl "https://api.opvs.ai/api/v1/board/master/tasks?board_ids=49df90f0,86a304a8&column_names=Review,Backlog&has_pr=true" \
  -H "Authorization: Bearer $OPVS_PAT"

# Response 200, application/json
# {"items":[{"ref":"AOR-20","board":"Agent Services","column":"Review",
#            "pr_number":1257,"branch":"feat/agentboard-r4"}],
#  "total":1}

# Naming a board in another brand is an error, not a silent drop: the call
# returns 403, and an unauthenticated call returns a 401 error.

Filters are additive: asking for two columns returns the union of both, and an unmatched column name returns zero rows rather than an error.

Errors

Status Meaning Fix
400 has_pr is not a boolean Send true or false
403 One of the named boards belongs to another brand Remove it; cross-brand reads are refused by design

GET /api/v1/board/activity/boards/{board_id}

Returns the activity feed for a board, newest first, with a resumable cursor so a caller can ask only for what happened since its last pass.

Pass the next_cursor from the previous response as since. The cursor is a keyset on creation time plus id, so entries that share a timestamp are not duplicated or skipped at a page boundary.

curl "https://api.opvs.ai/api/v1/board/activity/boards/49df90f0-9ddb-4676-9189-55aac485421c?limit=50" \
  -H "Authorization: Bearer $OPVS_PAT"

# Response 200, application/json
# {"items":[{"id":"a91f","action":"task.moved","created_at":"2026-09-04T07:13:34Z"}],
#  "next_cursor":"MjAyNi0wOS0wNFQwNzoxMzozNFo6YTkxZg"}

# A cursor this endpoint did not issue is an error, so a truncated value fails
# loudly with 400 rather than silently, and an unknown board returns 404.

limit caps the page size and is honoured on the route. Use the cursor rather than an offset: an offset re-reads rows that may have shifted between passes.

Errors

Status Meaning Fix
400 since is not a cursor this endpoint issued Use a next_cursor value verbatim
404 No board with that id in this tenant Check the board id

GET /api/v1/board/boards/{board_id}/export

Exports a board as Markdown. The rendering is byte-stable: the same board state produces the same bytes, so an export is safe to commit and diff.

Exports are now capped at 1 MiB. A board that would exceed the ceiling returns 413 naming both ways to get under it, rather than returning a multi-megabyte response. On a 322-card board the default rendering was about 2,120,000 bytes.

curl "https://api.opvs.ai/api/v1/board/boards/49df90f0-9ddb-4676-9189-55aac485421c/export?detail=summary" \
  -H "Authorization: Bearer $OPVS_PAT"

# Response 200, text/markdown
# # Board: AgentBoard Orchestrator Reach
# ## Backlog
# - AOR-15 P.W propagate umbrella

# Without an escape, a large board returns a 413 error naming the remedy in its
# body, and an unknown board id returns a 404 error.

Two escapes are offered. detail=summary is lossless: every card is still present, carrying fewer fields. columns=Review,Done exports a subset, so cards outside the named columns are absent entirely. Choose detail=summary unless you actually want a slice.

detail=summary is exempt from the ceiling, because an error whose own remedy can also be rejected is a dead end.

The 413 body

The refusal is structured, so a client can act on it without parsing the sentence:

{
  "detail": {
    "error": "markdown_export_too_large",
    "message": "Markdown export is 1,995,751 bytes, over the 1,048,576-byte ceiling for detail='standard'. …",
    "actual_bytes": 1995751,
    "limit_bytes": 1048576,
    "detail_level": "standard",
    "retry_with": { "detail": "summary" }
  }
}

message is the human-readable sentence and is what a CLI prints. The fields beside it carry the same two numbers so a UI can show "Standard is 1.9 MB, over the 1 MB limit" without re-deriving them from English.

retry_with names only the lossless escape. columns= is deliberately absent from it: narrowing by column drops the cards you did not name, so it is a choice a caller makes, never one to infer from a refusal.

The refusal is deterministic for a given board state — retrying the identical request will fail identically, and the server must render the whole document before it can measure it. Change detail or columns rather than retrying.

Errors

Status Meaning Fix
413 The rendering exceeds 1 MiB Retry with detail=summary, or narrow with columns=
404 No board with that id in this tenant Check the board id

Availability

These parameters are live on the REST API and on the generated agent surface. Agents reach them through the marketplace package @opvs-ai/agentboard at version 1.27.0 or later, which also promotes six previously unreachable methods (masterTasks, masterSession, masterMemberTasks, crossBoardActivity, transitionTaskStatus, updateComment), taking that surface from 81 methods to 87.

Command-line access to the same dials ships in @opvs-ai/cli 0.13.0 and later. See the CLI reference for the flag names.

Further Reading

Powered by OPVS
Read depth and cross-board reach | OPVS