Asignar y cerrar un chat de WhatsApp por API: PATCH /conversations en Waspy
PATCH /api/v2/conversations/:id asigna el chat (assignedTo) o lo cierra (status closed). El servidor solo acepta open y closed. pending y resolved dan 400. Marcar leído y el typing no asignan.
Respuesta corta: para asignar o cerrar un chat hacés PATCH https://api.waspytech.com/api/v2/conversations/:id con scope conversations:write. status es open o closed. assignedTo es el UUID de un usuario de esa cuenta, o null para soltarlo. pending y resolved no existen en el servidor: responden 400 VALIDATION_ERROR.
La referencia está en conversations. Donde esa página y el código no coinciden, manda el handler de apps/api/src/routes/v2/conversations.ts. El listado, el leído y el «escribiendo…» están en conversaciones por API. Cómo llegar al id desde un teléfono está en abrir el chat desde el CRM. La clave pública entra en Scale y Enterprise: Planes y la API.
Donde la doc y el servidor no dicen lo mismo
| Tema | Página de docs | Lo que hace el servidor |
|---|---|---|
status del PATCH y del listado | open, pending, resolved, closed | Solo open y closed. Otro valor: 400 VALIDATION_ERROR |
Filtro assignedTo en el GET | No está en la página de v2 | Tampoco en el handler. El query se ignora |
| Notas | No las documenta en esta página | No hay rutas /notes en /api/v2 |
Default de limit en el listado | 50 | 20 (máximo 100) |
status de la base también es solo open o closed. El schema zod (ConversationUpdateBody y ConversationListQuery) frena el body y el query antes del handler.
Asignar y cerrar
Los dos campos son opcionales. Podés mandar uno solo.
curl -sS -X PATCH "https://api.waspytech.com/api/v2/conversations/CONV_ID" \
-H "Authorization: Bearer $WASPY_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "status": "open", "assignedTo": "USER_UUID" }'
Para cerrar y soltar al mismo tiempo:
curl -sS -X PATCH "https://api.waspytech.com/api/v2/conversations/CONV_ID" \
-H "Authorization: Bearer $WASPY_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "status": "closed", "assignedTo": null }'
El 200 trae data con el chat (id, status, assignedTo, contactId, phoneNumberId, lastMessageAt, serviceWindowExpiresAt, entre otros) y meta.requestId.
| Respuesta | Cuándo |
|---|---|
| 200 | Se guardó status, assignedTo, o los dos |
400 VALIDATION_ERROR | status no es open ni closed, assignedTo no es un UUID, o ese usuario no está activo en la cuenta |
404 CONVERSATION_NOT_FOUND | El id no es de esta cuenta, o el token no puede ver ni gestionar ese chat |
| 403 | A la clave le falta conversations:write |
assignedTo tiene que ser un usuario de esa cuenta y no dado de baja. No hay un GET /api/v2/users para listarlos: el UUID lo tenés del panel o de tu propio directorio. Un id de otra cuenta responde assignedTo user not found in this tenant.
Cerrar no borra el hilo y no le escribe al cliente. Si esa persona vuelve a mandar un WhatsApp, el mensaje entra igual. Para contestarle, el pedido es POST /api/v2/messages, como está en la API para desarrolladores.
El listado no asigna, y tampoco filtra por responsable
GET /api/v2/conversations?status=open (scope conversations:read) devuelve cada chat con su assignedTo ya puesto. El filtro status acepta open o closed. ?status=resolved es el mismo 400. ?assignedTo= no filtra: el handler no lee ese parámetro.
curl -sS "https://api.waspytech.com/api/v2/conversations?status=open&limit=20" \
-H "Authorization: Bearer $WASPY_API_KEY"
Cada ítem trae assignedTo (null si nadie lo tiene). Para saber quién lo tiene después del PATCH, leé GET /api/v2/conversations/:id. No esperes un webhook: los eventos públicos son message.received, message.status_changed, conversation.created y contact.created. No hay conversation.updated. El catálogo está en eventos.
Leer y «escribiendo…» no cambian al responsable
| Pedido | Scope | Qué hace | Qué no hace |
|---|---|---|---|
PATCH /conversations/:id | conversations:write | status y assignedTo | No manda un WhatsApp |
POST /conversations/:id/read | conversations:write | No leído en cero y doble tilde | No asigna ni cierra |
POST /conversations/:id/typing | conversations:write | «Escribiendo…» | No asigna ni cierra |
GET /conversations/:id/messages | messages:read | Lista los mensajes | No cambia el chat |
Idempotency-Key es de POST /messages, no de este PATCH.
Del webhook al vendedor
message.received ya trae data.conversationId. Si lo tenés, el PATCH va directo a ese id. No hace falta volver a buscar el teléfono.
Si el CRM solo guardó el número (un ticket que nació afuera, sin el UUID), primero resolvés:
curl -sS "https://api.waspytech.com/api/v2/conversations/resolve?phone=5491126032641&channel=whatsapp" \
-H "Authorization: Bearer $WASPY_API_KEY"
Scope: conversations:read. Si hay chat, data.conversationId es el UUID del PATCH. Si no hay, es null: este GET no crea la conversación. GET /conversations/by-phone/:phone es el atajo de solo WhatsApp: 200 con data: null cuando no hay match, y no trae assignedTo. El contrato de las dos lecturas está en resolver por teléfono.
Orden típico de un CRM:
- Llega
message.received(o resolvés el teléfono). PATCHcon el UUID del vendedor del ticket ystatus: "open".- Si tu sistema deja una nota para el equipo, no es un POST de la v2. Es la herramienta MCP
add_note(conversationIdybody, tope 5000 caracteres en el schema de la herramienta, scopeconversations:write). La ve el equipo en el panel y no sale al cliente. Está en conectar MCP.
Qué no existe
status: "pending"o"resolved", ni en el PATCH ni en el filtro del listado.POST /api/v2/conversations/:id/notes, ni un GET de notas en la v2.- Un webhook público de “se asignó” o “se cerró”.
GET /api/v2/userspara descubrir el UUID del vendedor.- Un PATCH que impida que el cliente vuelva a escribir.
El PATCH no es un mensaje de Meta. No gasta el cupo de 1.000 mensajes de servicio por número y por mes. Lo que cobra Meta es el texto o la plantilla que después mandás. En Argentina, desde el 1 de octubre de 2026, marketing alrededor de ARS 89,56 (US$0,0618) y, pasado ese cupo, el servicio alrededor de ARS 37,68 (US$0,026). La rate card es la fuente. Los planes de Waspy arrancan en US$70 por mes, sin un recargo sobre Meta: Planes. Diez días sin tarjeta, en el registro.
