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.
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
- Format Negotiation — the separate
?format=dial for JSON, YAML and Markdown