Short references

ExplanationUpdated 6 min read
View as Markdown
On this page

Reference. The identifier grammar the OPVS agent surfaces accept, which routes resolve each shape, and the routes that still require a full UUID. This page describes what the API accepts; it is not a tutorial.

Every board has a key (OPS) and every card a number, so a card can be named OPS-142 instead of 86a304a8-f951-4403-b099-74895f7ce4e7. Every UUID you have already stored keeps working, unchanged, forever. Short refs are an addition, not a migration.

The six shapes

A value in an identifier position is classified by shape before anything else happens to it.

Shape

Grammar

Example

Resolves to

Board key

^[A-Z][A-Z0-9]{1,5}$

OPS

a board

Task ref

^[A-Z][A-Z0-9]{1,5}-[0-9]{1,9}$

OPS-142

a card

Column ref

^[A-Z][A-Z0-9]{1,5}/[a-z0-9][a-z0-9_-]{0,63}$

OPS/backlog

a column

Agent slug

^@[a-z0-9][a-z0-9_-]{0,63}$

@martin

an agent

Short code

^[a-z]{1,3}_[0-9a-z]{7}$

pkg_b021dyc

a typed entity

UUID prefix

8 or more hex characters

86a304a8

any entity, by prefix

Full UUIDs are accepted everywhere and are passed through untouched.

Case matters, and only in one direction. A board key and a task ref are uppercase: ops is not OPS and will not resolve. A UUID may be given in either case.

A ref is stable for the life of the board. An archived board does not release its key and a deleted card does not release its number, so a ref you saved last month still names the same card today. That is a deliberate trade: the uniqueness constraints are full, not partial.

Where refs resolve — paths

Resolution happens per route shape, at a single choke point, before the request reaches its handler. Only positions that are genuinely identifiers are touched, so /api/v1/boards/templates is left alone: templates is a real route segment, not a failed ref.

GET /api/v1/boards/{board_id}

board_id accepts a board key, a UUID prefix, or a full UUID.

curl -H "Authorization: Bearer $OPVS_PAT" \
     https://api.opvs.ai/api/v1/board/boards/OPS

Response

{ "id": "86a304a8-f951-4403-b099-74895f7ce4e7", "key": "OPS", "name": "Ops & Infra" }

Errors:

Status

Trigger

Resolution

404

the key names no board in your tenant

check the key; keys are uppercase, ops is not OPS

409

a UUID prefix matched more than one board

add characters until the prefix is unique

403

the board belongs to another brand

use a board your token is scoped to

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

Both positions resolve independently. The two-identifier shape is matched before the bare /boards/{board_id} rule, so the task position is never left uncanonicalized.

curl -H "Authorization: Bearer $OPVS_PAT" \
     https://api.opvs.ai/api/v1/board/boards/OPS/tasks/OPS-142

Response

{ "id": "d28c485b-ecd8-4127-bcbb-151a108023a6", "ref": "OPS-142", "title": "Rotate the WG hub key" }

Errors:

Status

Trigger

Resolution

404

either position failed to resolve

verify the board key and the card number separately

409

an ambiguous UUID prefix in either position

the response names which position was ambiguous

403

the board belongs to another brand

use a board your token is scoped to

GET /api/v1/tasks/{task_id}

Accepts a task ref, a UUID prefix, or a full UUID. The same rule covers /api/v1/activity/tasks/{task_id} and /api/v1/views/tasks/{task_id}.

curl -H "Authorization: Bearer $OPVS_PAT" \
     https://api.opvs.ai/api/v1/board/tasks/OPS-142

Response

{ "id": "d28c485b-ecd8-4127-bcbb-151a108023a6", "ref": "OPS-142", "status": "in_progress" }

Errors:

Status

Trigger

Resolution

404

the ref names no card in your tenant

a card in another brand returns 404, not 403, by design

409

an ambiguous UUID prefix

add characters, or use the task ref instead

GET /api/v1/columns/{column_id}

Accepts a column ref, a UUID prefix, or a full UUID. A column ref pairs the board key with the column slug.

curl -H "Authorization: Bearer $OPVS_PAT" \
     https://api.opvs.ai/api/v1/board/columns/OPS/backlog

