Quickstart: tu primera petición en 60 segundos

Creá una clave de API en tu workspace, exportala, y listá tus conversaciones. Las claves tienen alcance por workspace y pueden ser de solo lectura o de lectura-escritura.

curl https://api.inteligenciaviva.com/v1/workspaces/WORKSPACE_ID/conversations \
  -H "Authorization: Bearer sk_live_..."

Toda respuesta es JSON. Los endpoints de colección devuelven arreglos `data`; los errores devuelven un objeto `error` con `code` y `message` y el estado HTTP correspondiente.

La API gratuita — sin cuenta, sin clave

La capa gratuita también habla API. `POST /v1/free/widget-snippet` recibe exactamente la misma configuración que arma el generador visual de la página Gratis y devuelve el snippet HTML listo para pegar — sin autenticación, sin registro. Usala para generar widgets por lote para sitios de clientes, regenerar embeds desde CI cuando cambian horarios, o simplemente para tocar la API antes de pagar un centavo.

curl -X POST https://api.inteligenciaviva.com/v1/free/widget-snippet \
  -H "Content-Type: application/json" \
  -d @widget-config.json
# → { "snippet": "<link rel=\"stylesheet\" ..." }

Autenticación

Toda petición lleva una clave de API en la cabecera `Authorization` como token Bearer. Las claves se crean y revocan desde la propia API o desde el panel, por workspace — revocar una clave corta su acceso de inmediato.

Los endpoints de sesión a nivel de cuenta (registro, login, acceso con Google, canje de ticket, perfil y eliminación de cuenta bajo `/v1/auth`) alimentan nuestras propias apps; para integraciones servidor-a-servidor preferí siempre claves de API de workspace sobre sesiones de usuario.

Authorization: Bearer sk_live_...

Referencia de recursos

Todas las rutas de abajo cuelgan de `https://api.inteligenciaviva.com/v1/workspaces/{workspaceId}` salvo que se indique. Esta es la superficie real montada del backend actual — no aspiracional.

Bandeja cruzada /inbox

GET/conversationsTodas las conversaciones de TODOS los workspaces que atiende la credencial, en una sola lista ordenada por actividad. Es la única familia de rutas que no cuelga de /workspaces/{id}.
GET/conversations/{id}Una conversación buscada entre todos tus workspaces, sin saber en cuál está. Devuelve su workspace_id.
GET/workspacesLos workspaces que esta credencial puede atender, con su rol en cada uno.
GET/visitantesQuién está navegando AHORA en todos tus workspaces, con la página que lee y si ya conversa. Es una foto del momento, no un historial: no se pagina. Un workspace que no contesta no tumba la lista: va en `casas_sin_responder`.

Conversaciones /conversations

