Skip to content

Reading

Every read is scoped to your workspace and takes the scope named under it.

GET /messages/whatsapp — scope MESSAGES_READ

Query Notes
page from 1. Default 1
perPage default 20, maximum 100
direction INBOUND | OUTBOUND
status QUEUED | SENT | DELIVERED | READ | FAILED | RECEIVED
contact a number in any form; its digits are matched
from which of your numbers, in any form. A workspace with more than one holds more than one set of threads
Terminal window
curl "https://vaarto.com/api/v1/messages/whatsapp?direction=OUTBOUND&perPage=20" \
-H "Authorization: Bearer $VAARTO_API_KEY"

Each row carries id, direction, status, kind, text, caption, templateName, variables, metadata, providerMessageId, error, to, from, contactName, conversationId, and the timestamps createdAt, sentAt, deliveredAt, readAt, failedAt.

Attachments are described in the row too — hasMedia, mediaId, mediaMimeType and mediaFilename — so drawing a thread does not mean fetching every message again to find out which of them carry one. hasMedia is the field to branch on: a template can carry a media header while its kind reads TEMPLATE.

GET /messages/whatsapp/:id — scope MESSAGES_READ.

The same fields, plus idempotencyKey. The list already carries everything else, so this is for one message you hold the id of rather than a second call per row. 404 NOT_FOUND if the id is not one of yours.

Poll the log if you are not using webhooks.

Three endpoints so nothing has to be hard-coded. /templates and /numbers take CATALOG_READ, which any send scope already includes — a sender needs to know what it may send. /conversations takes CONVERSATIONS_READ, kept separate because a thread list is every contact name and number you hold.

A template your code keeps a copy of can be rejected or paused by Meta without your code knowing. Asking is how it finds out.

Terminal window
# Approved templates, and the variables each one needs.
curl "https://vaarto.com/api/v1/templates?status=APPROVED" \
-H "Authorization: Bearer $VAARTO_API_KEY"
# Numbers you may send from. isDefault is used when you omit "from".
curl "https://vaarto.com/api/v1/numbers" \
-H "Authorization: Bearer $VAARTO_API_KEY"
# Threads, each carrying windowOpen — whether free text would arrive now.
curl "https://vaarto.com/api/v1/conversations?perPage=20" \
-H "Authorization: Bearer $VAARTO_API_KEY"
Endpoint Query Returns
GET /templates status (default APPROVED), language name, language, category, status, bodyText, variables, variableCount, parameterFormat, rejectedReason
GET /numbers — displayNumber, verifiedName, status, isDefault, qualityRating
GET /conversations page, perPage, contact, from id, contactWaId, contactName, from, lastInboundAt, lastOutboundAt, lastMessageAt, lastMessageText, unreadCount, windowOpen

Only a number with status: "REGISTERED" can send.

GET /calls — scope CALLS_READ. Takes page, perPage, direction (INBOUND | OUTBOUND), status (RINGING, ACCEPTED, REJECTED, MISSED, COMPLETED, FAILED) and from, which narrows to one of your own numbers — the same number each row reports as on. Each row has id, direction, status, with, on, conversationId, startedAt, endedAt, durationSeconds, createdAt.

This endpoint is the log. Acting on a call — answering it, declining it, placing one — is a separate scope (CALLS_WRITE) and a separate guide, because it means exchanging an SDP offer and carrying audio over DTLS-SRTP rather than making one HTTP request. See calls. To act on a call as it rings rather than afterwards, subscribe to call.status.