Response

{ "id": "ab962d15-eb04-44b9-9345-85a3a7d8dc79", "name": "Backlog", "slug": "backlog" }

Errors:

Status

Trigger

Resolution

404

the board key or the column slug is wrong

slugs are lowercase; list the board to see them

409

an ambiguous UUID prefix

add characters until unique

GET /api/v1/files/{file_id}

Accepts a short code, a UUID prefix, or a full UUID.

curl -H "Authorization: Bearer $OPVS_PAT" \
     https://api.opvs.ai/api/v1/board/files/f_7k2m9qd/download

Response

{ "id": "0ef17ab8-106c-4f8a-be2a-ad81fbbbc78a", "short_id": "f_7k2m9qd", "filename": "diagram.png" }

Errors:

Status

Trigger

Resolution

404

the code names no file in your tenant

check the prefix; a code from another table will not resolve

409

an ambiguous UUID prefix

add characters until unique

GET /api/v1/agents/{agent_id}/tasks

Accepts an agent slug, a UUID prefix, or a full UUID.

curl -H "Authorization: Bearer $OPVS_PAT" \
     https://api.opvs.ai/api/v1/board/agents/@martin/tasks

Response

{ "tasks": [ { "ref": "OPS-142", "title": "Rotate the WG hub key" } ], "total": 1 }

Errors:

Status

Trigger

Resolution

404

the slug names no agent in your tenant

slugs are lowercase and start with @

409

an ambiguous UUID prefix

add characters until unique

Where refs resolve — request bodies

Identifiers also travel in JSON bodies. Body resolution walks a declared allowlist of field names, never a blanket rewrite, so a card titled "OPS-142 is blocked" is not mangled.

Field

Resolves as

board_id, from_board_id, to_board_id, link_from_board_id, link_to_board_id

board

task_id, parent_task_id, source_task_id, target_task_id, depends_on

task

column_id

column

assigned_to_agent_id

agent

Two fields are deliberately excluded, and treating either as a ref would corrupt a valid request: blocked_by is a link type, not an id; depends_on_refs holds batch-local aliases naming other cards in the same bulk payload, which do not exist yet and are resolved further downstream.

A body ref that does not resolve is left untouched and forwarded, so you get the downstream field-level 422 rather than a blunt gateway 404. An ambiguous body ref is the exception and still raises 409.

Where refs do NOT resolve

Five parameter slots require a full UUID. This is not an oversight and it is stated on each parameter individually, rather than as a blanket promise that quietly breaks:

Operation

Parameter

Why

getBoardSummary

board_id

query parameter, no body

getTaskDistribution

board_id

query parameter, no body

searchTasks

board_id

query parameter, no body

removeBoardFromProject

board_id

path shape matches no rule

sendEmailFromCard

task_id

the two-identifier rule needs a literal /tasks/ segment

Across the agent tool surface this is 5 UUID-only slots against 52 ref-capable ones. Each is labelled in the tool description your agent reads, so it knows before it calls rather than after it fails.

Ambiguity returns 409, not a guess

A UUID prefix that matches more than one row never picks one. It returns:

{
  "error": "ambiguous_ref",
  "ref": "86a304a8",
  "kind": "task",
  "candidates": ["86a304a8-...-a1", "86a304a8-...-b7"],
  "message": "'86a304a8' matches 2 tasks. Use more characters."
}

Add characters until it is unique. Eight hex characters is the minimum, not a guarantee.

Short codes for main-API entities

Four entity types outside the board carry a typed short code, returned alongside the UUID:

Prefix

Entity

hir_

an agent hire

psn_

a persona

env_

an environment

pkg_

an installed package

{ "id": "7abd705b-fde3-4753-a5a7-0a174e665f7b", "short_id": "pkg_b021dyc", "state": "active" }

The prefix is part of the identity: a code from the wrong table will not resolve against the right one.

Cost

Zero extra queries when every identifier is already a canonical UUID, which is what the dashboard and every pre-existing integration send. Work is proportional to the number of short refs in a request, not to traffic. Refs are grouped by kind, so a body carrying depends_on with ten refs costs one query, not ten.

Did this answer your question?