GUÍA DE IMPLEMENTACIÓN

API pública

Integra chat, archivos y voz desde una web o una app. Una superficie limitada por usuario, diseñada para ejecutarse de forma segura en clientes públicos.

Actualizado sep 2026Lectura de 8 min

Cómo funciona

Cada mensaje entra por POST /api/message, se autentica y pasa al pipeline compartido del agente. La API conserva el contexto por userId, ejecuta tools si hacen falta y devuelve todas las partes de la respuesta en el mismo request.

01Autentica
02Recupera contexto
03Ejecuta agente
04Entrega respuesta
El mismo agente en todas partes

La API hereda prompt, memoria, herramientas, guardrails y deduplicación. No necesitas mantener una implementación paralela.

Elige la credencial correcta

Todos los requests indican el agente con X-Agent-Id. La credencial viaja como Bearer token y depende de dónde corre tu integración.

WEB PÚBLICA

Key publicable

Empieza con pk_ y solo puede crear una sesión anónima. Segura para un widget.

USUARIO FINAL

Sesión

Token kas1. limitado a una identidad. Ideal cuando el usuario ya inició sesión.

Crear sesión anónima
curl https://agents.kaitools.dev/api/sessions \
  -H "Authorization: Bearer pk_widget.SECRETO" \
  -H "X-Agent-Id: mi-agente" \
  -H "Content-Type: application/json" \
  -d '{ "mode": "anonymous" }'
201 · Sesión creada
{
  "token": "kas1.eyJhZ2VudERvY0lkIjo...",
  "userId": "anon_K8x2mQp4...",
  "expiresAt": 1789106400,
  "expiresIn": 3600
}
CAMPOREGLA DE POST /api/sessions
modeanonymous con key publicable; el servidor genera el usuario
previousTokenOpcional; conserva el usuario anónimo al renovar, incluso si expiró
ttlSecondsOpcional; mínimo 60 s y máximo 24 h

Puede responder 400 invalid_request, 401 unauthorized, 429 rate_limited con Retry-After o 503 sessions_disabled.

!
Nunca publiques una key secreta

Una key secreta puede apuntar a cualquier conversación. Para frontend usa una key pk_ o una sesión emitida por tu backend.

Tu primer request

1

Define una identidad estable

Usa el identificador del usuario en tu sistema. El mismo userId continúa la misma conversación.

2

Envía el mensaje

Añade un messageId único para que cualquier reintento sea idempotente.

3

Renderiza las partes

reply trae el texto agregado; parts conserva texto, imágenes, documentos y otros resultados en orden.

Terminal
curl https://agents.kaitools.dev/api/message \
  -H "Authorization: Bearer $KAI_API_KEY" \
  -H "X-Agent-Id: mi-agente" \
  -H "Content-Type: application/json" \
  -d '{
    "userId": "cliente_123",
    "messageId": "pedido_8f31",
    "profileName": "Ana",
    "message": {
      "type": "text",
      "text": "¿Qué horarios manejan?"
    }
  }'
200 · Respuesta
{
  "code": "success",
  "conversationId": "api:cliente_123",
  "messageId": "pedido_8f31",
  "reply": "Abrimos de lunes a viernes, de 9 a 18 h.",
  "parts": [
    { "kind": "text", "text": "Abrimos de lunes a viernes, de 9 a 18 h." }
  ]
}

Mensajes aceptados

TYPECAMPOS
texttext
image / videourl HTTPS, mimeType, caption?
audiourl HTTPS, mimeType
documenturl HTTPS, mimeType, filename?, caption?
locationlatitude, longitude, name?, address?

Con sesión, userId se omite porque está firmado en el token. Si se envía debe coincidir. messageId es opcional, pero conviene generarlo para que los reintentos respondan duplicate en vez de crear otro turno.

Streaming con SSE

Agrega Accept: text/event-stream al mismo endpoint. Recibirás eventos message, tool y finalmente done. El evento final tiene exactamente el mismo contrato que la respuesta JSON normal.

Streaming con curl
curl -N https://agents.kaitools.dev/api/message \
  -H "Authorization: Bearer $KAI_API_KEY" \
  -H "X-Agent-Id: mi-agente" \
  -H "Content-Type: application/json" \
  -H "Accept: text/event-stream" \
  -d '{ "userId": "cliente_123", "message": { "type": "text", "text": "Cotiza mi pedido" } }'
EVENTOCONTENIDO
messageUna parte estructurada lista para renderizar
toolNombre, fase call/result y duración cuando aplica
doneEl AgentReply completo que cerró el turno
errorFallo ocurrido después de abrir el stream

No es streaming token por token: cada evento representa una parte completa que el agente decidió entregar. Esto mantiene estable la experiencia entre HTTP, WhatsApp, Messenger e Instagram.

