> ## Documentation Index
> Fetch the complete documentation index at: https://docs.keebai.com/llms.txt
> Use this file to discover all available pages before exploring further.

# PUT /v1/assistants/{id}/blocks

> Replace the whole block list of an assistant. The only way to reorder blocks or pin versions.

Sets the assistant's entire block list in one call. This is the only endpoint that can **reorder** blocks or change which **version** each one is pinned to.

Because it replaces rather than merges, read the current list first and send the whole thing back with your change applied. Any block you leave out is detached.

<Warning>
  Send a partial list and you detach everything missing from it. Start from [`GET /v1/assistants/{id}/blocks`](/dev/endpoints/assistants-blocks-list), not from memory.
</Warning>

## Endpoint

```
PUT https://api.keebai.com/v1/assistants/{id}/blocks
```

## Required scope

`assistants:write`

## Headers

| Header          | Required | Value                    |
| --------------- | -------- | ------------------------ |
| `Authorization` | Yes      | `Bearer kbai_pk_<token>` |
| `Content-Type`  | Yes      | `application/json`       |

## Path parameters

| Parameter | Type     | Description                  |
| --------- | -------- | ---------------------------- |
| `id`      | `string` | `ObjectId` of the assistant. |

## Body

| Field                        | Type       | Required | Description                                                                       |
| ---------------------------- | ---------- | -------- | --------------------------------------------------------------------------------- |
| `blocks`                     | `object[]` | Yes      | The complete list, up to 50 entries.                                              |
| `blocks[].block_id`          | `string`   | Yes      | `ObjectId` of a block in the same company.                                        |
| `blocks[].pinned_version_id` | `string`   | No       | Freeze this assistant to one version. Omit to follow the block's current version. |
| `blocks[].order`             | `integer`  | No       | Position in the prompt, `0` or greater. Defaults to `0`.                          |

## Example request

<CodeGroup>
  ```bash curl theme={null}
  curl -X PUT https://api.keebai.com/v1/assistants/6650a1b2c3d4e5f6a7b8c9d0/blocks \
    -H "Authorization: Bearer kbai_pk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
    -H "Content-Type: application/json" \
    -d '{
      "blocks": [
        { "block_id": "6650bbbb2222cccc3333dddd", "order": 0 },
        { "block_id": "6650bbbb2222cccc3333eeee", "order": 1 },
        { "block_id": "6650bbbb2222cccc3333ffff", "order": 2,
          "pinned_version_id": "6650ffff4444aaaa5555bbbb" }
      ]
    }'
  ```

  ```js JavaScript theme={null}
  const base = "https://api.keebai.com/v1/assistants/6650a1b2c3d4e5f6a7b8c9d0";
  const auth = { Authorization: `Bearer ${process.env.KEEBAI_API_TOKEN}` };

  // Read the current wiring, then move one block to the front.
  const { data } = await (await fetch(`${base}/blocks`, { headers: auth })).json();
  const reordered = [...data]
    .sort((a, b) => (a.type === "do_not" ? -1 : b.type === "do_not" ? 1 : 0))
    .map((block, order) => ({
      block_id: block._id,
      pinned_version_id: block.pinned_version_id ?? undefined,
      order,
    }));

  await fetch(`${base}/blocks`, {
    method: "PUT",
    headers: { ...auth, "Content-Type": "application/json" },
    body: JSON.stringify({ blocks: reordered }),
  });
  ```

  ```python Python theme={null}
  import os, requests

  base = "https://api.keebai.com/v1/assistants/6650a1b2c3d4e5f6a7b8c9d0"
  auth = {"Authorization": f"Bearer {os.environ['KEEBAI_API_TOKEN']}"}

  current = requests.get(f"{base}/blocks", headers=auth, timeout=10).json()["data"]
  blocks = [
      {
          "block_id": b["_id"],
          "order": i,
          **({"pinned_version_id": b["pinned_version_id"]} if b["pinned_version_id"] else {}),
      }
      for i, b in enumerate(current)
  ]

  resp = requests.put(f"{base}/blocks", headers=auth, json={"blocks": blocks}, timeout=10)
  resp.raise_for_status()
  ```
