Skip to main content

Responses and Webhooks

When a contact submits a flow, WhatsApp sends the answers back as a message in the chat. SendSeven:

  1. Matches the answer to the session it belongs to, using the flow token from the send. The answers are always stored on the contact the flow was sent to.
  2. Stores the answers on the session and shows the reply in the conversation.
  3. Writes back mapped answers to the contact (field mapping).
  4. Notifies you: the whatsapp_flow.completed webhook, the waiting Send WhatsApp Flow node, and the whatsapp_flow.completed trigger.

Each session completes at most once. A redelivered answer from WhatsApp is ignored.

Flows that were built outside SendSeven (for example in WhatsApp Manager and imported) or sent before you used SendSeven still arrive. SendSeven creates an adopted session for them (sender_type: "adopted"), without write-back mappings.

Write-back result​

Every completion carries a writeback object:

{
"state": "partial",
"contact_fields": { "first_name": "Anna", "email": "[email protected]" },
"custom_fields": [
{ "custom_field_id": "3f1c…", "key": "kundennummer", "status": "ok" }
],
"tags": [{ "tag_id": "b7d2…", "status": "ok" }],
"subscriptions": [{ "list_id": "a9c4…", "status": "ok" }],
"unsubscriptions": [{ "list_id": "d2e8…", "status": "ok" }],
"skipped": [
{ "input": "email", "target": "contact_field", "field": "email", "reason": "owned_by_other_contact" }
],
"media": {
"invoice_upload": [
{ "file_name": "rechnung.pdf", "mime_type": "application/pdf", "stored": true, "file_size": 48213 }
]
}
}
stateMeaning
doneEverything that was mapped was written.
partialSome items were skipped or failed. Check skipped and the item statuses.
errorThe write-back failed. The answers are still stored and delivered.
skippedThere was no contact to write to.

Item statuses:

KeyStatuses
custom_fieldsok, not_found, not_editable, invalid (with reason)
tagsok, not_found, error
subscriptionsok, not_found, list_type_not_allowed, not_offered, error
unsubscriptionsok, skipped (with reason), error
skipped[].reasone.g. consent_required, owned_by_other_contact, invalid_email, invalid_date, invalid_language

Uploaded photos and documents are copied into SendSeven's storage and listed under media, keyed by input name.

Sessions API​

GET /whatsapp-flows/{id}/sessions lists every send of a flow with its answers, newest first. Scope: whatsapp_flows:read.

QueryNotes
page, page_sizePagination, page_size up to 100 (default 25).
statusFilter by session status (sent, completed, …).
contact_idOnly sessions of one contact.
curl "https://api.sendseven.com/api/v1/whatsapp-flows/0f9e8d7c-…/sessions?status=completed&page_size=50" \
-H "Authorization: Bearer s7_api_a1b2c3d4e5f6789012345678abcdef00"
{
"items": [
{
"id": "5b0e2c1a-…",
"definition_id": "0f9e8d7c-…",
"version_id": "a1b2c3d4-…",
"version_number": 3,
"contact_id": "c0ffee00-…",
"contact_name": "Anna Schmidt",
"conversation_id": "9a8b7c6d-…",
"channel_id": "8c1d0f5e-…",
"sender_type": "automation",
"status": "completed",
"last_screen": "CONTACT",
"answers": { "callback_time": "vormittags", "phone_ok": true, "customer_number": "••••" },
"writeback_result": { "state": "done", "contact_fields": { "first_name": "Anna" } },
"last_error": null,
"created_at": "2026-10-03T09:15:00Z",
"opened_at": "2026-10-03T09:16:10Z",
"completed_at": "2026-10-03T09:17:02Z",
"expires_at": "2026-10-04T09:15:00Z"
}
],
"pagination": { "page": 1, "page_size": 50, "total": 1, "total_pages": 1 }
}

Answers of fields listed in a screen's sensitive list are masked as ••••.

sender_type tells you where the send came from: inbox, campaign, automation, api or adopted.

Webhook events​

Subscribe a webhook endpoint to these events. They use the standard envelope:

