# Crear un agente sin entrar a la pantalla

Guía para aprovisionar agentes desde un sistema propio, con una credencial de organización. Sirve
para un flujo de n8n, para un guion de onboarding, o para cualquier cosa que sepa hacer un `POST`.

---

## Qué resuelve esto

Crear un agente se hacía a mano: entrar a la pantalla, elegir una plantilla, ponerle nombre,
guardar, ir a la pestaña de conocimiento, asignarle las colecciones, y recién ahí activarlo. Para un
cliente son diez minutos. Para el que da de alta veinte clientes en una semana, son diez minutos
veinte veces, y el paso que se olvida es siempre el mismo: el conocimiento, que no se ve hasta que
el agente contesta mal.

Con esto son tres llamadas, el conocimiento viaja con el agente, y la última dice qué falta.

---

## Lo que necesitás

Una credencial de organización con el alcance **`agents:write`**. Se crea en Configuración →
Integraciones, y el secreto se muestra una sola vez.

`agents:write` puede crear agentes, sacarles una plantilla, activarlos y pausarlos. **No puede
concederles acciones nuevas**: eso es `actions:write` y se otorga aparte, porque una acción es un
camino de salida de datos hacia donde el que la crea diga.

---

## 1. Elegir del catálogo

```
GET https://<host>/v1/external/agent-templates
Authorization: Bearer lel_<prefijo>_<secreto>
```

```json
{
  "data": [
    {
      "id": "7c1e...",
      "slug": "ejecutivo-de-ventas",
      "name": "Ejecutivo de ventas",
      "description": "Califica leads y agenda reuniones",
      "category": "sales",
      "knowledgeCollections": ["precios", "garantias"]
    }
  ],
  "limit": 50,
  "offset": 0,
  "hasMore": false
}
```

`knowledgeCollections` dice qué colecciones va a querer el agente que salga de esta plantilla. Si tu
organización todavía no las tiene, creálas antes con
[la guía de conocimiento](lelemon-api-conocimiento.md) y el agente nace completo.

---

## 2. Crear el agente

```
POST https://<host>/v1/external/agent-templates/<templateId>/instantiate
Authorization: Bearer lel_<prefijo>_<secreto>
Content-Type: application/json
```

```json
{
  "slug": "ventas",
  "name": "Ventas"
}
```

| Campo | Qué es |
|---|---|
| `slug` | Obligatorio. Cómo se llama el agente dentro de tu organización, en minúsculas con guiones. |
| `name` | Opcional. Por omisión, el nombre de la plantilla. |

La respuesta:

```json
{
  "id": "9a4f...",
  "slug": "ventas",
  "name": "Ventas",
  "status": "draft",
  "audience": "external",
  "channels": ["whatsapp"],
  "knowledge": {
    "assigned": ["precios"],
    "missing": ["garantias"]
  }
}
```

`201` cuando lo creó. `200` cuando ese slug ya nombraba un agente: es el mismo, no una copia.

**`missing` son las colecciones que la plantilla quería y tu organización no tiene.** No se crean
solas a propósito: una colección vacía se ve configurada y no tiene un solo documento que citar, así
que el agente contestaría como si no supiera nada y nadie sabría por qué. Creá las que falten y
volvé a llamar — la segunda llamada devuelve `200`, no duplica el agente, y termina de asignar.

### El slug es la llave de idempotencia

Mandá siempre el mismo slug para el mismo agente. Si tu cliente HTTP corta por timeout y reintenta,
o si el guion corre dos veces, la segunda llamada devuelve el agente que ya existe.

Esto importa más de lo que parece: por la pantalla, crear dos veces deja `ventas` y `ventas-2` —dos
agentes válidos, ningún error, y nadie se entera hasta que uno contesta distinto que el otro—. Con
el slug fijo eso no puede pasar.

### El cuerpo no acepta `tools`

Si lo mandás, la respuesta es `400` y nombra el campo. Las acciones del agente salen de la
plantilla; agregarle una necesita `actions:write`. Se rechaza en vez de ignorarse justamente para
que no te vayas creyendo que concediste un permiso que no concediste.

