Skip to content

Mark as read

POST /messages/whatsapp/:id/read — scope CONVERSATIONS_WRITE.

Pass the messageId that message.received gave you. WhatsApp marks everything up to that message read, so a backlog needs one call rather than one per message.

Terminal window
# Blue ticks.
curl -X POST "https://vaarto.com/api/v1/messages/whatsapp/MESSAGE_ID/read" \
-H "Authorization: Bearer $VAARTO_API_KEY"
# Blue ticks and a "typing…" bubble, for while you compose the reply.
curl -X POST "https://vaarto.com/api/v1/messages/whatsapp/MESSAGE_ID/read" \
-H "Authorization: Bearer $VAARTO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"typing": true
}'

Meta accepts typing only alongside a read receipt — there is no endpoint for it on its own. It clears after 25 seconds or when your reply lands, whichever comes first. Ask for it when you are about to compose, not when you queue the job.

The reply carries receipt: true when Meta accepted the acknowledgement, and false when the number’s credentials are missing or Meta refused it. Either way the thread stops counting as unread in the console.

Acknowledging costs one call to Meta, so it is rate-limited like a send rather than like a read.

// Inside your "message.received" handler. data.messageId is the one it gives you.
async function onInbound(data) {
const auth = { Authorization: `Bearer ${process.env.VAARTO_API_KEY}` };
// 1. Tick it. The customer sees two blue ticks straight away, rather than
// waiting however long your reply takes to compose.
await fetch(
"https://vaarto.com/api/v1/messages/whatsapp/" + data.messageId + "/read",
{ method: "POST", headers: auth },
);
// 2. Now take your time.
const answer = await decideWhatToSay(data.text);
// 3. Reply on the same thread.
await fetch("https://vaarto.com/api/v1/messages/whatsapp", {
method: "POST",
headers: { ...auth, "Content-Type": "application/json" },
body: JSON.stringify({ to: data.from, text: answer }),
});
}

To catch up a conversation when you kept the conversation id but not the message ids, POST /conversations/:id/read acknowledges the last inbound message in it, under the same scope.