WhatsApp-berichten ontvangen
Inkomende berichten komen op dezelfde resource als uitgaande berichten terecht, zonder een apart inbox-endpoint om te pollen. Lees ze via de berichtenlijst in het dashboard, of via de API met GET /v1/whatsapp/messages/{id} nadat je de lijst op inkomend hebt gefilterd.
Elk inkomend bericht verlengt het klantenservicevenster tot 24 uur na de eigen timestamp van dat bericht, en dat is wat een vrije reply van jou bezorgbaar maakt. Een bericht dat Bird laat bereikt, draagt daarom het venster mee dat het contact daadwerkelijk heeft verleend, en een latere deadline die al geregistreerd is wordt nooit verkort.
Wat een inkomend bericht bevat
Elk inkomend bericht deelt één envelope: een id, direction: "inbound", het contact in from, je eigen nummer in to, een status van received, en een created_at. Er staat precies één contentveld naast, dat aangeeft wat het contact heeft verstuurd:
{
"id": "wam_01kya19eknftrs2s6p82asmvnh",
"direction": "inbound",
"from": { "phone_number": "+14155550100" },
"to": { "phone_number": "+13124495569" },
"status": "received",
"text": { "body": "Is my order out for delivery yet?" },
"created_at": "2026-08-25T09:04:11Z"
}from benoemt het contact op basis van de identiteit die WhatsApp rapporteert: een E.164 phone_number, een bsuid, of beide, plus de username en display_name die ze publiceren. Een contact dat een WhatsApp-gebruikersnaam heeft aangenomen kan je bereiken zonder telefoonnummer; zie business-scoped user IDs voor wat je opslaat en hoe je een nummer opvraagt.
Eén van deze contentvelden, een arm, bevat wat ze hebben verstuurd, en er is precies één arm ingesteld per bericht. Elk heeft een eigen pagina, met de leesstructuur, de whatsapp.received-payload, en waar je op moet letten:
| Veld | Wat een inkomend bericht bevat |
|---|---|
text | body, het bericht dat het contact heeft getypt |
image | Een id, url, mime_type, en eventuele caption |
video | Dezelfde mediavelden, plus eventuele caption |
audio | Dezelfde mediavelden, plus voice bij een spraakbericht; er is geen bijschrift |
sticker | Dezelfde mediavelden, plus animated |
document | Dezelfde mediavelden, plus eventuele filename en caption |
location | latitude en longitude, en soms name, address, of een url |
contact_cards | Een of meer contactkaarten die het contact heeft gedeeld |
interactive_reply | De slug en text van de knop of rij waarop het contact heeft getikt |
unsupported | Het WhatsApp-contenttype dat de API niet modelleert, zoals een bestelling |
Twee tikken komen binnen op een arm die je misschien niet verwacht. Een locatieverzoek wordt beantwoord als een gewoon inkomend location, en een contactinfoverzoek wordt beantwoord als contact_cards, dus een integratie die alleen interactive_reply bewaakt voor een tik mist beide.
Inkomende media ophalen
Een inkomend image, video, audio, sticker of document komt binnen als een verwijzing naar een bestand dat Bird heeft opgeslagen, in plaats van als het bestand zelf:
{
"image": {
"id": "waf_01kyb2m4xq7whs0d8n3prv6tez",
"url": "https://platform.bird.com/v1/whatsapp/messages/wam_01kya19eknftrs2s6p82asmvnh/media/waf_01kyb2m4xq7whs0d8n3prv6tez",
"mime_type": "image/jpeg",
"caption": "Is this the right part?"
}
}Haal de bytes op met de mediamethode van het kanaal, door de message id en de media id mee te geven:
const media = await bird.whatsapp.messages.media(
"wam_01kya19eknftrs2s6p82asmvnh",
"waf_01kyb2m4xq7whs0d8n3prv6tez",
);
console.log(media.contentType, media.contentLength);media = client.whatsapp.messages.media(
"wam_01kya19eknftrs2s6p82asmvnh", "waf_01kyb2m4xq7whs0d8n3prv6tez"
)
print(media.content_type, media.content_length)media, err := client.Whatsapp.Messages.Media(context.Background(),
"wam_01kya19eknftrs2s6p82asmvnh", "waf_01kyb2m4xq7whs0d8n3prv6tez")
if err != nil {
log.Fatal(err)
}
fmt.Println(media.ContentType, media.ContentLength)$media = $bird->whatsapp->messages->media('wam_01kya19eknftrs2s6p82asmvnh', 'waf_01kyb2m4xq7whs0d8n3prv6tez');
file_put_contents('photo.jpg', $media->data);
echo $media->contentType, ' ', $media->contentLength;bird whatsapp media <message-id> <media-id>curl -L -X GET "https://{region}.platform.bird.com/v1/whatsapp/messages/{message_id}/media/{media_id}" \
-H "Authorization: Bearer $TOKEN"Je krijgt de bytes terug met het mime_type-opslagtype dat ervoor is opgegeven. Een id die Bird niet herkent geeft 404 terug, en een uitgaand bericht heeft geen opgeslagen media om te serveren.
Een bericht is 30 dagen na ontvangst terug te lezen, en de media ervan leeft nooit langer. Dit endpoint leest het bericht voordat het het bestand serveert, dus zodra dat venster voorbij is antwoorden beide met 404. Sla elk bestand dat je langer nodig hebt op zolang het bericht nog leesbaar is. Het enige geval dat eerder eindigt is wanneer de opgeslagen bytes verdwijnen vóór het venster afloopt: de fetch antwoordt met 410 E15021, en het bericht is nog steeds terug te lezen met de mime_type en caption van de media.
Onder de motorkap antwoordt dit endpoint met 302 met een presigned URL die 15 minuten geldig is. De SDK's en de CLI nemen die stap voor je. Als je het direct aanroept, draagt de presigned URL zijn eigen credential, dus het doorgestuurde verzoek mag niet ook je Authorization-header meesturen. Beide versturen mislukt. curl -L verwijdert de header bij een cross-host redirect automatisch; een client die headers letterlijk doorstuurt moet de Location als een apart, niet-geauthenticeerd verzoek ophalen.
Geciteerde antwoorden
in_reply_to_message_id benoemt het bericht waar een inkomend bericht op antwoordt, wanneer WhatsApp het als reply markeert:
{
"id": "wam_01kyb2m4xq7whs0d8n3prv6tez",
"direction": "inbound",
"in_reply_to_message_id": "wam_01kya19eknftrs2s6p82asmvnh",
"text": { "body": "Yes, that one" }
}WhatsApp markeert niet elke reply, en een ongemarkeerde draagt helemaal geen ID. Het veld wordt ook weggelaten wanneer het geciteerde bericht niet kan worden gekoppeld aan een bericht dat Bird bewaart: een bericht dat is verstuurd voordat deze werkruimte ze begon vast te leggen, of een bericht ouder dan de 15 dagen die Bird de eigen bericht-id's van WhatsApp bewaart. Een mis laat het veld weg in plaats van er een te rapporteren, wat hetzelfde leest als een reply die nergens op antwoordt.
Behandel het veld als een hint, niet als een sleutel. metadata op je eigen verzending helpt hier niet, omdat het op jouw bericht blijft staan en nooit meereist naar de reply van het contact, dus een integratie die moet weten bij welke vraag een antwoord hoort houdt zelf bij welke vraag ze het laatst aan dat contact heeft gesteld. Zie Een bericht citeren voor de uitgaande kant.
De webhook
Abonneer je op whatsapp.received om op een inkomend bericht te reageren zodra het binnenkomt, in plaats van de lijst te pollen. De payload bevat de content bovenop de event-envelope, dus een endpoint hoeft niet apart na te lezen:
{
"type": "whatsapp.received",
"timestamp": "2026-08-25T09:04:11.118Z",
"data": {
"whatsapp_id": "wam_01kyb2m4xq7whs0d8n3prv6tez",
"workspace_id": "ws_01ky7m235keycbnwyajabe1a6b",
"direction": "inbound",
"from": { "phone_number": "+14155550100", "display_name": "Alex Rivera" },
"to": { "phone_number": "+13124495569" },
"in_reply_to_message_id": "wam_01kya19eknftrs2s6p82asmvnh",
"interactive_reply": {
"type": "button",
"button": { "slug": "cancel-booking", "text": "Cancel" }
},
"tags": null,
"metadata": null
}
}Zie WhatsApp-webhooks voor de volledige envelope en de rest van de eventlijst.
Volgende stappen
- Serviceberichten: de verzendzijde van dezelfde content-arms
- WhatsApp-berichten versturen: antwoorden binnen het servicevenster, en een bericht citeren
- WhatsApp-webhooks: elk event waarop je je kunt abonneren
- WhatsApp-log: het gesprek bekijken in het dashboard
- WhatsApp-groepsberichten ontvangen: de deelnemer, de groep, en het servicevenster van de groep
Gerelateerde bronnen
Ga verder met de documentatie, handleidingen en voorbeelden voor dit onderwerp.