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.
# 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 }'// Blue ticks.const readRes = await fetch("https://vaarto.com/api/v1/messages/whatsapp/MESSAGE_ID/read", { method: "POST", headers: { Authorization: `Bearer ${process.env.VAARTO_API_KEY}` },});
const { data: read } = await readRes.json();
// Blue ticks and a "typing…" bubble, for while you compose the reply.const readTypingRes = await fetch("https://vaarto.com/api/v1/messages/whatsapp/MESSAGE_ID/read", { method: "POST", headers: { Authorization: `Bearer ${process.env.VAARTO_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ typing: true, }),});
const { data: readTyping } = await readTypingRes.json();import os, requests
# Blue ticks.read_res = requests.post( "https://vaarto.com/api/v1/messages/whatsapp/MESSAGE_ID/read", headers={"Authorization": f"Bearer {os.environ['VAARTO_API_KEY']}"}, timeout=10,)
read = read_res.json()["data"]
# Blue ticks and a "typing…" bubble, for while you compose the reply.readTyping_res = requests.post( "https://vaarto.com/api/v1/messages/whatsapp/MESSAGE_ID/read", headers={"Authorization": f"Bearer {os.environ['VAARTO_API_KEY']}"}, json={ "typing": True, }, timeout=10,)
readTyping = readTyping_res.json()["data"]// Blue ticks.$read = Http::withToken(env('VAARTO_API_KEY')) ->post('https://vaarto.com/api/v1/messages/whatsapp/MESSAGE_ID/read') ->json('data');
// Blue ticks and a "typing…" bubble, for while you compose the reply.$readTyping = Http::withToken(env('VAARTO_API_KEY')) ->post('https://vaarto.com/api/v1/messages/whatsapp/MESSAGE_ID/read', [ 'typing' => true, ]) ->json('data');The typing bubble
Section titled “The typing bubble”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 response
Section titled “The response”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.
Acknowledge, then reply
Section titled “Acknowledge, then reply”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 }), });}A whole thread at once
Section titled “A whole thread at once”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.