</CodeGroup>

## Response

### 200 OK

The full assistant, same shape as [`GET /v1/assistants/{id}`](/dev/endpoints/assistants-get), with the new `blocks` array.

### 400 Bad Request

More than 50 entries, a malformed `ObjectId`, or an unknown property. `error.code` is `ASSISTANT_DUPLICATE_IDENTITY_BLOCK` when the list would give the assistant two identity blocks of the same type.

### 401 Unauthorized

Missing, invalid, revoked, or expired token.

### 403 Forbidden

The token does not have the `assistants:write` scope.

### 404 Not Found

The assistant does not exist in the token's company and project, or `error.code` is `ASSISTANT_BLOCK_NOT_FOUND` because one of the `block_id` values does not exist or belongs to another company.

### 502 Bad Gateway

The assistant service is unreachable.

## Operational notes

* **At most one `personification`, one `objective` and one `response_format`.** Those three are the identity family and a duplicate is a `400`. Instruction blocks — `steps`, `cases_possible`, `do_not`, `response_policy` — have no such limit.
* **A block from another company is a `404`, not a `403`.** Cross-company block ids are checked before anything is written, so a rejected call changes nothing.
* **`order` decides the prompt, and gaps are fine.** Blocks are composed in ascending `order` within their family; the families themselves always render identity, then instructions, then information. Reordering across families does nothing.
* **Pinning is how you freeze an assistant against edits.** With `pinned_version_id` set, later [content saves](/dev/endpoints/blocks-content-save) on that block do not reach this assistant. Useful when one block is shared by a production and a staging assistant.
* **Detaching does not archive.** Blocks dropped from the list stay in [the catalogue](/dev/endpoints/blocks-list) and keep their history.


## OpenAPI

````yaml PUT /v1/assistants/{id}/blocks
openapi: 3.0.0
info:
  title: Keebai Public API
  description: Superficie pública REST autenticada con Personal Access Tokens (PAT).
  version: 1.0.0
  contact: {}
servers:
  - url: https://api.keebai.com
    description: Production
security: []
tags: []
paths:
  /v1/assistants/{id}/blocks:
    put:
      tags:
        - assistants
      summary: Reemplazar los bloques de un asistente
      description: >-
        Fija la lista completa de bloques de una sola vez. Es la unica forma de
        reordenar o de fijar versiones. Los bloques que no aparezcan en el body
        quedan desadjuntados.
      operationId: PublicAssistantsController_setBlocks
      parameters:
        - name: id
          required: true
          in: path
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PublicAssistantBlocksSetDto'
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicAssistantEnvelopeDto'
      security:
        - PAT: []
components:
  schemas:
    PublicAssistantBlocksSetDto:
      type: object
      properties:
        blocks:
          maxItems: 50
          description: >-
            Lista completa de bloques del asistente. Los que no aparezcan quedan
            desadjuntados.
          type: array
          items:
            $ref: '#/components/schemas/PublicBlockRefDto'
      required:
        - blocks
    PublicAssistantEnvelopeDto:
      type: object
      properties:
        assistant:
          $ref: '#/components/schemas/PublicAssistantDto'
      required:
        - assistant
    PublicBlockRefDto:
      type: object
      properties:
        block_id:
          type: string
          description: ObjectId del bloque.
        pinned_version_id:
          type: string
          description: >-
            ObjectId de una version concreta. Sin este campo el asistente sigue
            la version vigente del bloque.
        order:
          type: number
          default: 0
          minimum: 0
          description: Posicion del bloque en el prompt.
      required:
        - block_id
    PublicAssistantDto:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        description:
          type: string
        avatar_url:
          type: string
        model:
          type: string
        voice_response_enabled:
          type: boolean
          description: Indica si la respuesta de voz está habilitada.
        created_at:
          type: string
      required:
        - id
        - name
        - model
        - voice_response_enabled
  securitySchemes:
    PAT:
      scheme: bearer
      bearerFormat: kbai_pk_<hex>
      type: http
      description: >-
        Personal Access Token con prefijo `kbai_pk_`. Generar desde el portal
        con permiso `developer.manage_tokens`.

````