Ir al contenido
Integraciones

Claves de API y uso de la API (para programadores)

Usala con tu IA:Abrir en ChatGPTAbrir en Claude Markdown

¿Para qué sirve?

La API de Bravilo permite que otro sistema (tu CRM, tu sistema de gestión, tu app o un desarrollo propio) trabaje con Bravilo sin que nadie entre al panel: hablar con tus agentes, leer y cerrar sus conversaciones, cargarles información y mandar plantillas de WhatsApp. Esta guía es para la persona que programa esa conexión.

Bravilo tiene tres formas de conectarse con otros sistemas. Elegí la que corresponde:

Querés que…Usá
Tu sistema le pida algo a Bravilo (mandarle un mensaje al agente, leer una conversación, cargar información)Esta API, con una clave
El agente consulte tu sistema durante una charla (por ejemplo, el stock de un producto)Conectar APIs
Bravilo le avise a tu sistema cuando pasa algo (por ejemplo, un lead nuevo)Webhooks

Cómo crear una clave

  1. Hacé clic en tu inicial, arriba a la derecha, después en Configuración y después en API Keys.
  2. Hacé clic en Crear API Key. La clave nueva aparece en la lista.
  3. Pasá el mouse sobre la clave: a la derecha aparecen dos íconos. Con el de copiar la copiás; con la papelera la borrás.

Pantalla API Keys: Claves de API, el enlace Documentación, la lista de claves y el botón Crear API Key

Cómo cuidarlas

  • Tratala como una contraseña. Usala solo desde tu servidor: nunca en el código que se ejecuta en el navegador, en una app que se instala en el teléfono ni en un repositorio público.
  • Una clave por integración. Si conectás varios sistemas, creá una clave para cada uno. Así podés cortar uno sin afectar a los demás.
  • Si se filtró, reemplazala. Creá una clave nueva, cambiala en tu sistema y recién después borrá la vieja con la papelera.

Cuidado: si borrás una clave que está en uso, esa integración deja de funcionar al instante.

Antes de probar los ejemplos: los pedidos de lectura (los GET y las búsquedas) no cambian nada. Los demás sí: query crea conversaciones que aparecen en Conversaciones; upsert, update y DELETE cambian lo que sabe el agente; PATCH cambia el estado de una conversación; y send-template le manda un WhatsApp real a un cliente. Probalos con un agente de prueba.

Cómo se usa

Todas las rutas de esta guía empiezan con esta dirección base:

https://app.braviloai.com/api

Por ejemplo, GET /agents es https://app.braviloai.com/api/agents.

En cada pedido mandá la clave en el encabezado Authorization. Los pedidos con cuerpo, además, con Content-Type: application/json:

Authorization: Bearer TU_CLAVE
Content-Type: application/json

Los IDs que vas a usar:

Nombre en esta guíaQué esDónde lo conseguís
ID_DEL_AGENTEEl agenteEn GET /agents (campo id) o en la dirección del navegador cuando abrís el agente en Bravilo
ID_DE_LA_CONVERSACIONUna conversaciónEn la respuesta de query (conversationId)
ID_DEL_VISITANTELa persona que escribeEn la respuesta de query (visitorId)
ID_DE_LA_BASEUna base de conocimientoEn GET /datastores o en GET /agents/ID_DEL_AGENTE (en tools, el datastoreId)
ID_DE_LA_FUENTEUna fuente dentro de una baseEn la respuesta de upsert (ids) o en GET /datastores/ID_DE_LA_BASE

Índice de endpoints

MétodoRutaPara qué sirveDatos importantes
Agentes
GET/agentsListar los agentes de tu organización—
GET/agents/ID_DEL_AGENTEVer nombre, descripción, visibilidad y base de un agente—
Consultas al agente (ver)
POST/agents/ID_DEL_AGENTE/queryMandarle un mensaje y recibir la respuestaquery (obligatorio), conversationId, visitorId, streaming
Conversaciones (ver)
GET/conversations/ID_DE_LA_CONVERSACIONVer la conversación, sus mensajes y su lead?cursor= para mensajes anteriores
GET/conversations/ID_DE_LA_CONVERSACION/summaryPedir un resumen hecho con IA?refresh=true para uno nuevo
PATCH/conversations/ID_DE_LA_CONVERSACIONCambiar el estadostatus (obligatorio)
Bases de conocimiento (ver)
GET/datastoresListar las bases—
GET/datastores/ID_DE_LA_BASEVer una base con sus fuentes y su estado—
POST/datastores/ID_DE_LA_BASE/queryBuscar en una basequery (obligatorio), topK
POST/external/datastores/upsert/ID_DE_LA_BASECargar textosdocuments, con seis campos de metadata
POST/external/datastores/update/ID_DE_LA_BASECorregir un texto cargadoid (obligatorio), text, name
Fuentes (ver)
GET/datasources/ID_DE_LA_FUENTEVer una fuente—
DELETE/datasources/ID_DE_LA_FUENTEBorrar una fuente—
Plantillas de WhatsApp (ver)
POST/v2/agents/ID_DEL_AGENTE/send-templateMandar una plantilla aprobada por MetaphoneNumber y templateName (obligatorios). Manda un mensaje real
Análisis de archivos (ver)
POST/agents/ID_DEL_AGENTE/universalDescribir una imagen o extraer el texto de un archivofileBuffer, fileType y fileName (obligatorios)

