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

# POST /v1/agents

> Create an agent: its goal, the sections of its prompt, its channels and its tools.

Creates an agent in the token's project and returns it with its `_id`.

**The goal is the decision that matters.** It is what switches the toolkits on, so an agent created to inform cannot later be made to book without changing it. Everything else — sections, channels, tools — can be adjusted afterwards.

You can send the prompt sections here, or create the agent bare and [write them one at a time](/dev/endpoints/agents-prompt-set). Valid values for `section`, `goal.type` and `channels` come from [the catalogue](/dev/endpoints/prompt-types-list).

## Endpoint

```
POST https://api.keebai.com/v1/agents
```

## Required scope

`agents:write`

## Body

| Field                | Type       | Required | Description                                                                     |
| -------------------- | ---------- | -------- | ------------------------------------------------------------------------------- |
| `name`               | `string`   | Yes      | Up to 150 characters.                                                           |
| `description`        | `string`   | No       | Up to 500 characters. Internal, the customer never sees it.                     |
| `model`              | `string`   | No       | Model id, for example `gpt-5`. An unsupported one fails with `INVALID_MODEL`.   |
| `language`           | `string`   | No       | Locale. **The country matters**: `es-CL` and `es-MX` do not conjugate the same. |
| `timezone`           | `string`   | No       | IANA name, for example `America/Santiago`.                                      |
| `goal`               | `object`   | No       | `{ type, instructions? }`. `instructions` only applies to the `custom` goal.    |
| `prompts`            | `array`    | No       | `{ section, label?, markdown }`, up to 20.                                      |
| `channels`           | `string[]` | No       | Channel types from the catalogue, up to 10.                                     |
| `reengagement`       | `object`   | No       | How often it comes back to a conversation that went cold.                       |
| `tools`              | `string[]` | No       | `ObjectId`s from [`GET /v1/tools`](/dev/endpoints/tools-list), up to 100.       |
| `knowledge_node_ids` | `string[]` | No       | Up to 200.                                                                      |

## Example request

<CodeGroup>
  ```bash curl theme={"system"}
  curl -X POST https://api.keebai.com/v1/agents \
    -H "Authorization: Bearer kbai_pk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
    -H "Content-Type: application/json" \
    -d '{
      "name": "Camila",
      "model": "gpt-5",
      "language": "es-CL",
      "timezone": "America/Santiago",
      "goal": { "type": "appointment_management" },
      "channels": ["whatsapp"],
      "prompts": [
        {
          "section": "business_context",
          "markdown": "# Quiénes somos\n\nBarbería en Providencia. Atendemos con hora, de martes a sábado."
        }
      ]
    }'
  ```

  ```js JavaScript theme={"system"}
  const resp = await fetch("https://api.keebai.com/v1/agents", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.KEEBAI_API_TOKEN}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      name: "Camila",
      model: "gpt-5",
      language: "es-CL",
      goal: { type: "appointment_management" },
      channels: ["whatsapp"],
    }),
  });
  const { agent } = await resp.json();
  ```

  ```python Python theme={"system"}
  import os, requests

  resp = requests.post(
      "https://api.keebai.com/v1/agents",
      headers={"Authorization": f"Bearer {os.environ['KEEBAI_API_TOKEN']}"},
      json={
          "name": "Camila",
          "model": "gpt-5",
          "language": "es-CL",
          "goal": {"type": "appointment_management"},
          "channels": ["whatsapp"],
      },
      timeout=10,
  )
  resp.raise_for_status()
  agent = resp.json()["agent"]
  ```
</CodeGroup>

## Response

### 201 Created

```json theme={"system"}
{
  "agent": {
    "_id": "6650a1b2c3d4e5f6a7b8c9d0",
    "name": "Camila",
    "description": null,
    "model": "gpt-5",
    "language": "es-CL",
    "timezone": "America/Santiago",
    "goal": { "type": "appointment_management", "instructions": null },
    "prompts": [
      { "section": "business_context", "label": null, "markdown": "# Quiénes somos\n\nBarbería en Providencia..." }
    ],
    "channels": ["whatsapp"],
    "reengagement": null,
    "tools": [],
    "knowledge_node_ids": [],
    "voice_response_enabled": false,
    "is_active": true,
    "created_at": "2026-08-18T09:04:21.100Z",
    "updated_at": "2026-08-18T09:04:21.100Z"
  }
}
```

### 400 Bad Request

A field over its length limit, a malformed `ObjectId`, an unknown property, or `INVALID_MODEL`.

### 401 Unauthorized