GET/Listar conversaciones, filtrables por estado.
GET/{id}Una conversación con su estado, canal, contacto y asignación.
GET/{id}/messagesHistorial de mensajes (visitante, agente humano, IA, sistema).
POST/{id}/messagesEnviar un mensaje a la conversación como agente.
POST/{id}/assignAsignar la conversación a un agente humano o a un departamento.
POST/{id}/resolveCerrar la conversación. Si la IA la cerró sola, cuenta como resolución facturable.
POST/Abrir una conversación desde fuera del widget (por ejemplo, para migrar un hilo existente).
GET/{id}/ai-turns«¿De dónde sacó eso?» — la bitácora de cada turno de IA: en qué se apoyó, con qué modelo, cuánto tardó y por qué se traspasó.
GET/{id}/portal-caseEstado del caso que esta conversación abrió en el tablero de soporte del Portal, con sus respuestas. Se consulta en vivo: el estado cambia en el otro tablero.
POST/{id}/readMarcar la conversación como leída por el equipo (contador de no leídos).
POST/{id}/reopenReabrir una conversación cerrada, conservando el historial. Sin esto, un cierre por error no tenía vuelta atrás.
POST/{id}/archiveArchivar o desarchivar. Sale de la bandeja sin cerrarse ni perder nada; el cuerpo es {valor:true|false}.
POST/{id}/pinFijar arriba o soltar. Las fijadas encabezan la primera página de la bandeja. Cuerpo: {valor:true|false}.
POST/{id}/muteSilenciar los avisos durante N horas, o {horas:null} para volver a oírla.
POST/{id}/unreadDejarla como no leída: retrocede la marca de lectura por detrás del último mensaje del cliente.
POST/{id}/typing«Escribiendo…» hacia el cliente: en WhatsApp lo recibe Meta y en el chat web, el widget del visitante. Sin cuerpo; 204 siempre (en los canales que no lo admiten no hace nada) y 404 si la conversación no existe.
POST/{id}/messages/{messageId}/forwardReenviar un mensaje a otra conversación del mismo workspace, con su adjunto (se copia, no se comparte). Cuerpo: `{to_conversation_id}`. Sale como mensaje del agente.
PUT/{id}/messages/{messageId}/reactionReaccionar con un emoji a un mensaje de WhatsApp; sustituye la reacción anterior del negocio y `{emoji:null}` la quita. Solo WhatsApp (API de Meta) y solo sobre un mensaje que Meta ya confirmó: si no, 400.
POST/{id}/locationEnviar una ubicación por WhatsApp: `{lat, lon, name?, address?, reply_to_message_id?}`. Se manda a Meta primero: si Meta la rechaza, no queda en el hilo. Solo WhatsApp.
POST/{id}/contact-cardEnviar una tarjeta de contacto por WhatsApp: `{name:{formatted_name}, phones?, emails?, reply_to_message_id?}`. Igual que la ubicación: primero Meta, después el hilo. Solo WhatsApp.
GET/{id}/copilotoEl copiloto del agente: lo que la IA ha sugerido para esta conversación y lo hablado con ella. Es privado del equipo: el cliente no lo ve.
POST/{id}/copilotoPreguntarle algo a la IA en privado sobre esta conversación (`{content}`, hasta 4.000 caracteres). Responde AL AGENTE, no al cliente: nada sale por el canal. 503 `copilot_unavailable` si no puede responder.
POST/{id}/copiloto/{sugerenciaId}/enviarMandar al cliente una sugerencia del copiloto, tal cual o editada (`{content}` opcional). Sale como mensaje del agente, con su autor humano. Una sugerencia se envía una sola vez: la segunda da 409 `ya_enviada`.
POST/{id}/resumenPedir el resumen de la conversación (abierta o cerrada) y actualizar con él la ficha del contacto. Cuesta una tarea del KIT; pedirlo otra vez sin mensajes nuevos no vuelve a cobrar. Si no hay resumen, devuelve por qué (`motivo`, `codigo`).

Contactos /contacts

GET/Listar contactos.
GET/{id}Un contacto con sus identidades y su historial de conversaciones.
GET/{id}/historialDe qué fueron sus últimas consultas: las últimas conversaciones con su resumen (`?limit=`, hasta 25), los totales y la ficha del contacto. Abrirla no genera la ficha si falta, porque cuesta dinero: se pide aparte con `?generar=1`.

Agentes de IA /ai-agents

GET/Listar los agentes de IA del workspace.
POST/Crear un agente de IA: instrucciones, departamento, umbral de confianza.
PATCH/{id}Actualizar instrucciones, umbral o departamento.
GET/{id}/knowledge-sourcesFuentes de conocimiento asociadas a este agente.
POST/{id}/knowledge-sourcesAsociar una fuente de conocimiento a este agente.
GET/{aiAgentId}Un agente de IA: instrucciones, umbral de confianza, herramientas y departamento.

Conocimiento (RAG) /knowledge-sources

