waiaconnect

Documentation / Essential

Mark a message as read (and show "typing…")

POST /v1/messages/read

Markdown

POST /v1/messages/read with {"messageId": "wamid…"}: the id of a message you RECEIVED (data.message.id of message.received). It goes in the body, not the URL (a URL ends up in access logs, and the id carries the contact's number). The contact sees the blue ticks; Meta also marks the earlier messages of that chat as read. Add "typing": true to also show "typing…": it disappears when you reply or after 25 seconds, whichever comes first.

It is not a send: no msg_…, no quota, it does not count as a message, and no Idempotency-Key (marking twice is harmless). It works the same on Cloud API and Coexistence numbers.

# messageId comes from a message.received event: data.message.id
curl -X POST https://api.waiaconnect.com/v1/messages/read \
  -H "Authorization: Bearer wc_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"messageId": "wamid.HBgN…AA==", "typing": true}'
# → { "messageId": "wamid.…", "status": "read", "typing": true, "typingSeconds": 25, "connectionId": "conn_…" }
const res = await fetch("https://api.waiaconnect.com/v1/messages/read", {
  method: "POST",
  headers: { "Authorization": "Bearer wc_live_YOUR_API_KEY", "Content-Type": "application/json" },
  body: JSON.stringify({
    messageId: event.data.message.id, // from message.received
    typing: true                      // omit to only mark as read
  })
});
console.log(res.status); // 200 = read (and "typing…" shown up to 25 s)
<?php
$ch = curl_init("https://api.waiaconnect.com/v1/messages/read");
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_POST => true,
  CURLOPT_HTTPHEADER => ["Authorization: Bearer wc_live_YOUR_API_KEY", "Content-Type: application/json"],
  CURLOPT_POSTFIELDS => json_encode([
    "messageId" => $event["data"]["message"]["id"], // from message.received
    "typing" => true,                               // omit to only mark as read
  ]),
]);
$res = json_decode(curl_exec($ch), true);
echo $res["status"]; // read
import requests
res = requests.post("https://api.waiaconnect.com/v1/messages/read",
  headers={"Authorization": "Bearer wc_live_YOUR_API_KEY"},
  json={"messageId": event["data"]["message"]["id"],  # from message.received
        "typing": True})                              # omit to only mark as read
print(res.json()["status"])  # read

Meta only accepts messages received in the last 30 days → MESSAGE_TOO_OLD. A msg_… id → INVALID_MESSAGE_ID; an id of ANOTHER account → INBOUND_MESSAGE_NOT_FOUND (we never confirm it exists).

Show "typing…" only when you are about to answer (Meta's guidance): a contact who sees it and gets nothing has a worse experience than one who sees nothing.