How-to guide

Pagination

Page through lists of messages, conversations, events and logs with cursors, without missing or repeating items.

What you'll build

A loop that reads a whole list one page at a time, and a poller that only fetches what is new.

Prerequisites

  • A secret API key with the scope of the list you read (messages:read, events:read, webhooks:manage or logs:read). See Authentication and API keys.
  • curl, Node.js 18 or later with @voicelayer/api, or Python 3.9 or later with voicelayer.

How cursors work

Lists return a page and a cursor. Pass the cursor back to get the next page. Cursors are opaque: store them as they are, never build or change one. A cursor the API did not issue is refused with 400.

ListSendRead from the answerPage sizeOrder
GET /v1/messages, GET /v1/conversations/{id}/messagesafter (newer), before (older, or end for the newest page)nextCursor, prevCursor, hasMoredefault 50, max 100oldest first
GET /v1/conversationscursornextCursor (null at the end)default 50, max 100most recently active first
GET /v1/eventsaftercursor, hasMoredefault 100, max 500oldest first
GET /v1/request-logsafter (older), since (newer)nextCursor, latestCursordefault 50, max 200newest first
GET /v1/webhooks/deliveriessince, until (times)—default 50, max 200newest first

Set the page size with limit.

Step 1: Read the first page

curl "https://api.vlayers.ai/v1/conversations?controller=human&limit=20" \
  -H "Authorization: Bearer $VOICELAYER_API_KEY"
Response · 200
{
  "conversations": [
    {
      "id": "0192f5b0-7c3e-7a51-9b2d-4e8f1a6c3d21",
      "channel": "sms",
      "lineId": "0192f5a8-1a2b-7c3d-8e4f-5a6b7c8d9e01",
      "bindingId": null,
      "peer": {
        "address": "+15550123"
      },
      "controller": "api",
      "status": "open",
      "openedAt": "2026-10-20T15:04:05.000Z",
      "lastActivityAt": "2026-10-20T15:04:05.000Z",
      "test": true
    }
  ],
  "nextCursor": null
}

Step 2: Follow the cursor

Pass nextCursor back as cursor until it is null:

export NEXT_CURSOR=<the nextCursor you got>
curl "https://api.vlayers.ai/v1/conversations?cursor=$NEXT_CURSOR&limit=50" \
  -H "Authorization: Bearer $VOICELAYER_API_KEY"

Step 3: Show a chat history, newest first

For a conversation's messages, start with before=end for the newest page. Each page is still in time order (oldest first). Pass its prevCursor as before for the page before it; prevCursor is null when nothing older is left.

export CONVERSATION_ID=<the conversation's id>
curl "https://api.vlayers.ai/v1/conversations/$CONVERSATION_ID/messages?before=end&limit=20" \
  -H "Authorization: Bearer $VOICELAYER_API_KEY"

Step 4: Poll for what is new

Keep the last nextCursor (messages) or cursor (events) and pass it as after next time. On an empty page the cursor stays where it was, so a poller never loses its place. For events, add waitSec to wait for the next one instead of polling in a tight loop:

curl "https://api.vlayers.ai/v1/events?after=$CURSOR&waitSec=20" \
  -H "Authorization: Bearer $VOICELAYER_API_KEY"

Test it

With a test key, simulate a few messages with POST /v1/test/inbound (see Test mode), then page through GET /v1/messages with limit=1.

Common errors

ErrorStatusHow to fix
invalid_request400Read message and details.issues (the validation issues, by path), fix the request and send it again.
invalid_cursor400Pass back a cursor (pull) or an event id: line (stream) exactly as received, or leave it out to start afresh.
cursor_expired410Page through GET /v1/events from the same cursor (the pull reads the whole retention), then reconnect the stream with its last cursor.

Next steps

On this page