Todas las rutas públicas

MÉTODORUTAUSO
POST/api/sessionsCrear identidad segura para frontend
POST/api/messageEnviar un mensaje o abrir SSE
POST/api/uploadsSubir adjuntos de hasta 20 MB
POST/api/voice/sessionsCrear un ticket efímero para voz realtime
WS/api/voice/realtimeAbrir audio bidireccional usando el ticket efímero
GET/api/conversations/:idLeer la conversación de la propia sesión
GET/api/conversations/:id/messagesPaginar el historial visible de la propia sesión
El aislamiento está en el token

Una sesión solo puede leer y escribir su conversación. Intentar acceder a otra identidad responde 404 para no revelar que existe.

Archivos, voz e historial

Subir archivos

POST /api/uploads recibe multipart/form-data con un campo file de hasta 20 MB. Devuelve { url, type, mimeType, filename }; usa esa URL en un mensaje de tipo image, video, audio o document.

Subir un archivo
curl https://agents.kaitools.dev/api/uploads \
  -H "Authorization: Bearer $SESSION_TOKEN" \
  -H "X-Agent-Id: mi-agente" \
  -F "file=@menu.pdf"
201 · Archivo almacenado
{
  "url": "https://storage.googleapis.com/.../menu.pdf",
  "type": "document",
  "mimeType": "application/pdf",
  "filename": "menu.pdf"
}

El MIME determina type; si falta, se infiere de la extensión. Los fallos propios son 400 invalid_file/invalid_multipart, 413 file_too_large, 415 unsupported_media_type y 500 upload_failed.

Crear una llamada de voz

POST /api/voice/sessions acepta mode como hands_free o push_to_talk. Devuelve callId, ticket, expiresAt, protocol y websocketUrl. El cliente abre el WebSocket con los subprotocolos indicados por la respuesta.

Crear ticket de voz
curl https://agents.kaitools.dev/api/voice/sessions \
  -H "Authorization: Bearer $SESSION_TOKEN" \
  -H "X-Agent-Id: mi-agente" \
  -H "Content-Type: application/json" \
  -d '{ "mode": "hands_free", "profileName": "Ana" }'
201 · Ticket válido por 60 s
{
  "callId": "e7c87a89-...",
  "expiresAt": 1789102860,
  "protocol": "kai.voice.v1",
  "ticket": "kvt1...",
  "websocketUrl": "wss://voice.agents.kaitools.dev/api/voice/realtime"
}

Abre websocketUrl ofreciendo kai.voice.v1 y kai.ticket.<ticket>. Envía PCM16 mono de 16 kHz y recibe PCM16 mono de 24 kHz. hands_free usa detección de voz; push_to_talk requiere eventos activity.start y activity.end.

El socket emite session.ready, state, transcript, tool, message, interrupted, pong, error y session.ended. Puede rechazar la creación con 403 voice_disabled, 409 voice_requires_post_validation, 429 rate_limited o 503 voice_unavailable.

Leer el propio historial

GET /api/conversations/:id devuelve los metadatos del hilo y GET /api/conversations/:id/messages?limit=50&cursor=… pagina sus mensajes visibles en orden cronológico. limit admite 1–100 y el cursor debe tratarse como opaco.

200 · Página de mensajes
{
  "conversationId": "api:anon_K8x2mQp4...",
  "items": [
    {
      "id": "msg_01",
      "role": "user",
      "kind": "user_message",
      "content": "¿Qué horarios manejan?",
      "createdAt": "2026-09-11T08:00:00.000Z"
    }
  ],
  "nextCursor": "eyJpZCI6Im1zZ18wMS..."
}

Una sesión solo puede consultar su propio id de conversación. Estas rutas devuelven 400 invalid_request para ids o cursores inválidos y 404 not_found tanto si el hilo no existe como si pertenece a otra sesión.

Errores y reintentos

Reintenta fallos de red, respuestas 5xx y 409 conversation_busy. Conserva siempre el mismo messageId: el servidor lo deduplicará y evitará una segunda respuesta.

HTTPCODEACCIÓN
400invalid_requestCorrige body, id o cursor; no reintentes igual
401unauthorized / session_expiredRenueva la sesión o revisa la credencial
403forbiddenRespeta el bloqueo o corrige el alcance
409conversation_busyEspera retryAfterMs y reutiliza el messageId
429rate_limitedEspera el header Retry-After
5xxError de servicioReintenta con backoff exponencial y jitter
El SDK ya resuelve esta parte

Incluye ids automáticos, timeout de 130 segundos, backoff con jitter y errores tipados.

SIGUIENTEConfigurar y operar desde la API privada