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 fieldSendport fieldTransformation
messages[].iddata.wamidKept verbatim
messages[].fromdata.fromNormalised to E.164 with a leading +
metadata.display_phone_numberdata.toNormalised to E.164
metadata.phone_number_iddata.channelResolved to the Sendport channel id
messages[].timestampdata.created_atUnix seconds → RFC 3339 UTC
contacts[0].profile.namedata.contact.nameUnchanged
messages[].typedata.typeSame vocabulary
messages[].context.iddata.context.wamidQuote-reply target
statuses[].statusevent typesent/delivered/read/failed → message.*
statuses[].errors[0].codedata.error.meta_codeMeta's numeric code, kept for escalation
statuses[].errors[0]data.error.codeMapped to a Sendport error code
—data.conversation_idAdded — resolved from contact + channel
—data.session_expires_atAdded — 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

A retried 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.