Resend a Failed Message
FAILED or CANCELED again, without you having to keep the original payload.
This creates a new message with its own id and correlationId. The original row is left exactly as it is — its status, its timestamps and its provider message id all stay readable, so the two attempts remain distinguishable afterwards.
The resend counts against your plan quota, the same as any send. It is a new message, not a retry of the old one.
Finding what failed — it differs by connection type
⛔ An official (Meta Cloud API) number receives no canonicalmessage.* event at all. Not message.failed, not message.sent — none of the family, whatever the cause of the failure. On those numbers your webhook receives Meta’s native envelope, so a failure arrives as value.statuses[].status: "failed" with an errors[] array.
On an unofficial number nothing changes: message.failed is dispatched as before.
⇒ The discovery path that works for both connection types is GET /v1/messages?status=FAILED. The message row reaches FAILED either way — including for a failure Meta itself reported — so the list and this endpoint agree regardless of how the number is connected.
Headers
Requires the
messages:resend permission — ADMIN and OWNER. This is deliberately higher than messages:send: a resend spends quota on a send you already paid for once, and it can deliver twice.
Path parameter
Body
None. This endpoint accepts no body fields: the message comes from the URL and the content comes from the original send. Any field you send is refused with400 UNKNOWN_FIELDS, naming it. There is no whatsappInstanceId override here — a resend always leaves on the number the key points at.
Example
Response (202)
originalMessageId is the message you resent. Everything else is the new message, in the same shape POST /v1/messages/send returns — including flowToken, when the template carries a Flow button. The resend mints a new flowToken, so the answer that comes back can be attributed to this attempt and not to the first one.
Common errors
Plus everything
POST /v1/messages/send can answer, unchanged: a resend refused for no quota reads exactly like a send refused for no quota.
Why SENT and QUEUED are refused
SENT does not mean settled. It means an acknowledgement arrived; the delivery may still land, and the platform is already watching that message at 30 and 60 minutes and will re-dispatch it itself if it never reached the provider. Resending on top of that gives one message two senders.
QUEUED is still being handled by the worker and the reconciler. In both cases the answer names the current status in details.status, so you can decide what to do without a second call.