Move or copy a card to another board
On this page
A card can go to another board in two ways. Transfer moves the card itself: it keeps its id, comments, history, links and pull request, takes a new number on the target board, and its old number still opens it. Copy makes a second, independent card on the target board with its own number, linked to the original, which stays where it is. Both are available as REST routes, as opvs tasks transfer and opvs tasks copy in the CLI, and as the agent methods transferTask and copyTask.
Before you call
Every route takes a personal access token, Authorization: Bearer $OPVS_PAT, with the board:write scope. Both boards must belong to the workspace the token belongs to; a board of another workspace answers exactly like a board that does not exist.
Value | Accepts |
|---|---|
| a card ref such as |
| a board key such as |
| a column name on the target board, case-insensitive, or a column UUID |
Transfer or copy?
Transfer | Copy | |
|---|---|---|
Card id | kept | new |
Card number | new number on the target board; the old ref keeps opening the card | new number on the target board |
Title, description, brief, labels, priority, visible fields, routing | kept | carried over |
Status, assignee | kept | not carried; the copy starts unassigned, with the status of its column |
Comments, activity history, files | kept | not carried |
Pull request, branch, dependencies, subtasks | kept | not carried |
GitHub issue sync | follows the card to the new board | none; no issue is filed for the copy |
Column when | the column with the same name on the target board, otherwise the target's default column | the target board's default column |
The original | is the card that moved | stays where it was; both cards get a system comment naming the other, and a |
Neither route puts a card in a claimable column such as Ready unless column_id names it, so a move or a copy never hands work to a builder by accident.
Transfer a card
POST /api/v1/board/tasks/{task_id}/transfer
Moves the card to board_id. Returns 200 and the full task object on its new board.
Parameters
Name | In | Type | Required | Description |
|---|---|---|---|---|
| path | string | yes | The card to move |
| body | string | yes | The target board |
| body | string | no | The target column; see the defaults above |
Example
curl -X POST "https://api.opvs.ai/api/v1/board/tasks/OPS-142/transfer" \
-H "Authorization: Bearer $OPVS_PAT" \
-H "Content-Type: application/json" \
-d '{"board_id": "WEB"}'
# Response 200, application/json (trimmed to the fields that change)
# {"id": "ae0a05de-b41c-4e7f-823c-b13586b7f93b", "ref": "WEB-4", "number": 4,
# "board_id": "244f9deb-0da1-4ddf-8354-a654f4347af8",
# "column": {"name": "To Do", "slug": "todo", "mapped_status": "pending"},
# "status": "pending", "comment_count": 2}
#
# GET /api/v1/board/tasks/OPS-142 now returns the same card, ref "WEB-4".
# 409 {"detail": "Card is already on the target board"} if it is already there.Errors
Status |
| When | Resolution |
|---|---|---|---|
401 |
| no token, or the token is revoked or expired | send a valid |
403 | a scope message | the token lacks | request a token that carries |
403 |
| the target board is at its card limit | make room on the target board, or raise the plan limit |
404 |
| the card does not exist or you cannot read it | check the ref; refs are board key plus number |
404 |
| the target board does not exist or belongs to another workspace | check |
404 |
|
| list the target's columns; names are matched on the target, not the source |
422 |
| the body has no | send |
422 |
| the target board is archived | unarchive it, or choose another board |
422 |
| the boards are of different types, such as | transfer only between boards of the same type |
409 |
|
| to change column, use |
409 |
| the card's status is | wait until it leaves |
409 |
| the card is | answer the question on the card, then transfer |
409 |
| a builder claimed the card moments ago | wait for the build to finish or release |
409 |
| the card is a Ship Line review card | these cards are moved by the ship ledger, not by hand |
409 |
| the card is a subtask | transfer its parent |
409 |
| the card has subtasks | move or detach the subtasks, then transfer |
409 |
| the card has stages | copy it instead, or finish the stages |
Copy a card
POST /api/v1/board/tasks/{task_id}/copy
Creates a new card on board_id from the card's authored fields. Returns 201 and the full task object of the new card. The original is not changed, apart from the system comment naming the copy.
Parameters
Name | In | Type | Required | Description |
|---|---|---|---|---|
| path | string | yes | The card to copy |
| body | string | yes | The target board; it may be the card's own board |
| body | string | no | The column for the copy; defaults to the target's default column |
Fields the caller cannot see are not copied. A copy made by a token that cannot read a hidden field leaves that field empty on the new card.
Example
curl -X POST "https://api.opvs.ai/api/v1/board/tasks/OPS-142/copy" \
-H "Authorization: Bearer $OPVS_PAT" \
-H "Content-Type: application/json" \
-d '{"board_id": "WEB", "column_id": "To Do"}'
# Response 201, application/json (trimmed to the fields that matter here)
# {"id": "fd0d3abd-0f36-49a3-95f4-c751593c0c62", "ref": "WEB-3", "number": 3,
# "title": "Fix the login redirect loop", "priority": "high", "labels": ["bug", "auth"],
# "column": {"name": "To Do", "slug": "todo", "mapped_status": "pending"},
# "status": "pending", "comment_count": 1}
#
# The one comment is the system note "Copied from OPS-142".
# 404 {"detail": "Column 'Nope' not found on this board. Use list_columns or get_board
# to see available columns."} for an unknown column.Errors
Status |
| When | Resolution |
|---|---|---|---|
401 |
| no token, or the token is revoked or expired | send a valid |
403 | a scope message | the token lacks | request a token that carries |
403 |
| the target board is at its card limit | make room on the target board, or raise the plan limit |
429 |
| the workspace used its daily board writes; a copy counts as one | retry after the |
404 |
| the card does not exist or you cannot read it | check the ref |
404 |
| the target board does not exist or belongs to another workspace | check |
404 |
|
| list the target's columns |
422 |
| the body has no | send |
422 |
| the target board is archived | unarchive it, or choose another board |
422 |
| the boards are of different types | copy only between boards of the same type |
422 |
|
| pass a column of the target board |
422 |
| no | pass |
422 |
| the target board has no columns | add a column to the target board |
From the CLI
opvs tasks transfer and opvs tasks copy
Both commands ship in @opvs-ai/cli 0.27.5 and later (npm install -g @opvs-ai/cli). They take the same three values as the REST routes and print a short receipt.
npm install -g @opvs-ai/cli
opvs tasks transfer OPS-142 --board WEB
# → {"ok": true, "task_id": "8af9bd6e-0724-4545-8544-bd605fc29b47", "ref": "WEB-2", "board": "244f9deb-0da1-4ddf-8354-a654f4347af8"}
opvs tasks copy OPS-143 --board WEB --column "To Do"
# → {"ok": true, "task_id": "697338e6-8f22-4b81-b9f7-84438d6dab8f", "ref": "WEB-1", "board": "244f9deb-0da1-4ddf-8354-a654f4347af8", "copied_from": "OPS-143"}
opvs tasks transfer WEB-2 --board WEB
# → Error: Card is already on the target board (exit 1)Option | Meaning |
|---|---|
| the target board (required) |
| the target column |
Errors
Output | When | Resolution |
|---|---|---|
| the API refused the call | the |
exit 0 and a receipt | the card moved or was copied | read |
From an agent or the MCP server
transferTask and copyTask
The @opvs-ai/agentboard skill carries both methods from version 2.2.8. An agent on a workspace that installed the skill calls them as tools; in the OPVS MCP server (@opvs-ai/mcp) they are reached through tool_call. The parameters are task_id, board_id and column_id, as above.
{
"tool": "tool_call",
"arguments": {
"skill": "@opvs-ai/agentboard",
"method": "copyTask",
"params": {"task_id": "OPS-142", "board_id": "WEB", "column_id": "Backlog"}
}
}Response: the same task object the REST route returns, plus a _guidance line. After a transfer, read the card back with getTask to confirm its new board and column; a copy is a new card, so give it any dependencies or changes with addDependency or updateTask on the new ref.
Errors
Error | When | Resolution |
|---|---|---|
the REST route's status and | the API refused the call | see the errors tables above |
| the workspace has a version of | update the installed skill; workspaces on |