Hablar con un agente

POST /agents/ID_DEL_AGENTE/query le manda un mensaje al agente, como si fuera un cliente, y devuelve su respuesta.

curl -X POST https://app.braviloai.com/api/agents/ID_DEL_AGENTE/query \
  -H "Authorization: Bearer TU_CLAVE" \
  -H "Content-Type: application/json" \
  -d '{"query": "Hola, ¿hacen envíos a Rosario?"}'
Campo del cuerpoQué es
queryObligatorioEl mensaje del cliente
conversationIdOpcionalPara seguir una conversación que ya existe. Sin él, se crea una nueva
visitorIdOpcionalLa persona que escribe. Sin él, se crea uno nuevo
streamingOpcionaltrue para recibir la respuesta por partes (ver abajo)

Respuesta (resumida):

{
  "answer": "Sí, hacemos envíos a Rosario: llegan en 48 a 72 horas hábiles.",
  "sources": [
    {"datasource_id": "cmv…", "datasource_name": "Política de envíos", "datasource_type": "text", "score": 1}
  ],
  "conversationId": "cmv…",
  "visitorId": "cmv…",
  "messageId": "cmv…",
  "usage": {"promptTokens": 1906, "completionTokens": 297, "totalTokens": 2203}
}
CampoQué es
answerLo que respondió el agente
sourcesLas fuentes que usó para responder
conversationIdLa conversación. Guardalo para seguirla
visitorIdLa persona que escribe. Guardalo para seguir la conversación
messageIdEl mensaje de la respuesta
usageCuánto procesamiento usó la respuesta

Seguir la misma conversación

Mandá en cada mensaje siguiente el conversationId y el visitorId que te devolvió la primera respuesta. Así el agente recuerda lo que se habló y todo queda en una sola conversación:

curl -X POST https://app.braviloai.com/api/agents/ID_DEL_AGENTE/query \
  -H "Authorization: Bearer TU_CLAVE" \
  -H "Content-Type: application/json" \
  -d '{"query": "¿Y cuánto cuesta el envío?", "conversationId": "ID_DE_LA_CONVERSACION", "visitorId": "ID_DEL_VISITANTE"}'

Recibir la respuesta por partes (streaming)

Con "streaming": true, la respuesta llega como un flujo de eventos (text/event-stream) mientras el agente escribe:

event: answer
data: S%C3%AD%2C%20hacemos%20env%C3%ADos

event: endpoint_response
data: %7B%22messageId%22%3A%22cmv%E2%80%A6%22%2C%22answer%22%3A%22S%C3%AD%E2%80%A6%22%7D

event:
data: [DONE]
  • answer: un pedazo del texto de la respuesta.
  • endpoint_response: al final, la respuesta completa con los mismos campos del modo normal (answer, sources, conversationId, visitorId, messageId).
  • [DONE]: la respuesta terminó.

El contenido de cada data llega codificado para URL (por ejemplo, %20 es un espacio). Pasalo por decodeURIComponent antes de mostrarlo; el de endpoint_response, después, por JSON.parse.

Las conversaciones que se crean así aparecen en Conversaciones con el canal API. Si corresponde, el agente también crea el lead.

Leer y cerrar conversaciones

Ver una conversación

curl https://app.braviloai.com/api/conversations/ID_DE_LA_CONVERSACION \
  -H "Authorization: Bearer TU_CLAVE"

Respuesta (resumida):

{
  "id": "cmv…",
  "status": "UNRESOLVED",
  "isAiEnabled": true,
  "lead": {"id": "cmv…", "name": "Lead sin nombre", "phone": null, "priority": "medium", "notes": "…"},
  "messages": [
    {"id": "cmv…", "from": "agent", "text": "Sí, hacemos envíos a Rosario…", "createdAt": "2026-10-21T01:03:49.549Z"},
    {"id": "cmv…", "from": "human", "text": "¿Me lo pueden mandar a Rosario?", "createdAt": "2026-10-21T01:03:36.090Z"}
  ]
}
  • messages viene del más nuevo al más viejo.
  • En from, agent es el agente y human es el cliente.

