Saltar al contenido principal

La bandeja de entrada por la API

La bandeja de entrada es donde el equipo atiende desde el panel. La API da acceso a las mismas conversaciones, para conectar un helpdesk, un CRM o un bot: lo que responde tu integración aparece para el equipo, y lo que responde el equipo aparece para tu integración.

Leer las conversaciones

curl 'https://api.joinotify.com/inbox/conversations?folder=open&unread=true' \
-H 'Authorization: Bearer sk_live_xxx'

La lista se pagina por cursor: envía el nextCursor de la respuesta en cursor para la página siguiente, hasta que venga null. Los mensajes de una conversación vienen en GET /inbox/conversations/{id}/messages, del más reciente al más antiguo, con el cursor en before.

Cada conversación dice si la ventana de 24 horas está abierta (windowOpen) y hasta cuándo (windowExpiresAt). Es lo que decide qué se puede responder.

Responder

curl -X POST https://api.joinotify.com/inbox/conversations/cm1c0nv000001/messages \
-H 'Authorization: Bearer sk_live_xxx' \
-H 'Content-Type: application/json' \
-d '{ "kind": "text", "text": "¡Hola, Ana! Tu pedido salió para entrega." }'
kindCuándo usarlo
textTexto libre, con la ventana abierta
mediaUn archivo, con la ventana abierta — súbelo antes en /media
templateVentana cerrada: una plantilla aprobada reabre la conversación
noteNota interna para el equipo; nunca va a WhatsApp

Con la ventana cerrada, text y media se rechazan antes de salir, con 422 window_closed — a diferencia de POST /messages, que acepta y falla después. Responder reabre una conversación cerrada o pospuesta y pausa las automatizaciones de esa conversación por un tiempo, como cuando alguien del equipo toma el control.

Para enviar un archivo, sube sus bytes a POST /inbox/conversations/{id}/media (el cuerpo es el archivo, no multipart, con el nombre en X-File-Name) y responde con { "kind": "media", "assetId": "..." }.

Organizar

PATCH /inbox/conversations/{id} cierra, reabre, deja pendiente, pospone hasta una fecha, asigna a alguien del equipo (los ids vienen de GET /inbox/agents) o pausa las automatizaciones. Cada cambio queda registrado en la conversación, y cerrar y asignar disparan los eventos conversation.closed y conversation.assigned.

Una API key no es una persona del equipo: la carpeta mine siempre viene vacía, y en los eventos assigned_by y closed_by vienen null cuando quien actuó fue la clave.

Tiempo real

GET /inbox/stream es un stream Server-Sent Events. Avisa qué cambió — mensaje nuevo, estado de mensaje, conversación actualizada, alguien escribiendo —, sin el contenido:

event: message.created
id: 12
data: {"type":"message.created","conversationId":"cm1c0nv000001","phoneNumberId":"106540352242922","messageId":"cm1m3ss4g3001","direction":"in"}

Al recibir un evento, busca la conversación o los mensajes. Tres cuidados:

  • No hay reenvío. Un evento perdido durante una caída no vuelve, y Last-Event-ID no recupera nada. En cada reconexión, vuelve a leer las conversaciones que importan.
  • El EventSource del navegador no envía la cabecera Authorization. Usa un cliente que la envíe — el stream está pensado para servidores.
  • Cada API key abre como máximo 5 streams a la vez.

Si solo necesitas reaccionar a mensajes recibidos, el webhook de messages sigue siendo el camino más simple.

Alcance de la clave

Una API key restringida a algunos números solo ve las conversaciones de esos números — una conversación de otro número responde 404, como si no existiera. GET /inbox/agents, que lista el equipo de la cuenta con los correos, responde 403 para ella.