POST/documentIndexar un documento como conocimiento fundamentado.
POST/urlIndexar una URL.
GET/{id}Estado y metadatos de la fuente.
DELETE/{id}Eliminar una fuente y sus vectores.
POST/{id}/reindexVolver a leer una fuente de URL sin borrarla: si la descarga falla, no se toca nada.
GET/Listar las fuentes de conocimiento con su estado de indexación.

Ficha del negocio /ficha

GET/Listar las unidades (filtros ?estado= y ?tipo=) con los umbrales de las franjas.
POST/Crear una unidad: pregunta, variantes, respuesta aprobada, URL e idioma. Un administrador o una clave la publica; un agente la propone. La IA nunca publica.
GET/{id}Ver una unidad.
PATCH/{id}Editar una unidad; cambiar la pregunta o las variantes recalcula su embedding.
DELETE/{id}Borrar una unidad (administrador).
POST/{id}/aprobarAprobar una propuesta: pasa a activa y queda quién la aprobó (administrador).
POST/{id}/rechazarRechazar una propuesta (administrador).
POST/probarVista previa: qué contestaría la ficha a una pregunta, con su nota y su franja (opcional: incluir las propuestas).
POST/recalcularRecalcular los embeddings que falten o no casen con el texto (administrador).

Canales /channels

GET/Listar canales. Vivos hoy: chat web y API; más se conectan a medida que salgan.
PUT/{id}/credentialsDefinir las credenciales de un canal.
PUT/{id}/statusHabilitar o deshabilitar un canal.
POST/Crear un canal.
GET/{channelId}Un canal con su tipo, estado y ajustes.
GET/{channelId}/snippetLa línea que el cliente pega en su sitio. Es UNA sola: todo lo demás se configura aquí.
GET/{channelId}/configLa configuración del chat: lo publicado (`config`) y lo que este canal sobrescribe (`settings`). Son distintos y conviene no confundirlos.
PUT/{channelId}/configGuardar la configuración. Guardar y publicar son el mismo paso: si la CDN falla, el PUT falla — no se responde «guardado» a medias.
POST/{channelId}/config/publishVolver a publicar la configuración a la CDN sin cambiarla.
POST/{channelId}/avatarSubir la cara del asistente (≤512 KB). Va a R2 y la sirve la CDN.
POST/republicar-todosRepublicar todos los canales del workspace. Lo hace el cron cuando cambia el pack de cadenas; esto es para no esperar.
PUT/{channelId}/modoEl modo de atención del canal: `{modo:"humano"|"hibrido"|"ia"}`. Decide si la IA le habla al cliente. Vale para cualquier tipo de canal (WhatsApp, Telegram, correo, widget). Requiere ser administrador.
PUT/{channelId}/autoLos mensajes automáticos del canal: `{bienvenida, fueraDeHorario}` (hasta 1.000 caracteres cada uno; `null` borra). El de fuera de horario usa el horario del canal. Requiere ser administrador.
PUT/{channelId}/tarjetaLa tarjeta de contacto que el agente puede enviar desde este canal: `{tarjetaContacto:{name:{formatted_name}, phones?, emails?}}`, con al menos un teléfono o un correo; `null` la quita. Solo WhatsApp. Requiere ser administrador.
PUT/{channelId}/horarioEl horario de atención: `{officeHours:{tz, dias:{lun:[["09:00","17:00"]], …}, festivos:["MM-DD" o "YYYY-MM-DD"]}}`; `null` lo quita (24/7). Lo que no se entiende se rechaza con 400 diciendo qué está mal. Devuelve `abiertoAhora`, calculado por el servidor. Requiere ser administrador.

Agentes humanos y departamentos /agents · /departments

GET/agentsListar los agentes humanos del workspace.
POST/agents/{userId}/departmentsPoner un agente en un departamento.
GET/departmentsListar departamentos.
PUT/departments/{id}/routing-rulesDefinir reglas de enrutamiento de conversaciones entrantes.
POST/agentsInvitar a una persona al workspace, con su rol y departamentos.
GET/agents/{userId}Una persona del equipo.
POST/departmentsCrear un departamento con sus reglas de enrutado.
GET/departments/{id}Un departamento.

