On this page
We hate breaking changes too, but to make the APIs more ergonomic and allow for easier integrations and future functionality, we are making each message's ID the same everywhere: in API send responses, in Get Message, in webhooks, in the MCP and in the dashboard. Please check whether they affect your integration.
These changes take effect on 9 November 2026.
1. The id in API send responses
The id of each recipient in the response of Create Message, Create Template Message and Create Conversation Message becomes a bare UUID. Today it is the full Message-ID header, with angle brackets and a domain.
Today:
{
"object": "message",
"id": "<[email protected]>",
"recipient": { "email": "[email protected]" },
"status": "queued"
}From November 9th:
{
"object": "message",
"id": "0192f5a4-7b1c-7d3e-9a2b-4c5d6e7f8a9b",
"message_id_header": "<[email protected]>",
"recipient": { "email": "[email protected]" },
"status": "queued"
}The Message-ID header moves to a new field, message_id_header, in the same response.
What you may need to do
- If you match a send with its webhook events by comparing the response
idwith the webhook'sdata.message_id_header, comparemessage_id_headerinstead. - If you filter List Messages by
message_id_headerusing theidfrom a send response, usemessage_id_headerinstead. - Don't derive
idfrommessage_id_header, or the other way round: they are not always the same UUID. For example, all copies of one Create Conversation Message send (To, Cc and Bcc) will share onemessage_id_header, while each copy keeps its ownid.
What keeps working
- Get Message and Cancel Message accept both the new bare
idand the old<...@domain>form. IDs you have already stored keep working. - The Message-ID header of your emails does not change.
- If you repeat a request with the same
Idempotency-Key, you get the stored response of the first request. For up to 24 hours after November 9, a repeat of an earlier request can still return the old form, withoutmessage_id_header. - Older versions of our SDKs keep working:
idis still a string. New SDK versions addmessage_id_header.
2. The message ID in webhooks and the dashboard
Message webhooks (message.reception, message.delivered, message.transient_error, message.failed, message.bounced, message.suppressed, message.opened and message.clicked) carry the message's ID as data.id: the same id that the send response gave you and that Get Message takes. Today data.id is the an internal injection ID, a different UUID written without hyphens.
Today:
"data": {
"id": "407926766d2711f09b30960002cafe7c",
"message_id_header": "<[email protected]>"
}From November 9th:
"data": {
"id": "0192f5a4-7b1c-7d3e-9a2b-4c5d6e7f8a9b",
"message_id_header": "<[email protected]>"
}What you may need to do
- If you group a message's webhook events by
data.id, note that a message sent before November 9 can get events with both IDs: for example amessage.deliveredevent with the old ID and a latermessage.openedevent with the new one. This can happen for up to 30 days.data.message_id_headerdoes not change, so you can group by it across the switch. - If you check the shape of
data.id, it now has hyphens and is 36 characters long, not 32. - If you archive messages to your own S3 bucket, new archives use the message's ID in their folder and file names. Archives written before November 9 keep their paths.
- Campaign messages send the same
message.*events. Theirdata.idchanges to the same 36-character format, with hyphens. Use it to group a campaign message's events. Get Message does not return individual campaign messages (it only works for Transactional messages). - Inbound route webhooks (
message.routing) carry the same message ID asid, so it matches thedata.idof that message'smessage.receptionevent.
What keeps working
- The dashboard shows this same ID as the message's "Message ID". Dashboard links that you already shared keep working.
- An ID you stored from an older webhook still works with Get Message and Cancel Message.
If you have questions, please reach out via [email protected] or on our Chat server.