Enviar mensajes
El endpoint principal del Canal API es POST /channel/message. Devuelve un 200 inmediato confirmando que el mensaje se encoló; la respuesta del bot llega después por webhook o por el stream SSE.
Request completo
Sección titulada «Request completo»POST https://api.overtaker.online/api/v1/channel/messageAuthorization: Bearer sk_live_YOUR_KEYContent-Type: application/jsonX-Overtaker-Version: 1Idempotency-Key: msg-001 # opcional — recomendado, evita duplicados (TTL 24h)
{ "session_id": "user-123", // recomendado — identidad opaca de la sesión (≤200 chars) "email": "cliente@empresa.com", // opcional — solo si lo conocés (se usa para el Lead) "contact_name": "Café Aroma", // opcional — nombre legible del contacto (≤120 chars) "message": "Hola, necesito ayuda", // requerido — texto (máx. 5000 chars). OPCIONAL en audio (se transcribe) "message_type": "text", // opcional — ver tabla abajo (default: "text") "media_base64": "<base64>", // opcional — archivo en base64 "media_mime_type": "image/jpeg" // obligatorio si hay media_base64}Respuesta:
{ "message_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "status": "received"}session_id — identidad de la sesión
Sección titulada «session_id — identidad de la sesión»session_id es el identificador opaco de la sesión. El bot lo usa para reconocer al usuario y mantener contexto entre mensajes. Puede ser cualquier string de hasta 200 caracteres (UUID, ID interno, teléfono, lo que sea).
| Qué envías | Comportamiento |
|---|---|
Solo session_id | Sesión anónima válida — el bot opera sin email del usuario. |
Solo email | Compat histórica — el session_id se deriva del email. |
| Ambos | session_id manda como identidad de sesión; email se preserva para identificar/crear el Lead. |
// Mismo session_id = misma sesión, el bot recuerda el contexto{"session_id": "user-123", "message": "Hola"}{"session_id": "user-123", "message": "¿Qué me dijiste antes?"} // ✓ recuerda
// session_id distinto = sesión nueva, sin contexto previo{"session_id": "user-456", "message": "Hola"} // conversación independientecontact_name — nombrar el contacto
Sección titulada «contact_name — nombrar el contacto»contact_name es opcional y le da al contacto un nombre legible en el panel (de lo contrario aparece como api:<session_id>, ilegible y no-buscable). Útil cuando ya conocés el nombre del cliente/empresa al iniciar la sesión.
| Aspecto | Comportamiento |
|---|---|
| Cuándo aplica | Al crear el contacto, y en mensajes posteriores mientras aún no tenga nombre. |
| Si ya tiene nombre | No se pisa — un nombre ya capturado (por vos o por el agente) tiene prioridad. |
| Ausente / vacío | No-op, flujo idéntico al actual. |
| Largo | Se recorta a 120 caracteres. |
{ "session_id": "proyecto-42", "message": "Hola", "contact_name": "Café Aroma" }// → el contacto queda como "Café Aroma" en el panel y en el evento contact.updatedSi necesitás capturar más datos del contacto durante la conversación (no solo el nombre), eso lo hace el agente vía protocolo — ver Completar datos estructurados.
Tipos de mensaje
Sección titulada «Tipos de mensaje»message_type | Formatos soportados | media_mime_type (ejemplos) |
|---|---|---|
text | Texto plano (default) | — (no lleva media) |
image | JPEG, PNG, WebP, GIF | image/jpeg, image/png, image/webp, image/gif |
video | MP4, 3GPP | video/mp4 |
audio | MP3, OGG, MP4 audio, WebM | audio/mpeg, audio/ogg, audio/mp4, audio/webm |
document | PDF, Word, Excel, PowerPoint, TXT | application/pdf, application/vnd.openxmlformats-officedocument.wordprocessingml.document (.docx) |
Para mensajes con media, codificá el archivo en base64 y declará su media_mime_type:
# Pythonimport base64, requests
with open("imagen.jpg", "rb") as f: b64 = base64.b64encode(f.read()).decode()
requests.post(f"{BASE}/channel/message", headers=HEADERS, json={ "session_id": "user-123", "message": "Mira esta imagen", "message_type": "image", "media_base64": b64, "media_mime_type": "image/jpeg",})const fs = require("fs");const b64 = fs.readFileSync("./imagen.jpg").toString("base64");
await fetch(`${BASE}/channel/message`, { method: "POST", headers: HEADERS, body: JSON.stringify({ session_id: "user-123", message: "Mira esta imagen", message_type: "image", media_base64: b64, media_mime_type: "image/jpeg", }),});Idempotencia — reintentos seguros
Sección titulada «Idempotencia — reintentos seguros»Si tu request falla por timeout o red, podés reintentarlo con la misma clave de idempotencia y el mensaje no se procesa dos veces. Usá el header HTTP estándar Idempotency-Key (estilo Stripe / PayPal):
POST https://api.overtaker.online/api/v1/channel/messageAuthorization: Bearer sk_live_YOUR_KEYContent-Type: application/jsonX-Overtaker-Version: 1Idempotency-Key: mi-sistema-msg-id-20260527-001
{ "session_id": "user-123", "message": "Quiero cotizar" }
// Reintento con la misma key dentro de 24h → mismo message_id, sin reprocesar.- TTL: 24 horas.
- Máximo: 200 caracteres (si supera →
400).
Silencio — cuándo el bot no responde
Sección titulada «Silencio — cuándo el bot no responde»El bot puede decidir no responder. En ese caso no llega ningún evento message.response a tu webhook. Ocurre cuando el bot detecta un mensaje fuera de contexto, spam, o está en modo handoff activo.
Streaming SSE
Sección titulada «Streaming SSE»POST /channel/message/stream es la variante streaming. Mismo body que /channel/message, pero la respuesta llega como Server-Sent Events — pensado para chats con efecto “typing” donde mostrás el texto a medida que se genera. Bucket write (60/min).
POST https://api.overtaker.online/api/v1/channel/message/streamAuthorization: Bearer sk_live_YOUR_KEYContent-Type: application/jsonX-Overtaker-Version: 1Accept: text/event-stream
{ "session_id": "user-123", "message": "Cuéntame de sus planes" }Response (text/event-stream):
event: tokendata: {"text": "Tenemos tres "}
event: tokendata: {"text": "planes principales: "}
event: tokendata: {"text": "Starter, Pro y Enterprise..."}
event: donedata: { "message_id": "uuid", "conversation_id": "uuid", "lead_id": "uuid o vacío", "session_id": "user-123", "full_text": "Tenemos tres planes principales: Starter, Pro y Enterprise...", "quick_replies": ["Ver Starter", "Ver Pro"], "action": "REPLY"}
// En caso de errorevent: errordata: {"detail": "Error interno procesando el mensaje"}// Node.js — consumir el stream con fetch + ReadableStreamconst res = await fetch(`${BASE}/channel/message/stream`, { method: "POST", headers: { ...HEADERS, Accept: "text/event-stream" }, body: JSON.stringify({ session_id: "user-123", message: "Hola" }),});
const reader = res.body.getReader();const decoder = new TextDecoder();let buffer = "";
while (true) { const { value, done } = await reader.read(); if (done) break; buffer += decoder.decode(value, { stream: true });
const blocks = buffer.split("\n\n"); // \n\n delimita un evento SSE buffer = blocks.pop() || ""; // resto incompleto
for (const block of blocks) { const event = block.match(/^event:\s*(.+)$/m)?.[1]; const data = block.match(/^data:\s*(.+)$/m)?.[1]; if (!event || !data) continue; const payload = JSON.parse(data); if (event === "token") process.stdout.write(payload.text); if (event === "done") console.log("\n[FIN]", payload.message_id); if (event === "error") console.error("[ERROR]", payload.detail); }}