---

## 3. Activarlo

```
POST https://<host>/v1/external/agents/<agentId>/activate
Authorization: Bearer lel_<prefijo>_<secreto>
```

```json
{
  "id": "9a4f...",
  "slug": "ventas",
  "status": "active",
  "readiness": [
    {
      "codigo": "sin_flujo",
      "mensaje": "El agente 'ventas' no está conectado a ningún flujo, así que no hay dónde dejar la conversación. Asígnalo a una etapa desde la pantalla del flujo."
    }
  ]
}
```

**`readiness` es lo que todavía impide que alguien lo alcance.** Vacío significa que ya lo alcanza
cualquiera: es lo único que tenés que mirar para saber que terminaste.

| `codigo` | Qué pasa | Cómo se arregla |
|---|---|---|
| `sin_flujo` | Ninguna etapa lo tiene asignado, así que no hay dónde dejar la conversación. | Asignalo a una etapa desde la pantalla del flujo. |
| `no_es_la_etapa_de_entrada` | Está en un flujo pero no en su etapa de entrada, así que contestaría otro agente. | Movelo a la etapa de entrada, o abrí la conversación con el agente que sí está ahí. |
| `sin_canal_api` | Su lista de canales no incluye `api`, así que no atiende por esta API. | Agregá el canal desde la configuración del agente. |

Los códigos son estables: podés ramificar sobre ellos. Los mensajes son para mostrarle a una
persona.

**La activación no falla por nada de esto.** Un agente puede estar activo y atender sólo por
WhatsApp, que es lo normal. Lo que sí falla es que le falte el prompt o el modelo: eso da `400`,
porque activarlo así reventaría con el primer mensaje de un cliente real.

Hoy la etapa del flujo se asigna desde la pantalla. Es el único paso que todavía no tiene API.

---

## Pausar

```
POST https://<host>/v1/external/agents/<agentId>/pause
```

Deja de atender. Las conversaciones abiertas quedan donde están; no contesta hasta que lo actives de
nuevo.

---

## Clonar un agente que ya anda

```
POST https://<host>/v1/external/agents/<agentId>/template
Content-Type: application/json
```

```json
{
  "name": "Ventas — plantilla",
  "category": "sales"
}
```

Fotografía su configuración: prompt, modelo, acciones, canales, comportamientos y las colecciones de
conocimiento que tiene asignadas. Después la instanciás como cualquier otra plantilla, todas las
veces que quieras.

El asistente interno de tu organización no se puede fotografiar ni tocar por esta API. Trae
herramientas que leen contactos con su teléfono, tratos y líneas de cartola con RUT de la
contraparte, y por API no hay lista blanca de teléfonos que lo proteja.

---

## Errores

| Código | Qué pasó |
|---|---|
| `400` | El cuerpo está mal. Viene con `errors`, que dice qué campo y qué forma tenía. |
| `401` | Falta la credencial, o está revocada o es inválida. |
| `403` | A la credencial le falta `agents:write`. |
| `404` | Esa plantilla o ese agente no existe en tu organización. También si el agente es el asistente interno. |
| `409` | No se pudo crear ni encontrar el agente. Reintentá. |
| `429` | Pasaron un techo por minuto: 3.000 pedidos por credencial o 200 por dirección IP. Esperen lo que diga `Retry-After`. |

Las respuestas traen `RateLimit-Limit`, `RateLimit-Remaining` y `RateLimit-Reset` para que no
tengas que llegar al `429` para saber cómo vas.

---

## Guardar la credencial

En el gestor de secretos del sistema que hace las llamadas, nunca en el repositorio ni en el cuerpo
de un flujo exportado. Si se filtró, revocala desde la pantalla: la credencial revocada deja de
servir en el momento, y la fila queda para poder responder después qué hizo.

Una credencial por sistema, con el alcance más chico que le sirva. Si el guion de onboarding sólo
crea agentes, no le des `knowledge:write` ni `actions:write`.