Mensajes anteriores. Si la conversación es larga, pedí la página siguiente con cursor y el id del último mensaje que recibiste (el más viejo):

curl "https://app.braviloai.com/api/conversations/ID_DE_LA_CONVERSACION?cursor=ID_DEL_MENSAJE_MAS_VIEJO" \
  -H "Authorization: Bearer TU_CLAVE"

Devuelve los mensajes anteriores a ese. Cuando messages llega vacío, no quedan más.

Pedir un resumen

curl "https://app.braviloai.com/api/conversations/ID_DE_LA_CONVERSACION/summary?refresh=true" \
  -H "Authorization: Bearer TU_CLAVE"
{"summary": "El cliente consulta por 40 bolsas de cemento con envío a Rosario…", "model": "gpt-4o-mini", "tokens_used": 109, "max_tokens": 500, "cached": false}

Sin ?refresh=true devuelve el resumen guardado, si hay uno.

Cambiar el estado

curl -X PATCH https://app.braviloai.com/api/conversations/ID_DE_LA_CONVERSACION \
  -H "Authorization: Bearer TU_CLAVE" \
  -H "Content-Type: application/json" \
  -d '{"status": "RESOLVED"}'

status es obligatorio y acepta:

  • RESOLVED: resuelta.
  • UNRESOLVED: abierta.
  • HUMAN_REQUESTED: el cliente pidió hablar con una persona.

Respuesta: {"id": "cmv…", "status": "RESOLVED", "isAiEnabled": true}.

Por la API solo se cambia el estado. Para tomar el control de una conversación o devolvérsela a la IA, hacelo desde Conversaciones.

Cargarle información al agente

La información que usa el agente se guarda en bases de conocimiento (en la API, datastores; ver Cargar documentos). Cada base tiene fuentes (en la API, datasources).

1. Encontrá la base

curl https://app.braviloai.com/api/datastores \
  -H "Authorization: Bearer TU_CLAVE"

Devuelve {"items": [...]}, con el id y el name de cada base. Para ver una base con todas sus fuentes y el estado de cada una (synched cuando ya se puede usar):

curl https://app.braviloai.com/api/datastores/ID_DE_LA_BASE \
  -H "Authorization: Bearer TU_CLAVE"

2. Cargá un texto

curl -X POST https://app.braviloai.com/api/external/datastores/upsert/ID_DE_LA_BASE \
  -H "Authorization: Bearer TU_CLAVE" \
  -H "Content-Type: application/json" \
  -d '{
    "documents": [{
      "text": "Hacemos envíos a todo el país. A Rosario llegan en 48 a 72 horas hábiles.",
      "metadata": {
        "datastore_id": "ID_DE_LA_BASE",
        "datasource_id": "UN_ID_NUEVO",
        "datasource_type": "file",
        "datasource_name": "Política de envíos",
        "mime_type": "text/plain",
        "source_url": "https://tuempresa.com/envios"
      }
    }]
  }'
  • Los seis campos de metadata son obligatorios.
  • datasource_id tiene que ser un identificador nuevo en formato cuid (letras y números, empieza con «c»; por ejemplo, el que genera la librería cuid de JavaScript).
  • documents acepta varios textos a la vez.

Respuesta: {"ids": ["ID_DE_LA_FUENTE"]}. Guardá ese ID: lo vas a necesitar para corregir o borrar la fuente. En pocos segundos la fuente queda sincronizada y el agente ya responde con ese dato.

3. Corregí un texto

curl -X POST https://app.braviloai.com/api/external/datastores/update/ID_DE_LA_BASE \
  -H "Authorization: Bearer TU_CLAVE" \
  -H "Content-Type: application/json" \
  -d '{"id": "ID_DE_LA_FUENTE", "name": "Política de envíos", "text": "Hacemos envíos a todo el país. A Rosario llegan en 48 horas hábiles."}'
  • id es obligatorio, y tiene que ser el que te devolvió el upsert.
  • Respuesta: {"id": "ID_DE_LA_FUENTE"}. La fuente se vuelve a procesar.

4. Buscá en una base

Para ver qué información encuentra el agente sobre un tema:

curl -X POST https://app.braviloai.com/api/datastores/ID_DE_LA_BASE/query \
  -H "Authorization: Bearer TU_CLAVE" \
  -H "Content-Type: application/json" \
  -d '{"query": "envíos a Rosario", "topK": 3}'
  • query es obligatorio. topK (opcional) es la cantidad máxima de resultados.
  • Devuelve una lista de fragmentos, del más relacionado al menos:
[{"text": "Hacemos envíos a todo el país…", "score": 0.59, "source": "https://tuempresa.com/envios", "datasource_id": "cmv…", "offset": 0}]

5. Mirá o borrá una fuente

curl https://app.braviloai.com/api/datasources/ID_DE_LA_FUENTE \
  -H "Authorization: Bearer TU_CLAVE"
curl -X DELETE https://app.braviloai.com/api/datasources/ID_DE_LA_FUENTE \
  -H "Authorization: Bearer TU_CLAVE"

El DELETE borra la fuente y el agente deja de usar esa información.

Para subir archivos (PDF, Word, Excel), por ahora hacelo desde la pestaña Archivos y conocimientos del agente. Ver Cargar documentos.

Mandar una plantilla de WhatsApp

Este pedido le manda un WhatsApp real al número que pongas. No lo uses para probar con números de clientes.

Para escribirle primero a un cliente por WhatsApp hace falta una plantilla aprobada por Meta (ver Campañas), y el agente tiene que tener WhatsApp conectado.

curl -X POST https://app.braviloai.com/api/v2/agents/ID_DEL_AGENTE/send-template \
  -H "Authorization: Bearer TU_CLAVE" \
  -H "Content-Type: application/json" \
  -d '{
    "phoneNumber": "5491122334455",
    "templateName": "nombre_de_tu_plantilla",
    "languageCode": "es",
    "components": [
      {"type": "body", "parameters": [{"type": "text", "text": "Juan"}]}
    ]
  }'
CampoQué es
phoneNumberObligatorioEl número con código de país, sin «+» ni espacios
templateNameObligatorioEl nombre exacto de la plantilla aprobada
languageCodeOpcionalEl idioma de la plantilla, por ejemplo es
componentsOpcionalLos valores de las variables de la plantilla (por ejemplo, el nombre del cliente)

Si el agente no tiene WhatsApp conectado, responde 400 con {"error": "No WhatsApp integration found for this agent or organization"}.

Analizar una imagen o un documento

POST /agents/ID_DEL_AGENTE/universal recibe un archivo y devuelve lo que entiende de él: la descripción de una imagen o el texto de un documento.

curl -X POST https://app.braviloai.com/api/agents/ID_DEL_AGENTE/universal \
  -H "Authorization: Bearer TU_CLAVE" \
  -H "Content-Type: application/json" \
  -d '{
    "fileBuffer": "CONTENIDO_DEL_ARCHIVO_EN_BASE64",
    "fileType": "png",
    "fileName": "foto.png",
    "userQuery": "¿Qué producto es?"
  }'
CampoQué es
fileBufferObligatorioEl archivo en base64 (una forma de escribir un archivo como texto)
fileTypeObligatorioLa extensión: jpg, jpeg, png, tiff, pdf, mp3, wav, m4a, ogg, webm, mp4, avi o mov
fileNameObligatorioEl nombre del archivo
userQueryOpcionalQué querés saber del archivo

Respuesta: {"success": true, "messageText": "…"}, con el análisis en messageText. Con un formato no aceptado (por ejemplo, txt o docx) responde 400 con la lista de formatos válidos.

Errores

Los errores llegan como JSON con un campo error.

CódigoCuándo pasaEjemplo
400Falta un campo obligatorio, o un valor o un ID no tiene el formato esperado{"error": "status: Required"}, {"error": "conversationId: Invalid cuid"}
403Falta la clave, no es válida, o el recurso no es de tu organización{"error": "UNAUTHORIZED"}
404El agente o la fuente no existen{"error": "NOT_FOUND"}
500Error del servidor. También pasa si mandás query sin el campo query{"error": "Internal server error"}

Preguntas frecuentes

Todo me responde 403 «UNAUTHORIZED». Revisá que mandes el encabezado Authorization: Bearer TU_CLAVE en cada pedido, que la clave esté completa y que no la hayas borrado. Si es un solo pedido, revisá que el ID sea de tu organización.

El agente responde como si no recordara lo anterior. Mandá el conversationId y el visitorId de la primera respuesta en cada mensaje siguiente.

Cargué un texto con upsert y me da error 400. Revisá que estén los seis campos de metadata y que datasource_id sea un identificador nuevo en formato cuid.

Corregí un texto con update y me da 403. Usá el ID que te devolvió el upsert (el que viene en ids), no uno inventado.

El texto que llega en streaming tiene símbolos raros, como «%20». Está codificado. Pasalo por decodeURIComponent.

¿Dónde veo las conversaciones que creé por la API? En Conversaciones, con el canal API.

¿Te sirvió esta guía?

Guías relacionadas

¿No lo resolviste?

Escribinos por el chat: te responde el equipo de Bravilo.

Volver a Integraciones