Missing, invalid, revoked, or expired token.

### 403 Forbidden

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

### 502 Bad Gateway

The agent service is unreachable.

## Operational notes

* **An agent created here is not yet answering.** The channel has to be connected on the company as well as enabled on the agent, and a goal that uses tools needs the data behind them — services, staff, catalogue — to exist.
* **Every section you send is versioned from the start.** The creation is version 1 of each.
* **`language` carries the country and it is not cosmetic.** It is what decides how the agent conjugates; getting it wrong is the first thing a customer notices.
* **Leaving a section out is better than inventing one.** An empty section is filled later; an invented one is discovered by a customer.


## OpenAPI

````yaml POST /v1/agents
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/agents:
    post:
      tags:
        - agents
      summary: Crear un agente
      description: >-
        El agente nace con un bloque de personalidad y uno de politica de
        respuesta ya adjuntos y vacios, porque sin ellos no responde nada util.
        Un modelo no soportado devuelve 400 INVALID_MODEL.
      operationId: PublicAgentsController_create
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PublicAgentCreateDto'
      responses:
        '201':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicAgentEnvelopeDto'
      security:
        - PAT: []
components:
  schemas:
    PublicAgentCreateDto:
      type: object
      properties:
        name:
          type: string
          maxLength: 150
          example: Agente de ventas
        description:
          type: string
          maxLength: 500
        model:
          type: string
          description: >-
            Identificador del modelo. Se valida contra los modelos soportados;
            uno desconocido devuelve 400 INVALID_MODEL.
          example: gpt-5
        language:
          type: string
          description: >-
            Locale del agente. El pais importa: `es-CL` y `es-MX` no conjugan
            igual.
          example: es-CL
        timezone:
          type: string
          example: America/Santiago
        goal:
          $ref: '#/components/schemas/PublicAgentGoalDto'
        prompts:
          maxItems: 20
          description: >-
            Las secciones del prompt. Se pueden mandar aca al crear, o una por
            una con `PUT /v1/agents/{id}/prompts/{section}`.
          type: array
          items:
            $ref: '#/components/schemas/PublicPromptDto'
        channels:
          maxItems: 10
          description: >-
            Canales por los que atiende, del catalogo (`GET
            /v1/prompt-types?kind=channel`).
          example:
            - whatsapp
            - webchat
          type: array
          items:
            type: string
        reengagement:
          type: object
          description: >-
            Reenganche: cuantas veces vuelve sobre una conversacion enfriada y
            cada cuanto.
          example:
            enabled: true
            max_attempts: 2
            delay_minutes: 1440
        tools:
          maxItems: 100
          description: ObjectIds de herramientas (`GET /v1/tools`) que puede usar.
          type: array
          items:
            type: string
        knowledge_node_ids:
          maxItems: 200
          description: ObjectIds de nodos de la base de conocimiento a asignar.
          type: array
          items:
            type: string
        stages:
          maxItems: 20
          description: >-
            Etapas del embudo, por NOMBRE y en orden. Se crea un pipeline en el
            CRM con el nombre del agente y esas etapas, y el agente queda
            habilitado para mover tickets solo entre ellas. Sin esto no se crea
            ningun pipeline y el agente no mueve tickets.
          example:
            - Nuevo
            - Contactado
            - Agendado
            - Cerrado
          type: array
          items:
            type: string
      required:
        - name
    PublicAgentEnvelopeDto:
      type: object
      properties:
        agent:
          $ref: '#/components/schemas/PublicAgentDto'
      required:
        - agent
    PublicAgentGoalDto:
      type: object
      properties:
        type:
          type: string
          description: >-
            Tipo de objetivo, del catalogo (`GET /v1/prompt-types?kind=goal`).
            Decide que herramientas se encienden.
          example: appointment_management
        instructions:
          type: string
          maxLength: 600
          description: >-
            Solo para el objetivo `custom`: que tiene que lograr el agente,
            escrito por el negocio.
      required:
        - type
    PublicPromptDto:
      type: object
      properties:
        section:
          type: string
          description: >-
            Tipo de seccion, del catalogo. Una seccion desconocida devuelve 404
            PROMPT_SECTION_NOT_FOUND.
          example: business_context
        label:
          type: string
          maxLength: 150
          description: Titulo propio de la seccion. Sin esto se usa el del catalogo.
        markdown:
          type: string
          description: El texto de la seccion, en markdown.
          example: |-
            # Quienes somos

            Barberia en Providencia, atendemos con hora.
      required:
        - section
        - markdown
    PublicAgentDto:
      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`.

````