## Add a card with HTML

`POST /v1/pages/:pageId/blocks/:blockId/cards`

Adds a new card with its own markup to a block's card set.

## Parameters

| Parameter | Type | Required | Description |
|----------|-----|:-----:|----------|
| `pageId` (path) | number | yes | Page ID. List: [`GET /v1/pages`](/docs/entities/pages/list) |
| `blockId` (path) | number | yes | ID of a block on this page. List: [`GET /v1/pages/:pageId/blocks`](/docs/entities/pages/blocks/list). A block of another page returns `404` |
| `scope` (query) | string | no | Internal landing area: `KNOWLEDGE` — knowledge base, `GROUP` — workgroup knowledge base, `MAINPAGE` — main page. Omit for a regular site |

## Request fields (body)

| Field | Type | Required | Description |
|------|-----|:-----:|----------|
| `selector` | string | yes | Card set selector with the index of the reference card: with `.landing-block-card@0`, the new card goes after the first one. Set selectors are the keys of the `cards` object in the [`GET /v1/pages/:pageId/blocks/:blockId/content`](/docs/entities/pages/block-content) response. Without `@N`, the new card goes to the start of the set, at position `0` |
| `content` | string | yes | HTML of the new card. The root element carries the set class, and the elements inside it carry the same node classes as the block's cards: subsequent calls find the card by these classes. The markup is sanitized: `<script>` tags are neutralized |

## Examples

### curl — personal key

```bash
curl -X POST "https://vibecode.bitrix24.com/v1/pages/42/blocks/50/cards" \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"selector":".landing-block-card@0","content":"<div class=\"landing-block-card\"><h3 class=\"landing-block-node-title\">Example</h3></div>"}'
```

### curl — OAuth application

```bash
curl -X POST "https://vibecode.bitrix24.com/v1/pages/42/blocks/50/cards" \
  -H "X-Api-Key: YOUR_APP_KEY" \
  -H "Authorization: Bearer USER_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"selector":".landing-block-card@0","content":"<div class=\"landing-block-card\"><h3 class=\"landing-block-node-title\">Example</h3></div>"}'
```

### JavaScript — personal key

```javascript
const response = await fetch("https://vibecode.bitrix24.com/v1/pages/42/blocks/50/cards", {
  method: "POST",
  headers: {"X-Api-Key": "YOUR_API_KEY", "Content-Type": "application/json"},
  body: JSON.stringify({"selector": ".landing-block-card@0", "content": "<div class=\"landing-block-card\"><h3 class=\"landing-block-node-title\">Example</h3></div>"}),
})
const result = await response.json()
if (!response.ok) throw new Error(result.error.message)
console.log(result.data)
```

### JavaScript — OAuth application

```javascript
const response = await fetch("https://vibecode.bitrix24.com/v1/pages/42/blocks/50/cards", {
  method: "POST",
  headers: {"X-Api-Key": "YOUR_APP_KEY", "Authorization": "Bearer USER_SESSION_TOKEN", "Content-Type": "application/json"},
  body: JSON.stringify({"selector": ".landing-block-card@0", "content": "<div class=\"landing-block-card\"><h3 class=\"landing-block-node-title\">Example</h3></div>"}),
})
const result = await response.json()
if (!response.ok) throw new Error(result.error.message)
console.log(result.data)
```

## Response fields

| Field | Type | Description |
|------|-----|----------|
| `success` | boolean | Always `true` on success |
| `data.cards` | object | Block cards after the write. The key is the set selector, the value is the array of the set's cards in order |
| `data.cards.<selector>[].index` | number | Current zero-based position of the card in the set. This is the `N` in `@N` for the next call |
| `data.cards.<selector>[].content` | string | Card HTML after sanitization |

## Response example

The block had the cards `First` and `Second`. The new card went after the first one, at position `1`:

```json
{
  "success": true,
  "data": {
    "cards": {
      ".landing-block-card": [
        {
          "index": 0,
          "content": "<div class=\"landing-block-card\"><h3 class=\"landing-block-node-title\">First</h3></div>"
        },
        {
          "index": 1,
          "content": "<div class=\"landing-block-card\"><h3 class=\"landing-block-node-title\">Example</h3></div>"
        },
        {
          "index": 2,
          "content": "<div class=\"landing-block-card\"><h3 class=\"landing-block-node-title\">Second</h3></div>"
        }
      ]
    }
  }
}
```

