# Move or copy a card to another board

Source: https://opvs.ai/docs/guides/move-and-copy-cards

> A card can go to another board in two ways.

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

`task_id` (path)

a card ref such as `OPS-142`, a UUID prefix of 8 or more hex characters, or a full UUID

`board_id` (body)

a board key such as `OPS`, a UUID prefix, or a full UUID

`column_id` (body)

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 `column_id` is omitted

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 `related` task link joins them

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

`task_id`

path

string

yes

The card to move

`board_id`

body

string

yes

The target board

`column_id`

body

string

no

The target column; see the defaults above

**Example**

```bash
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

`detail`

When

Resolution

401

`Not authenticated`

no token, or the token is revoked or expired

send a valid `Authorization: Bearer` token

403

a scope message

the token lacks `board:write`

request a token that carries `board:write`

403

`max_tasks_per_board`

the target board is at its card limit

make room on the target board, or raise the plan limit

404

`Task not found`

the card does not exist or you cannot read it

check the ref; refs are board key plus number

404

`Board not found`

the target board does not exist or belongs to another workspace

check `board_id`; list boards with `GET /api/v1/board/boards`

404

`Column '<name>' not found on this board.`

`column_id` names no column on the target board

list the target's columns; names are matched on the target, not the source

422

`board_id must be a non-empty string`

the body has no `board_id`

send `{"board_id": "…"}`

422

`Target board is archived`

the target board is archived

unarchive it, or choose another board

422

`Target board has a different board type`

the boards are of different types, such as `tasks` and `contacts`

transfer only between boards of the same type

409

`Card is already on the target board`

`board_id` is the card's own board

to change column, use `POST /api/v1/board/tasks/{task_id}/move`

409

`Cards in progress cannot be transferred`

the card's status is `in_progress`

wait until it leaves `in_progress`, or copy it instead

409

`Cards waiting on an answer cannot be transferred`

the card is `waiting_on_answer`

answer the question on the card, then transfer

409

`A build has just claimed this card`

a builder claimed the card moments ago

wait for the build to finish or release

409

`Ship Line cards move only when the ledger moves`

the card is a Ship Line review card

these cards are moved by the ship ledger, not by hand

409

`A card with a parent cannot be transferred`

the card is a subtask

transfer its parent

409

`A card with subtasks cannot be transferred`

the card has subtasks

move or detach the subtasks, then transfer

409

`A card with stages cannot be transferred`

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

`task_id`

path

string

yes

The card to copy

`board_id`

body

string

yes

The target board; it may be the card's own board

`column_id`

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**

```bash
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

`detail`

When

Resolution

401

`Not authenticated`

no token, or the token is revoked or expired

send a valid `Authorization: Bearer` token

403

a scope message

the token lacks `board:write`

request a token that carries `board:write`

403

`max_tasks_per_board`

the target board is at its card limit

make room on the target board, or raise the plan limit

429

`board_writes_per_day`

the workspace used its daily board writes; a copy counts as one

retry after the `Retry-After` header's seconds

404

`Task not found`

the card does not exist or you cannot read it

check the ref

404

`Board not found`

the target board does not exist or belongs to another workspace

check `board_id`

404

`Column '<name>' not found on this board.`

`column_id` names no column on the target board

list the target's columns

422

`board_id must be a non-empty string`

the body has no `board_id`

send `{"board_id": "…"}`

422

`Target board is archived`

the target board is archived

unarchive it, or choose another board

422

`Target board has a different board type`

the boards are of different types

copy only between boards of the same type

422

`Column does not belong to the target board`

`column_id` is the UUID of a column on another board

pass a column of the target board

422

`Target board default column is claimable`

no `column_id`, and the target's default column hands cards to builders

pass `column_id`, for example `Backlog`

422

`Target board has no live column`

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.

```bash
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

`--board <key or uuid>`

the target board (required)

`--column <name or uuid>`

the target column

**Errors**

Output

When

Resolution

`Error: <detail>`, exit 1

the API refused the call

the `detail` is the REST route's; see its errors table above

exit 0 and a receipt

the card moved or was copied

read `ref` for the card's new number

## 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.

```json
{
  "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 `detail`

the API refused the call

see the errors tables above

`404 method 'transferTask' not in skill schema`

the workspace has a version of `@opvs-ai/agentboard` older than 2.2.8

update the installed skill; workspaces on `latest` receive it automatically within 24 hours
