Paginación de la API de Waspy: page y cursor, y el error INVALID_CURSOR
Contactos y plantillas usan page y pageSize. Conversaciones, mensajes y el log de webhooks usan un cursor opaco. Si lo fabricás, la API responde 400 INVALID_CURSOR.
Respuesta corta: la API v2 no pagina todo igual. Contactos y plantillas usan page y pageSize. Conversaciones, mensajes y el log de webhooks usan un cursor que tenés que copiar de meta.cursor. Si lo inventás, GET /conversations, GET /conversations/:id/messages y GET /webhooks/:id/deliveries responden 400 INVALID_CURSOR.
La tabla está en paginación. La API está en Scale y Enterprise: Planes y la API.
Los dos modelos
| Recurso | Modelo | Parámetros | Orden |
|---|---|---|---|
GET /api/v2/contacts | page | page (default 1), pageSize (default 20, máx. 100) | Creados, más nuevos primero |
GET /api/v2/templates | page | Igual | Creados, más nuevos primero |
GET /api/v2/conversations | cursor | cursor, limit (default 20, máx. 100) | Más recientes primero |
GET /api/v2/contacts/:id/conversations | cursor | cursor, limit (default 20, máx. 100) | Más recientes primero |
GET /api/v2/conversations/:id/messages | cursor | cursor, limit (default 50, máx. 100) | Más antiguos primero |
GET /api/v2/webhooks/:id/deliveries | cursor | cursor, limit (default 50, máx. 100), status | Más nuevos primero |
meta de la página trae page, pageSize y total. meta del cursor trae cursor y hasMore. Los dos traen requestId.
Page: contactos y plantillas
curl -sS "https://api.waspytech.com/api/v2/contacts?page=2&pageSize=50" \
-H "Authorization: Bearer $WASPY_API_KEY"
Hay otra página si page * pageSize < total. pageSize fuera de 1–100 no pasa el schema. El mismo par va en plantillas, junto con status y phoneNumberId si los usás. Cómo leer approved está en listar plantillas.
Este modelo sirve cuando la lista es bastante estable y te importa el total (“página 3 de 142”). En un inbox que se mueve mientras paginás, el offset se saltea o repite filas. Por eso los chats no usan page.
Cursor: no lo armes
La primera llamada no manda cursor.
curl -sS "https://api.waspytech.com/api/v2/conversations?limit=20" \
-H "Authorization: Bearer $WASPY_API_KEY"
Si meta.hasMore es true, la siguiente es la misma URL con cursor igual al string que vino. No le saques caracteres, no le cambies la zona horaria, no reemplaces el id por el waMessageId.
Qué valida el servidor
En conversaciones, mensajes y deliveries el cursor pasa por un validador. Tiene que ser una fecha ISO que termine en Z (con o sin milisegundos), una barra vertical y un UUID. Si falta la barra, la fecha no matchea o el id no es un UUID, la respuesta es:
| Campo | Valor |
|---|---|
| HTTP | 400 |
code | INVALID_CURSOR |
message | Cursor format is invalid |
offset=40 o un id suelto caen en este 400.
La doc de paginación muestra un ejemplo con forma fecha|uuid para que se vea el compuesto. Eso no es una invitación a construirlo. El valor que tenés que mandar es el de meta.cursor, opaco. La nota de la doc dice lo mismo: no lo modifiques ni lo armes a mano.
GET /contacts/:id/conversations también rechaza un cursor sin las dos partes (INVALID_CURSOR). Igual: copiá el que devolvió la llamada anterior.
Orden, para no mezclar la UI
Las conversaciones y el log de webhooks van de lo nuevo a lo viejo. Los mensajes de un chat van de lo viejo a lo nuevo, que es como se dibuja la bandeja: el cursor pide el tramo más antiguo. Si lo usás al revés, el hilo se arma cabeza abajo.
status en deliveries es success, failed o all (el default). El alta del webhook, el test y el replay están en gestionar webhooks.
Un recorrido corto
GET /contacts?page=1&pageSize=100hasta quepage * pageSizealcance atotal.- Por cada contacto,
GET /contacts/:id/conversations?limit=20y seguímeta.cursormientrashasMoresea true. - Por cada conversación,
GET /conversations/:id/messages?limit=50del mismo modo. Scopemessages:read. - No persistás el cursor de ayer para “seguir desde ahí” mañana sin haberlo recibido en esa respuesta. Pedí la primera página de nuevo.
Scopes de lectura: contacts:read, templates:read, conversations:read, messages:read, webhooks:read.
Diez días de prueba, sin tarjeta, en el registro. La referencia de envío, que no pagina pero sí devuelve el id que después ves en el mensaje, está en messages.
