What changes in the API
Endpoint, webhook envelope and behaviour changes, in chronological order. New fields are additive: nothing you already receive goes away without being announced here.
Addition is compatible with what you already do. Fix changes what you receive, towards what was already documented. Needs attention changes behaviour for anyone who does nothing.
- Needs attentionChatwootWhatsApp Oficial
24-hour window: free-form messages outside it are no longer sent
On the official account, 24h after the customer's last message, free text is no longer attempted: it either goes out as an approved template or becomes a notice in the conversation.
Meta refuses any free-form message outside the 24-hour window. Until now the attempt left here, was refused there, and the error stayed in the log only: the agent saw the message as sent.
What changes:
- If the text the agent wrote matches an approved template, it goes out as a template and the conversation reopens. Variables are extracted from the text itself, so the agent can edit the template before sending.
- If it does not match, nothing is sent: a private note explains why and the message is marked as failed.
POST /chatwoot/templates/syncpublishes approved templates as canned responses (shortcuttpl_<name>), which is how the agent fires one without leaving the panel.
When we do not know when the customer last wrote, the send proceeds as usual — Meta remains the authority.
- FixChatwoot
Chatwoot: the account is now found through the inbox
Using a single Access Token across several Chatwoot accounts caused messages sent from the phone to be written to the wrong account — or rejected with 404.
The
account_idwas resolved from the Access Token profile, which returns the user's active account — unrelated to the account that owns the configured inbox. For an automation user serving dozens of accounts with one token, that pointed at the wrong account.The account is now found by looking for the configured
inbox_identifieramong the inboxes of every account the token can see. When the scan finds it nowhere, nothing is written: the log explains why, instead of the message landing in another customer's conversation.No action needed on your side. If your integration was working, it keeps working.
- FixChatwootWhatsAppWhatsApp Oficial
Chatwoot: five failures in the conversation mirror
Messages whose media could not be downloaded disappeared from the panel; delivered/read ticks never worked on unofficial WhatsApp; attachments beyond the first were dropped.
An audit of the integration found five problems, all fixed:
- The customer's message disappeared when the media could not be downloaded (file over 40 MB, download refused). Chatwoot rejects a message with no content and no attachment, and the conversation was left with no trace. A notice now takes the place of the silence.
- Delivered and read ticks never worked on unofficial WhatsApp: they depended on a webhook setting that starts disabled. The receipt now goes straight to Chatwoot.
- A contact identified only by LID became an invented phone number, and the agent's reply reached no one.
- From the second attachment on, nothing was sent when the agent attached several files. And every document arrived as
file.pdfinstead of its real name. - A send refused by the provider still looked delivered in the panel; it now shows as failed.
- FixAPIWhatsApp Oficial
Webhook diagnostics: the statistics route now talks about your instance
GET /instance/webhook/statistics answered 500 on official accounts and, when it did answer, returned the server queue counters — identical for every instance. It now returns the real result of each POST for your instance.
The route had two defects stacked on top of each other.
On an official (Cloud API) account it answered 500: internally it went through the QR Code session, which an official account does not have. The instance whose webhook causes the most trouble was precisely the one that could not be diagnosed. On a QR Code instance not yet loaded on the server, "not found" also came out as a 500.
And when it did answer, the numbers were the process queue counters — global, shared by every instance on that server. They answered "is the queue backed up?", never "did my instance's webhook go out, and what did my server answer?". The only answer to that lived in the server log, out of your reach.
The response is now about your instance:
json{ "status": 200, "data": { "official": true, "webhook": { "allowNumber": "all", "webhookFormat": "meta", "configured": ["webhookMessage", "webhookMessageFromMe"], "anyUrl": true }, "delivery": { "queued": 118, "attempts": 118, "ok": 117, "clientError": 0, "failed": 1, "avgMs": 143, "last": { "ts": "2026-10-14T22:34:01.020Z", "status": 200, "ms": 131, "outcome": "ok" }, "recent": [] }, "queue": { "waiting": 0, "active": 0, "failed": 0, "scope": "worker-global" } } }How to read it:
anyUrl: false— no channel has a URL. With no URL there is nothing to deliver: the event only goes out over the socket. This is the number one cause of "I receive nothing", and it was what forced you to open a log to discover the obvious.configuredlists which channels actually have a URL.okis high and still "nothing arrives" — the POST went out and your server answered 2xx. The event was lost in your processing, not in transport. This was the case that left no trace at all.clientError— your server refused with a 4xx. We do not retry 4xx by design: a 404 or 422 does not improve by repeating.failed— timeout, 5xx or 429. These we do retry: 5 attempts with exponential backoff. Our delivery timeout is 8 seconds.queuedfar aboveattempts— the queue stalled on our side. The problem is not your server.queueis still in the response, now labelledscope: "worker-global": these are process-wide numbers, shared with other instances. To judge yours, usedelivery.
Fine print: the counters start at this release, there is no retroactive history. They are kept for 7 days, and
recentcarries the last 20 deliveries with status, duration and error. Works on official (Cloud API) and QR Code accounts.In the same batch,
/instance/webhook/errorwas removed from the documentation — it was listed there and never existed in the API. - FixMessenger
Messenger: reactions now work, and stickers no longer arrive as plain images
The reaction endpoint sent a fixed value instead of the chosen emoji, and every attempt was refused by Meta. Incoming reactions and stickers were not identified either.
- Sending a reaction (
/message/reactionwithprovider: messenger) discarded the emoji and sent a fixed value: Meta answered100/2018468every time. It now sends the emoji you pass. The accepted format is the UTF-8 emoji (👍,❤️); text names (like) are refused upfront, with an explanation. Removing is stilltext: "". - Incoming reactions were not delivered: the event was dropped before reaching your webhook. They now arrive as a
type: "reaction"message, withreaction.message_id,reaction.emojiandreaction.action(reactorunreact; on removal the emoji is empty). - Stickers arrived as ordinary images. Messenger delivers them as an attachment that calls itself
imagewithsticker_idin the payload; they now arrive astype: "sticker"with that id. The animated file is not exposed by Meta in the webhook — the served URL is the static version. - Contact picture: when the Graph refuses the profile, the reason is now logged, instead of the contact silently staying without an avatar.
- Sending a reaction (
- AdditionChatwoot
Chatwoot: native quoting, voice notes and real timestamps
With an Access Token configured, the panel conversation gets a real reply bubble, a voice player and the time the message actually existed on the channel.
The inbox public API accepts text and attachments only. With an Access Token, the customer's message is now created through the Application API, which unlocks three things:
- Native quoting: the reply shows in the Chatwoot bubble instead of a quoted snippet inside the text.
- Voice notes with their own player, instead of a generic audio attachment.
- Real timestamps: a backlog redelivered after a dropped connection no longer piles up at delivery time.
Without an Access Token nothing changes. Quotes pointing at messages older than this release show without the bubble, because they have no channel id stored.
- AdditionChatwootWhatsAppWhatsApp Oficial
Chatwoot: typing indicator both ways, and blue ticks when you reply
The customer now sees when the agent is typing, the agent sees when the customer types, and replying from the panel marks the conversation as read on WhatsApp.
Three signals that existed on both ends and never met:
- Agent typing → customer sees it. On unofficial WhatsApp it is presence; on the official account the indicator rides along with mark-as-read, as the Cloud API requires. "Stopped typing" does not exist on the official account: there the indicator clears itself after about 25 seconds.
- Customer typing → agent sees it in the panel. The indicator never opens a new conversation.
- Replying from the panel marks as read on the channel, so the customer stops staring at two grey ticks while already being served.
- AdditionMessengerAPI
Messenger webhook: sequence number and timestamps
Every event now carries a wame block with a per-conversation sequence number and three timestamps, to rebuild ordering and measure where the time went.
Webhook delivery is parallel: the order requests reach your server is the order each one completes, not the order events came in. Two messages sent in sequence can arrive swapped.
Instead of serialising delivery, Messenger events now carry:
json"wame": { "seq": 42, "event_ms": 1760000000000, "received_at_ms": 1760000000420, "dispatched_at_ms": 1760000000437 }seqis a per-conversation counter, assigned in the order we received the event from Meta. Sorting by it rebuilds the sequence, even when timestamps tie.event_msis the event time according to Meta, in milliseconds;received_at_ms, when it entered here;dispatched_at_ms, when it left. First to second is Meta's latency; second to third is ours.
The block is additive: no existing field changed. For now, Messenger events only.
In the same release,
GET /messenger/webhook-fieldsshows which webhook fields your Page subscribes to, andPOSTon the same path subscribes the missing ones — that is what explains delivery receipts, read receipts or reactions that never arrive.