Webhooks, claves, facturación, analítica /webhooks · /api-keys · /billing · /analytics

POST/webhooksRegistrar un endpoint; recibís su secreto de firma una sola vez.
DELETE/webhooks/{id}Eliminar un endpoint.
POST/api-keysCrear una clave (solo lectura o lectura-escritura).
DELETE/api-keys/{id}Revocar una clave de inmediato.
GET/billing/subscriptionPlan, resoluciones de IA incluidas y uso del período.
GET/analyticsMétricas agregadas del workspace.
GET/webhooksListar los endpoints de webhook.
GET/webhooks/{id}/deliveriesHistorial de entregas de un webhook, con reintentos. Es lo que dice si el fallo es tuyo o nuestro.
GET/api-keysListar las claves. Nunca se devuelve el secreto: solo su prefijo.

Herramientas del agente /conversations/{id} · /canned-responses · /tags · /presence

GET/conversations/{id}/notesNotas internas de la conversación. NUNCA las ve el visitante.
POST/conversations/{id}/notesAñadir una nota interna.
DELETE/conversations/{id}/notes/{noteId}Borrar una nota interna.
GET/canned-responsesRespuestas predefinidas del workspace, con su atajo.
POST/canned-responsesCrear una respuesta predefinida.
PATCH/canned-responses/{cannedId}Editar una respuesta predefinida.
DELETE/canned-responses/{cannedId}Borrar una respuesta predefinida.
POST/presenceLatido de presencia: declara que hay una persona atendiendo. Solo con sesión — una clave de API que late no es alguien mirando la pantalla, y se rechaza con 400.
GET/presence¿Hay alguien en línea ahora mismo? Es lo que decide si un traspaso espera o se deriva.
GET/ai-usageConsumo de inferencia del workspace por día y las conversaciones que más gastan.
GET/tagsEtiquetas del workspace.
GET/conversations/{id}/tagsEtiquetas de una conversación.
POST/conversations/{id}/tagsEtiquetar una conversación. Crea la etiqueta si no existía.
DELETE/conversations/{id}/tags/{tagId}Quitar una etiqueta de la conversación.
POST/conversations/{id}/transferTransferir la conversación a un departamento. Manda sobre las reglas de enrutado: una transferencia hecha a mano no se deshace sola.
POST/contacts/{contactId}/blockBloquear un contacto. Se le responde 403 sin detalle: a quien abusa no se le explica cómo se le detecta.
GET/canned-responses/categoriesLas categorías ya usadas en las respuestas predefinidas (texto libre), para autocompletar.
POST/canned-responses/{cannedId}/useAnotar que se usó una respuesta predefinida (contador y última vez). Siempre 204, exista o no: es telemetría de uso, no una acción.

Adjuntos, dispositivos y workspaces /conversations/{id}/attachments · /devices · /workspaces

POST/conversations/{id}/attachmentsSubir un adjunto como agente. Lista blanca estricta y 10 MB: no entra nada que un navegador pueda ejecutar.
GET/conversations/{id}/attachmentsAdjuntos de una conversación.
GET/attachments/{attachmentId}Descargar un adjunto. Se sirve con cabeceras duras y `nosniff`; solo imágenes y audio se muestran en línea.
POST/devices/registerRegistrar el token push de un dispositivo, para avisar a una persona cuando la IA traspasa. Fuera del prefijo de workspace: `/v1/devices/register`.
POST/workspacesCrear un workspace. Fuera del prefijo: `/v1/workspaces`.
GET/workspaces/{workspaceId}/retencion/ensayoEl ensayo de la purga: cuántas conversaciones y adjuntos (y bytes) se llevaría la retención, sin borrar nada. `?days=` pregunta por un plazo que aún no está guardado. Requiere ser administrador y `analytics:read`. Ruta completa: `/v1/workspaces/{workspaceId}/retencion/ensayo`.

