# Merge cards

`POST https://evohub.io/api/v1/retro/boards/{boardID}/cards/merge`

Part of the [Retro API](https://docs.evohub.io/retro.md) reference · operationId `mergeRetroCards`.

Combines several cards on an active board into one. The primary
card keeps its column and its text, followed by the other cards'
text in the order they were written, one per line. Reactions move to
the primary card (one per person and emoji); the other cards are
deleted with their comments and votes.

`card_ids` must hold at least two distinct cards, including
`primary_card_id`, all on this board. Cards from other columns are
merged into the primary card's column.

The answer is the merged card; its `vote_score` and `my_vote` are
not filled in, so read the board for them.

Requires the `retro:card:write` scope.

## Authorization

Any one of:

- `bearerKey`
- `headerKey`

Where:

- `bearerKey`: HTTP Bearer — An EvoHub API key (`evohub_…`) as a bearer token.
- `headerKey`: API key in the header `X-API-Key` — An EvoHub API key (`evohub_…`).

## Path parameters

- `boardID` (string, required, example `brd_3f9a1c7e2b8d4a60`): The board's ID.

## Request body (required)

Content type: `application/json`

Type: `MergeCardsRequest`

- `primary_card_id` (string, required): The card the others are merged into.
- `card_ids` (array of string, required, min items 2): Every card to merge, the primary card included.

## Responses

### 200 — The merged card.

Content type: `application/json`

Type: `CardEnvelope`

- `success` (boolean)
- `data` (Card)
  - `id` (string, example `crd_8d0c4b6e1a9f2357`)
  - `board_id` (string)
  - `column_id` (string)
  - `content` (string)
  - `created_at` (string (date-time))
  - `reactions` (array of Reaction): Only when a whole board is read; left out when there are none.
    - `emoji` (string)
    - `count` (integer)
    - `reacted` (boolean): Whether the caller reacted with this emoji.
  - `comments` (array of Comment): Only when a whole board is read; left out when there are none.
    - `id` (string, example `cmt_4e6a8c0b2d1f3957`)
    - `card_id` (string)
    - `board_id` (string)
    - `content` (string)
    - `created_at` (string (date-time))
  - `vote_score` (integer): The net of agree (+1) and disagree (-1) votes.
  - `my_vote` (integer, one of `-1`, `0`, `1`): The caller's own vote on the card.

### 400 — The body is not JSON (`INVALID_BODY`), `primary_card_id` is missing (`MISSING_PRIMARY_CARD_ID`), `card_ids` holds fewer than two distinct cards (`INSUFFICIENT_CARDS`), or it does not include the primary card (`PRIMARY_NOT_IN_LIST`).

Content type: `application/json`

Type: `Error`

- `success` (boolean, value `false`): Sent by Retro itself; refusals before the request reaches Retro (401, 429) carry only `error`.
- `error` (object, required)
  - `code` (string, required, example `NOT_FOUND`)
  - `message` (string, required, example `board not found`)
  - `request_id` (string)

### 401 — No API key was sent, or it is unknown, revoked or expired (`UNAUTHORIZED`).

Content type: `application/json`

Type: `Error`

- `success` (boolean, value `false`): Sent by Retro itself; refusals before the request reaches Retro (401, 429) carry only `error`.
- `error` (object, required)
  - `code` (string, required, example `NOT_FOUND`)
  - `message` (string, required, example `board not found`)
  - `request_id` (string)

### 403 — The key lacks the scope this endpoint needs (`FORBIDDEN`).

Content type: `application/json`

Type: `Error`

- `success` (boolean, value `false`): Sent by Retro itself; refusals before the request reaches Retro (401, 429) carry only `error`.
- `error` (object, required)
  - `code` (string, required, example `NOT_FOUND`)
  - `message` (string, required, example `board not found`)
  - `request_id` (string)

### 404 — The board is not one the caller can see (`NOT_FOUND`), or a card is not on it (`CARD_NOT_FOUND`).

Content type: `application/json`

Type: `Error`

- `success` (boolean, value `false`): Sent by Retro itself; refusals before the request reaches Retro (401, 429) carry only `error`.
- `error` (object, required)
  - `code` (string, required, example `NOT_FOUND`)
  - `message` (string, required, example `board not found`)
  - `request_id` (string)

### 409 — The board is closed and its content cannot change (`BOARD_CLOSED`). Reopen it first.

Content type: `application/json`

Type: `Error`

- `success` (boolean, value `false`): Sent by Retro itself; refusals before the request reaches Retro (401, 429) carry only `error`.
- `error` (object, required)
  - `code` (string, required, example `NOT_FOUND`)
  - `message` (string, required, example `board not found`)
  - `request_id` (string)

### 429 — Too many requests (`RATE_LIMITED`). Wait for `Retry-After` seconds.

Headers:

- `Retry-After` (integer): Seconds to wait before retrying.

Content type: `application/json`

Type: `Error`

- `success` (boolean, value `false`): Sent by Retro itself; refusals before the request reaches Retro (401, 429) carry only `error`.
- `error` (object, required)
  - `code` (string, required, example `NOT_FOUND`)
  - `message` (string, required, example `board not found`)
  - `request_id` (string)

### 500 — Something went wrong on EvoHub's side (`INTERNAL_ERROR`). Retry later.

Content type: `application/json`

Type: `Error`

- `success` (boolean, value `false`): Sent by Retro itself; refusals before the request reaches Retro (401, 429) carry only `error`.
- `error` (object, required)
  - `code` (string, required, example `NOT_FOUND`)
  - `message` (string, required, example `board not found`)
  - `request_id` (string)

## Example request

```bash
curl -X POST 'https://evohub.io/api/v1/retro/boards/brd_3f9a1c7e2b8d4a60/cards/merge' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer <TOKEN>' \
  -d '{
  "card_ids": [
    "crd_8d0c4b6e1a9f2357",
    "crd_2f4a6c8e0b1d3579"
  ],
  "primary_card_id": "crd_8d0c4b6e1a9f2357"
}'
```
