API · conversations
conversations operations in the OpenAPI contract that accept an API key.
Updated:
GET /conversations
Listar conversaciones del inbox (más recientes primero)
Array sin envoltorio; se pagina con limit + offset (F2-03). El panel lo adapta a Page (F3-16). Rol staff (F2-52): solo las conversaciones de clientes con tareas vigentes (o cerradas hace menos de 48 h) en un recurso propio; sin tareas, lista vacía. Los filtros se combinan entre sí (AND) y siempre dentro de ese alcance.
- Authentication: API key or dashboard session
- Scopes: None
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
mode | query | No | bot, human, paused | |
lifecycle | query | No | array<ConversationLifecycle> | Filtro por estado del ciclo de vida (F2-35). Multi-valor (?lifecycle=pending&lifecycle=waiting_team): une los estados con OR. Valor desconocido: 422. |
channel_type | query | No | array<—> | Filtro por canal visible (el mismo valor que channel_type de cada conversación). Multi-valor (?channel_type=whatsapp&channel_type=telegram): une los canales con OR. Valor desconocido: 422. |
last_message_from | query | No | string (date-time) | Solo conversaciones cuyo último mensaje (last_message_at) es igual o posterior a este instante (ISO 8601 con zona). Sin mensajes no entra en ningún rango. |
last_message_to | query | No | string (date-time) | Solo conversaciones cuyo último mensaje es anterior a este instante (límite exclusivo, [from, to)). |
bot_paused | query | No | boolean | true: el bot no responde (una persona tomó la conversación, human, o se pausó, paused). false: solo las que atiende el bot. |
replied_by_me | query | No | boolean | true: conversaciones donde quien consulta (sesión del panel) mandó al menos un mensaje; false: las demás. Con API key no hay persona: true devuelve una lista vacía. |
needs_attention | query | No | boolean | true: solo las conversaciones que necesitan atención; false: solo las que no (F3-487). Se calcula en la consulta con la misma regla que el campo needs_attention y que el conteo de /conversations/counts, así que paginar con offset recorre exactamente ese número de conversaciones. |
limit | query | No | integer default 50 | |
offset | query | No | integer default 0 |
Responses
| Status | Description |
|---|---|
200 | OK (array<Conversation>) |
default | Error (RFC 9457) (Problem) |
GET /conversations/{id}
Detalle de una conversación
Rol staff (F2-52): conversación de un cliente sin tareas suyas ⇒ 404 (no revela que existe).
- Authentication: API key or dashboard session
- Scopes: None
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
id | path | Yes | string |
Responses
| Status | Description |
|---|---|
200 | OK (Conversation) |
default | Error (RFC 9457) (Problem) |
PATCH /conversations/{id}
Actualizar etiquetas y/o estado del ciclo de vida de una conversación
contact_action (F3-143) permite release/report_spam/block/unblock solo con sesión owner/manager (contacts:moderate; API key 403). No ejecuta ni confirma tareas retenidas. Parcial: al menos uno de contact_action, tags (reemplaza las etiquetas) o lifecycle (transición manual, F2-35); sin ninguno, 422. Transiciones permitidas: entre pending, waiting_customer, waiting_team hacia cualquiera de ellas, resolved o closed; open solo desde resolved/closed (reabrir); desde resolved también a pending/closed; desde closed solo a open/pending. Otra combinación: 409 lifecycle_transition_not_allowed. Mismo estado = no-op idempotente (sin evento). resolution_reason solo con resolved/closed (422 si no) y se limpia al reabrir. Cada cambio se audita (sin datos personales) y emite conversation.lifecycle_changed. Roles owner/manager/staff; viewer 403. API key: scope messages:write. El ciclo de vida es ortogonal al modo bot/humano. lifecycle: resolved equivale a POST /resolve sobre el último entrante (fija resolved_at, emite conversation.resolved); salir de resolved limpia resolved_at. "Archivar" = closed (sale de la lista principal con ?lifecycle= de los estados activos; "desarchivar" = open). Un mensaje nuevo del cliente reabre una conversación resolved o closed (pasa a pending).
- Authentication: API key or dashboard session
- Scopes:
messages:write
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
id | path | Yes | string |
Request body (required)
application/json: object
Responses
| Status | Description |
|---|---|
200 | Conversación actualizada (Conversation) |
default | Error (RFC 9457) (Problem) |
GET /conversations/{id}/messages
Mensajes de una conversación (cronológico ascendente)
Rol staff (F2-52): conversación de un cliente sin tareas suyas ⇒ 404.
- Authentication: API key or dashboard session
- Scopes: None
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
id | path | Yes | string | |
limit | query | No | integer default 50 | |
before | query | No | string (date-time) | Solo mensajes anteriores a este instante (paginación hacia atrás) |
Responses
| Status | Description |
|---|---|
200 | OK (array<Message>) |
default | Error (RFC 9457) (Problem) |
POST /conversations/{id}/messages
Enviar mensaje manual (pone la conversación en modo humano)
Persiste una salida delivery_status=pending y el takeover en la misma transacción. El worker entrega después del commit; 201 no confirma aceptación del proveedor. Idempotency-Key opcional deduplica 24 h para sesión de panel y Bearer; una misma clave con otro actor, conversación o texto devuelve 422. Un replay devuelve la respuesta original (puede seguir indicando pending); GET messages muestra el estado actual. Un cliente is_test sólo puede usar simulator, nunca una conexión real. Entrega externa at-least-once: sin idempotencia del proveedor existe ventana ambigua. Rol staff (F2-52): solo a clientes con tareas vigentes en un recurso propio; otra conversación ⇒ 404 y no se envía nada. 409 channel_paused si el dueño pausó ese canal a nivel channel (el equipo tampoco envía hasta reanudarlo).
- Authentication: API key or dashboard session
- Scopes: None
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
id | path | Yes | string | |
Idempotency-Key | header | No | string |
Request body (required)
application/json: object
Responses
| Status | Description |
|---|---|
201 | Persistido, pendiente de despacho durable (Message) |
default | Error (RFC 9457) (Problem) |
POST /conversations/{id}/messages/{message_id}/intent
Registrar una intención explícitamente clasificada por el operador
Owner/manager con sesión y CSRF, o API key messages:write. Mensaje entrante real de esta conversación y tenant. Clasificación cerrada e inmutable; mismo valor es replay y distinto devuelve 409. No usa texto LLM, no ejecuta la intención ni modifica estado de reservas.
- Authentication: API key or dashboard session
- Scopes:
messages:write
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
id | path | Yes | string | |
message_id | path | Yes | string (uuid) |
Request body (required)
application/json: object
Responses
| Status | Description |
|---|---|
200 | Clasificación persistida (o replay) (MessageIntent) |
default | Error (RFC 9457) (Problem) |
POST /conversations/{id}/mode
Cambiar el modo (takeover / devolver al bot / pausar)
Rol staff (F2-52): solo conversaciones de sus clientes; otra ⇒ 404.
- Authentication: API key or dashboard session
- Scopes: None
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
id | path | Yes | string |
Request body (required)
application/json: object
Responses
| Status | Description |
|---|---|
200 | OK (object) |
default | Error (RFC 9457) (Problem) |
POST /conversations/{id}/notes
Agregar un aviso de sistema a la conversación (nunca se envía al cliente)
F3-222. Deja un mensaje type=system en el hilo (p. ej. el contexto de un caso de soporte): nace delivery_status=sent, sin destinatario, así que el worker nunca lo despacha por el canal. No cambia el modo (bot/humano) ni el ciclo de vida y no cuenta como respuesta del equipo. Roles owner/manager (sesión con CSRF) o API key con messages:write; tenant desde la credencial; 404 si la conversación no es de este proyecto. Idempotente por contenido: el mismo texto ya presente como aviso en la conversación devuelve ese aviso (sin otro mensaje ni otra auditoría), con cualquier clave o key. Con API key, Idempotency-Key además deduplica 24 h (la misma clave con otro cuerpo da 409). Auditado conversation.system_note (solo el largo del texto).
- Authentication: API key or dashboard session
- Scopes:
messages:write
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
id | path | Yes | string | |
Idempotency-Key | header | No | string |
Request body (required)
application/json: object
Responses
| Status | Description |
|---|---|
201 | Aviso guardado en el hilo (Message) |
default | Error (RFC 9457) (Problem) |
POST /conversations/{id}/read
Marcar la conversación como leída
Rol staff (F2-52): solo conversaciones de sus clientes; otra ⇒ 404.
- Authentication: API key or dashboard session
- Scopes: None
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
id | path | Yes | string |
Responses
| Status | Description |
|---|---|
200 | OK (object) |
default | Error (RFC 9457) (Problem) |
POST /conversations/{id}/resolve
Resolver explícitamente la conversación leída
Owner/manager con sesión y CSRF, o API key messages:write. Tenant de la credencial. message_id debe ser el último entrante persistido del tenant; snapshot obsoleto devuelve 409. Repetir sobre el mismo mensaje no cambia fecha ni duplica audit/evento. Una nueva entrada real reabre; un replay del webhook no reabre. No cambia el modo ni ejecuta reservas.
- Authentication: API key or dashboard session
- Scopes:
messages:write
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
id | path | Yes | string |
Request body (required)
application/json: object
Responses
| Status | Description |
|---|---|
200 | Resuelto (o replay de la misma resolución) (ConversationResolution) |
default | Error (RFC 9457) (Problem) |
GET /conversations/{id}/summary
Último resumen con IA de la conversación (sin llamar al LLM)
Owner, manager y staff. Devuelve el resumen guardado (summary: null si nunca se generó) y stale: true si llegaron mensajes después. Nunca consume IA. Rol staff (F2-52): conversación de un cliente sin tareas suyas ⇒ 404. Viewer ⇒ 403.
- Authentication: API key or dashboard session
- Scopes: None
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
id | path | Yes | string |
Responses
| Status | Description |
|---|---|
200 | OK (ConversationSummary) |
default | Error (RFC 9457) (Problem) |
POST /conversations/{id}/summary
Resumir la conversación con IA (acotado y cacheado)
Owner, manager y staff con sesión de panel y CSRF. Texto de lectura para el equipo: el LLM no decide nada. Pasa por el AI gateway (cuota del proyecto, pausa de IA manual y por spam, freno de ráfaga, registro de uso summary.conversation). Solo se leen los últimos 40 mensajes, con tope de caracteres de entrada y 2048 tokens de salida (incluido razonamiento), con un único reintento de hasta 4096 tokens si el contenido llega vacío; los teléfonos y emails se enmascaran ([tel], [email]) antes y después. El último resumen se guarda con el id del último mensaje considerado: sin mensajes nuevos se devuelve el guardado (cached: true) sin llamar al LLM, aunque la IA esté pausada. Rol staff: fuera de su alcance ⇒ 404. 409 ai_paused (pausa del dueño o auto_spam), 429 ai_limit_reached (cuota) y demás ai_* de admisión, 409 ai_unavailable (key pendiente), 422 conversation_empty (sin texto), 502 ai_provider_unavailable (error del proveedor, detail con tipo/status/motivo seguros) o ai_empty_response (modelo sin texto tras dos intentos).
- Authentication: API key or dashboard session
- Scopes: None
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
id | path | Yes | string |
Responses
| Status | Description |
|---|---|
200 | Resumen vigente (generado ahora o reutilizado) (ConversationSummary) |
default | Error (RFC 9457) (Problem) |
GET /conversations/counts
Contadores de conversaciones por estado del ciclo de vida
Cantidad de conversaciones por lifecycle (siempre las seis claves, con 0) y total, para los contadores de la Bandeja. Excluye las sesiones de prueba (is_test), igual que la lista. mode opcional restringe por modo bot/humano. Lectura para cualquier rol; API key: scope messages:read (F2-35).
- Authentication: API key or dashboard session
- Scopes:
messages:read
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
mode | query | No | bot, human, paused |
Responses
| Status | Description |
|---|---|
200 | OK (ConversationLifecycleCounts) |
default | Error (RFC 9457) (Problem) |
GET /conversations/stream
Avisos en vivo del inbox (SSE)
text/event-stream. Eventos message.received, message.sent y conversation.updated con
{event, workspace_id, conversation_id, mode, lifecycle, unread_count, channel_type, message_id?, direction?}. channel_type es null si la conversación ya fue eliminada. Son avisos:
el cliente vuelve a pedir la lista y los mensajes. Rol staff (F2-52): solo recibe los avisos de las
conversaciones que puede ver en ese momento; el resto se descarta en el servidor.
- Authentication: API key or dashboard session
- Scopes: None
Responses
| Status | Description |
|---|---|
200 | Stream abierto |
default | Error (RFC 9457) (Problem) |