Visitantes en vivo y tiempo real /visitantes · /stream-ticket

GET/visitantesQuién está navegando ahora en los sitios de este workspace: página, idioma, desde cuándo y si ya tiene el chat abierto o una conversación.
POST/visitantes/{visitorId}/invitarInvitar a un visitante a abrir el chat, con `{mensaje}` opcional (sin él, el saludo del widget en su idioma). No crea conversación ni gasta IA: la conversación nace cuando el visitante contesta. 404 `no_esta` si ya se fue; 409 `ya_conversa` si ya está conversando.
POST/stream-ticketUn ticket para abrir el WebSocket del stream con `?ticket=` sin poner el token en la URL. Dura 60 s y sirve una sola vez. Vale con sesión o con clave de API.

Capacidades del KIT /conversations/{id}/kit

GET/Las tareas del KIT lanzadas en esta conversación, con su estado. Al listarlas se repasan las pendientes.
GET/catalogoQué capacidades del KIT se pueden lanzar sobre un adjunto (`?attachment_id=`) o sobre el hilo, con buscador (`?q=`, `?lang=`). Solo lectura: no cuesta nada.
POST/Lanzar una capacidad del KIT (`{action, attachment_id?, params?}`) sobre un adjunto o sobre el hilo. El resultado vuelve como NOTA INTERNA, nunca al cliente. Cuesta dinero del monedero del KIT, y la misma petición no se cobra dos veces. 202 con la tarea en cola.

Teléfono (VOICE) /voice

GET/llamadasEl registro de llamadas de la central del workspace (`?limite=` hasta 200, `?antes_de=` para paginar). Alcance `voice:read`; 404 si el workspace no tiene central.
GET/llamadas-en-cursoLas llamadas que están ocurriendo ahora.
GET/aparcadasLas llamadas aparcadas, en espera de que alguien las retome.
GET/grabaciones/{id}/audioEl audio de una grabación, servido a través del API (sin enlace público) y sin caché. La central registra quién la escuchó.
GET/extensionesLas extensiones que el panel puede usar como teléfono (las reservadas no salen).
POST/webphone-sesionRegistrar el navegador como teléfono de una extensión (`{extension_id}`): devuelve una credencial SIP efímera, que la central rota en cada canje. Alcance `voice:write`. La clave de la central nunca sale del servidor.

Ejemplos reales

Enviar un mensaje de agente (curl)

curl -X POST \
  https://api.inteligenciaviva.com/v1/workspaces/$WS/conversations/$CONV/messages \
  -H "Authorization: Bearer $IV_KEY" \
  -H "Content-Type: application/json" \
  -d '{"content": "Hi! I checked your order — it ships tomorrow."}'

Listar conversaciones (JavaScript)

const res = await fetch(
  `https://api.inteligenciaviva.com/v1/workspaces/${WS}/conversations`,
  { headers: { Authorization: `Bearer ${process.env.IV_KEY}` } }
);
const { data } = await res.json();

Verificar la firma de un webhook (Node)

import { createHmac, timingSafeEqual } from "node:crypto";

function verify(secret, rawBody, header, toleranceSec = 300) {
  const { t, v1 } = Object.fromEntries(
    header.split(",").map((p) => p.split("="))
  );
  if (Math.abs(Date.now() / 1000 - Number(t)) > toleranceSec) return false;
  const expected = createHmac("sha256", secret)
    .update(`${t}.${rawBody}`)
    .digest("hex");
  return timingSafeEqual(Buffer.from(expected), Buffer.from(v1));
}

Webhooks

Registrá un endpoint HTTPS y te enviamos cada evento por POST, firmado. La cabecera es `iv-signature: t=<unix>,v1=<hex>`, donde `v1` es el HMAC-SHA256 de `<timestamp>.<cuerpo crudo>` con el secreto de tu endpoint — rechazá todo lo que tenga más de 5 minutos (protección contra replay). Las entregas fallidas se reintentan automáticamente con firma fresca, hasta 5 intentos.

