Payload mapping▾
Payload mapping
How Meta's raw Cloud API payloads are flattened into the Sendport event envelope.
Meta delivers inbound messages nested under entry[].changes[].value, with unix timestamps, digits-only phone numbers and status updates batched into arrays. Sendport flattens all of that into one event envelope before it reaches you. This page documents the transformation, so you can reason about anything that looks surprising.
Inbound message
{
"entry": [{
"changes": [{
"value": {
"messaging_product": "whatsapp",
"metadata": { "display_phone_number": "15550100200", "phone_number_id": "109876543210" },
"contacts": [{ "profile": { "name": "Nadia Haddad" }, "wa_id": "201005550123" }],
"messages": [{
"from": "201005550123",
"id": "wamid.SEJnTTIwMTAwNTU1MDEyM0ZRSUFFUmdT",
"timestamp": "1757151662",
"type": "text",
"text": { "body": "Is my order ready?" }
}]
},
"field": "messages"
}]
}]
}| Meta field | Sendport field | Transformation |
|---|---|---|
messages[].id | data.wamid | Kept verbatim |
messages[].from | data.from | Normalised to E.164 with a leading + |
metadata.display_phone_number | data.to | Normalised to E.164 |
metadata.phone_number_id | data.channel | Resolved to the Sendport channel id |
messages[].timestamp | data.created_at | Unix seconds → RFC 3339 UTC |
contacts[0].profile.name | data.contact.name | Unchanged |
messages[].type | data.type | Same vocabulary |
messages[].context.id | data.context.wamid | Quote-reply target |
statuses[].status | event type | sent/delivered/read/failed → message.* |
statuses[].errors[0].code | data.error.meta_code | Meta's numeric code, kept for escalation |
statuses[].errors[0] | data.error.code | Mapped to a Sendport error code |
| — | data.conversation_id | Added — resolved from contact + channel |
| — | data.session_expires_at | Added — computed from the last inbound message |
Batching and fan-out
Meta batches several status updates into one POST. Sendport fans them out: one Sendport event per status, each with its own evt_… id, delivered and retried independently. A single slow handler therefore cannot stall the rest of a batch.
Order is not guaranteed
message.delivered can land after its message.read. Treat status as a one-way ladder — accepted → sent → delivered → read — and never downgrade a message on a late event.Media
Meta sends inbound media as an id, never as bytes or a URL. Sendport keeps that shape — data.image.id — and you exchange it for a five-minute signed URL with retrieve media. Fetch within 30 days; after that Meta expires the object and it is unrecoverable.