25 July 2026
Read receipts, and messageId becomes WASync's stable id.
Read receipts — POST /messages/read gives your customers blue ticks
New feature.
If your operators read WhatsApp inside your own CRM, nobody ever opens the chat
in WhatsApp — the customer's messages stay on double grey ticks and the
conversation feels ignored. POST /messages/read sends the WhatsApp read
receipt for you.
Body: { connectionId, phone?, messageId? } — at least one of phone /
messageId (the latter is WASync's cuid of an incoming message, not
waMessageId). It uses the existing whatsapp.send scope, because marking read
transmits a receipt to the customer's device — no re-consent needed.
Idempotent by nature: no idempotencyKey, no rate limit, safe to retry.
Response { ok: true, marked }, where marked counts the stored inbound
messages newly flagged as read.
messageId is now WASync's stable id — WhatsApp's id moves to waMessageId
Breaking change · Improvement.
POST /messages previously returned WhatsApp's own message id
(true_…@c.us_3EB0…) as messageId, while GET /messages and the
message.received / message.status webhooks used WASync's id — so status
events could never be matched back to a send.
They are now the SAME id everywhere: messageId (POST) = id (GET) =
message.id (webhooks), a WASync cuid. WhatsApp's own id is still returned,
alongside, as waMessageId (REST), wa_id (webhooks) and wa_message_id
(media send) — use it for support tickets, never as a join key (it can be
null). GET /messages rows now also carry waMessageId, so you can map both
ways.
Action required if you stored the value of messageId to match webhooks: it
now changes format — join on the new value, and read waMessageId if you
specifically needed the WhatsApp id.
One new edge case: if a send reaches WhatsApp but cannot be persisted, the 200
body is { messageId: null, waMessageId, status, persisted: false } — the
message was delivered, so do not retry it.