## Error response example

400 — `content` is missing or empty:

```json
{
  "success": false,
  "error": {
    "code": "MISSING_REQUIRED_FIELDS",
    "message": "content must be non-empty HTML."
  }
}
```

## Errors

| HTTP | Code | Description |
|------|-----|----------|
| 400 | `MISSING_REQUIRED_FIELDS` | `content must be non-empty HTML.` — `content` is missing or whitespace-only |
| 400 | `MISSING_REQUIRED_FIELDS` | `selector is required.` — `selector` is missing, empty or whitespace-only |
| 404 | `ENTITY_NOT_FOUND` | `Page, block or card not found.` — the block does not belong to page `pageId`, the block has no set with this selector, or the set has no card with index `@N`. Same response if the block does not exist |
| 400 | `INVALID_PARAMS` | `selector index must be a non-negative safe integer.` — the value after `@` is not a non-negative integer, the selector contains more than one `@`, or nothing precedes `@` |
| 400 | `INVALID_PARAMS` | `pageId and blockId must be positive safe integers.` — `pageId` or `blockId` is not a positive integer |
| 400 | `INVALID_PARAMS` | `Body must be a JSON object.` — the body is not a JSON object |
| 400 | `INVALID_PARAMS` | `Body contains an unsupported field.` — the body contains a field other than `selector` and `content` |
| 400 | `INVALID_SCOPE` | `scope` is not `KNOWLEDGE`, `GROUP` or `MAINPAGE` |
| 422 | `BITRIX_NO_EFFECT` | `Bitrix24 did not confirm the card action.` — Bitrix24 did not confirm the write |
| 422 | `BITRIX_NO_EFFECT` | `Bitrix24 did not return cards after the action.` — after the write, Bitrix24 did not return the block content |
| 422 | `BITRIX_NO_EFFECT` | `Bitrix24 did not persist the requested card count.` — the block markup did not change after the write |
| 422 | `BITRIX_ERROR` | Bitrix24 rejected the action. The error text comes from Bitrix24 |
| 502 | `BITRIX_ERROR` | `Bitrix24 returned invalid or oversized card content.` — the block markup cannot be parsed or the cards exceed 8 MiB |
| 403 | `SCOPE_DENIED` | The API key does not have the `landing` scope |
| 403 | `WRITE_BLOCKED_READONLY_KEY` | The key is read-only. The request is rejected before Bitrix24 is called |
| 403 | `BITRIX_ACCESS_DENIED` | The Bitrix24 user the key is issued for has no permission to edit the page |
| 401 | `MISSING_API_KEY` | The `X-Api-Key` header is missing |
| 401 | `INVALID_API_KEY` | The key in `X-Api-Key` is not found or has been revoked |
| 401 | `TOKEN_MISSING` | The API key has no configured tokens |
| 409 | `LANDING_MODULE_NOT_ENABLED` | The Sites module is not available on the Bitrix24 account |
| 409 | `PORTAL_ADDRESS_CHANGED` | A self-hosted Bitrix24 moved to a new address, but the key's webhook was issued for the old one. Reissue the key's webhook |
| 429 | `RATE_LIMITED` | The Bitrix24 request limit is exceeded. Retry the request in 1-2 seconds |
| 502 | `BITRIX_UNAVAILABLE` | Bitrix24 did not respond or returned a server error |
| 503 | `BITRIX_TIMEOUT` | Bitrix24 did not respond in time |

Full list of common API errors — [Errors](/docs/errors).

## Known specifics

- **Indexes shift after every write.** Cards have no permanent ID: the `N` in `@N` is the position in the set at the time of the call. Before the next operation, take the indexes from `data.cards` of the latest response.
- **A failure after the write does not undo the addition.** The `422`, `502` and `503` responses do not mean the card was not added. Before retrying, re-read the block via [`GET /v1/pages/:pageId/blocks/:blockId/content`](/docs/entities/pages/block-content): a retry can add one more card.
- **The response is read from the draft.** The cards in the response come from the page draft, including unpublished edits.

## See also

- [Block cards](/docs/entities/pages/cards)
- [Clone a block card](/docs/entities/pages/cards/clone)
- [Remove a card by index](/docs/entities/pages/cards/remove)
- [Rebuild a card set](/docs/entities/pages/cards/update)
- [Block content](/docs/entities/pages/block-content)