conversation.createdArrancó una conversación nueva en cualquier canal.
message.createdCayó un mensaje (visitante, humano, IA o sistema).
handoff.requestedLa IA decidió que necesita un humano — el momento exacto, con el resumen.
conversation.resolvedSe cerró una conversación; te dice si la IA la cerró sola.

Cada entrega y su estado quedan registrados — un webhook en el que no podés confiar es peor que ninguno.

Streaming en tiempo real

Para interfaces en vivo, abrí un WebSocket contra el stream del workspace y recibí los eventos de conversación según ocurren — el mismo canal que usan nuestras propias apps de agente.

wss://api.inteligenciaviva.com/v1/workspaces/{workspaceId}/stream

Errores y límites

Los errores son HTTP convencional: `400` petición malformada, `401` clave ausente o inválida, `403` clave sin alcance, `404` recurso que no está en este workspace, `429` límite de tasa (esperá y reintentá con jitter), `5xx` culpa nuestra — reintentá las peticiones idempotentes. El cuerpo siempre trae un `error.code` y un `error.message` humano. Los límites de tasa son por clave y generosos para integraciones reales.

Preguntas frecuentes

¿Esta documentación es aspiracional o la superficie real?

Real. Está escrita contra las rutas montadas del backend actual. Cuando algo aquí dice "en camino", es que todavía no existe — no documentamos humo.

¿Necesito un plan de pago para usar la API?

Sí — la API es parte de la plataforma de pago (Pro la incluye). El Widget gratuito y el plugin de WordPress son client-side y no necesitan API.

¿Cuál es la diferencia entre claves de solo lectura y de lectura-escritura?

Las de solo lectura pueden listar y consultar pero nunca mutar — ideales para dashboards y BI. Las de lectura-escritura pueden crear mensajes, contactos, agentes de IA y configuración.

¿Cómo me entero de mensajes nuevos — polling o webhooks?

Webhooks (`message.created`) para servidores, el stream por WebSocket para interfaces en vivo. El polling funciona, pero vas a chocar con los límites de tasa antes de lograr tiempo real.

¿Cómo verifico exactamente que un webhook vino de ustedes?

Recalculá el HMAC-SHA256 de `<t>.<cuerpo crudo>` con el secreto de tu endpoint y comparalo en tiempo constante con el valor `v1` de la cabecera `iv-signature`; rechazá si `t` tiene más de 300 segundos. El código funcional está en esta página.

¿Qué pasa si mi endpoint de webhooks está caído?

Registramos la entrega fallida y reintentamos automáticamente con timestamp y firma frescos, hasta 5 intentos. Si el endpoint fue eliminado o deshabilitado mientras tanto, los reintentos paran limpiamente.

¿Puede la IA escalar hacia mi propio sistema?

Sí — para eso existe `handoff.requested`. Se dispara en el momento en que la conversación pasa a una persona (el visitante lo pidió, hubo señales de crisis o de frustración, o la respuesta no se apoyaba en el conocimiento), con el resumen de la conversación, para que avisés a una persona o abrás un ticket en otro sistema.

¿Qué canales puedo manejar por la API hoy?

El chat web está vivo de punta a punta, y todo lo de esta referencia funciona hoy. Telegram y más canales de mensajería se conectan al mismo modelo de conversación a medida que salgan — tu integración no cambia.

¿Hay SDK?

Un SDK de Flutter alimenta nuestra propia app de agente y es la base del SDK white-label de Enterprise. Para servidores, la superficie REST es deliberadamente simple — cualquier cliente HTTP funciona sin wrapper.

¿Dónde reporto un bug de la API o un hueco en esta documentación?

Escribí a l@inteligenciaviva.com. Una documentación que miente también es un bug — la tratamos con la misma severidad.