{
"id": "evt_8f2a1c3b4d5e6f70",
"type": "whatsapp_flow.completed",
"event_id": "5b0e2c1a-…",
"created_at": "2026-10-03T09:17:03Z",
"tenant_id": "tnt_…",
"data": { … }
}

whatsapp_flow.completed​

Sent once per completed session. event_id is the session id, so you can de-duplicate on it.

{
"type": "whatsapp_flow.completed",
"event_id": "5b0e2c1a-…",
"data": {
"session_id": "5b0e2c1a-…",
"flow_definition_id": "0f9e8d7c-…",
"flow_name": "Rückruf anfordern",
"version_id": "a1b2c3d4-…",
"meta_flow_id": "1234567890123456",
"channel_id": "8c1d0f5e-…",
"contact_id": "c0ffee00-…",
"conversation_id": "9a8b7c6d-…",
"message_id": "msg_91ab…",
"sender_type": "api",
"answers": {
"first_name": "Anna",
"callback_time": "vormittags",
"newsletter": true
},
"writeback": {
"state": "done",
"contact_fields": { "first_name": "Anna" },
"subscriptions": [{ "list_id": "a9c4…", "status": "ok" }]
},
"completed_at": "2026-10-03T09:17:02Z"
}
}

answers contains every input by its name, including unmapped ones. Answers of fields listed in a screen's sensitive list are masked as ••••, as in the sessions API, so those values do not leave SendSeven. If your system needs them, send them to your own data source webhook instead. Values keep WhatsApp's types: strings, booleans for OptIn, arrays of option ids for CheckboxGroup, dates as strings.

Lifecycle events​

whatsapp_flow.status_changed and whatsapp_flow.health_changed report changes that WhatsApp makes to a published flow. Their event_id is <publication id>:<hash>: unique for every change WhatsApp reports, and the same when WhatsApp delivers one change twice. De-duplicate on event_id.

whatsapp_flow.status_changed

{
"type": "whatsapp_flow.status_changed",
"event_id": "pub_3c9e…:9b1f0c2d3e4a5b6c",
"data": {
"channel_id": "8c1d0f5e-…",
"waba_id": "102938475610293",
"flow": { "id": "0f9e8d7c-…", "name": "Terminbuchung", "meta_flow_id": "1234567890123456" },
"message": "Flow Terminbuchung changed status from PUBLISHED to THROTTLED",
"old_status": "PUBLISHED",
"new_status": "THROTTLED"
}
}

Statuses: DRAFT, PUBLISHED, DEPRECATED, BLOCKED, THROTTLED (see Publishing).

whatsapp_flow.health_changed

Only relevant for dynamic flows: WhatsApp monitors the data endpoint and raises or clears alerts.

{
"type": "whatsapp_flow.health_changed",
"event_id": "pub_3c9e…:9b1f0c2d3e4a5b6c",
"data": {
"channel_id": "8c1d0f5e-…",
"waba_id": "102938475610293",
"flow": { "id": "0f9e8d7c-…", "name": "Terminbuchung", "meta_flow_id": "1234567890123456" },
"message": "Endpoint latency is above the threshold",
"alert": {
"event": "ENDPOINT_LATENCY",
"alert_state": "ACTIVATED",
"alert_severity": "WARNING",
"p90_latency": 7200,
"threshold": 7000
},
"health_status": "RED"
}
}
alert.eventMeaning
ENDPOINT_ERROR_RATEToo many failed data requests.
ENDPOINT_LATENCYData requests are too slow (p50_latency, p90_latency).
ENDPOINT_AVAILABILITYThe endpoint was not reachable.
CLIENT_ERROR_RATEToo many errors on the contacts' phones.

health_status is RED while any alert is ACTIVATED, and GREEN once all are DEACTIVATED. Metric fields (error_rate, threshold, p50_latency, p90_latency, requests_count, availability, errors) are present when WhatsApp sends them. If a dynamic flow keeps failing, check your webhook data source timeouts first; WhatsApp may block a flow whose endpoint stays unhealthy.