Webhook payloads
Envelope
Every webhook delivery is a POST with this top-level JSON body,
regardless of source:
{
"id": "82ad28e4-38c6-4ce4-870b-6734dc14a61f",
"timestamp": "2025-11-10T22:49:41.773393",
"event_type": "updated",
"source": "packet_response",
"data": {}
}
| Field | Type | Description |
|---|---|---|
id | string (UUID) | Unique identifier of this webhook event. |
timestamp | string (ISO-8601) | UTC timestamp when the event was generated. |
event_type | string | created, updated, or deleted. |
source | string | The resource type that triggered the event, see Event types. |
data | object | The resource payload. Its format matches the schema of the resource named in source. |
data is the latest persisted state of the resource at delivery
time, not a diff of what changed. Webhook retries may occur, deduplicate
by the top-level id if your handler isn't idempotent.
data shape per source
For form, packet, and response, data matches that resource's own
GET /v2/{resource}/{id} retrieve schema. Three sources diverge:
patient. The webhook uses a lighter serializer thanGET /v2/patients/{id}.datacontains onlyid,first_name,last_name,email,date_birth, andexternal_id, not the full patient detail shape (nomiddle_name, phone numbers, address,patient_tags,custom_fields, etc.). It's the same shape as the nestedpatientobject inside aresponsepayload.document. The webhook payload always includesinput_fields. TheGET /v2/documents/{id}endpoint only returns that shape when you send theX-API-VERSION: v2.1header; without it, the default response omitsinput_fields.packet_response. There's no public retrieve endpoint for it, so its shape is documented below in full.
patient
{
"id": "abc123",
"timestamp": "2025-11-10T22:49:41.773393",
"event_type": "created",
"source": "patient",
"data": {
"id": "jane-doe-9f2c",
"first_name": "Jane",
"last_name": "Doe",
"email": "jane.doe@example.com",
"date_birth": "1990-04-12",
"external_id": ""
}
}
document
{
"id": "83fd28e4-38c6-4ce4-870b-6734dc14a620",
"timestamp": "2025-11-10T22:49:41.773393",
"event_type": "updated",
"source": "document",
"data": {
"id": "5b1c1e2a-3f4d-4c5b-8e6f-7a8b9c0d1e2f",
"name": "New Patient Consent Form",
"enabled": true,
"created_at": "2025-10-01T13:22:09Z",
"updated_at": "2025-11-10T22:49:41Z",
"input_fields": [
{
"id": "6c2d2e3b-4f5e-5d6c-9f7a-8b9c0d1e2f3a",
"input_type": "signature",
"left": 120.5,
"top": 640.0,
"width": 200.0,
"height": 40.0,
"required": true,
"is_office_use": false
}
]
}
}
form
{
"id": "84fd28e4-38c6-4ce4-870b-6734dc14a621",
"timestamp": "2025-11-10T22:49:41.773393",
"event_type": "updated",
"source": "form",
"data": {
"id": "9d2f730d-bcea-4cae-9e0e-28eeb3542230",
"name": "Medical History",
"text": "Medical History",
"enabled": true,
"created_at": "2025-10-01T13:22:09Z",
"updated_at": "2025-11-03T09:51:44Z",
"questions": [
{
"id": "q_1",
"question_type": {"question_type": "text", "question_subtype": "single_line"},
"text": "Do you have any known allergies?",
"help_text": "",
"order": 1
}
]
}
}
packet
{
"id": "85fd28e4-38c6-4ce4-870b-6734dc14a622",
"timestamp": "2025-11-10T22:49:41.773393",
"event_type": "updated",
"source": "packet",
"data": {
"id": "p_98765",
"name": "New Patient Intake Packet",
"text": "New Patient Intake",
"forms": [
{
"id": "9d2f730d-bcea-4cae-9e0e-28eeb3542230",
"name": "Demographics",
"text": "Demographics",
"enabled": true,
"created_at": "2025-10-01T13:22:09Z",
"updated_at": "2025-10-01T13:22:09Z",
"questions": []
}
]
}
}
response
{
"id": "86fd28e4-38c6-4ce4-870b-6734dc14a623",
"timestamp": "2025-11-10T22:49:41.773393",
"event_type": "updated",
"source": "response",
"data": {
"id": "520f730d-bcea-4cae-9e0e-28eeb3542225",
"patient": {
"id": "jane-doe-9f2c",
"first_name": "Jane",
"last_name": "Doe",
"email": "jane.doe@example.com",
"date_birth": "1990-04-12",
"external_id": ""
},
"packet_response": "pr_123456",
"form": {"id": "9d2f730d-bcea-4cae-9e0e-28eeb3542230", "name": "Demographics"},
"document": null,
"is_submitted": true,
"submitted_at": "2025-11-10T22:49:00Z",
"is_archived": false,
"created_at": "2025-11-10T22:45:00Z",
"updated_at": "2025-11-10T22:49:00Z",
"answers": [
{
"id": "a_1",
"question": {"id": "q_1", "text": "Do you have any known allergies?"},
"answer_raw": "Penicillin",
"answer_text": "Penicillin",
"notes": null,
"answer_choices": []
}
],
"answer_signatures": [],
"answer_matrix_cells": [],
"answer_files": [],
"user": null
}
}
packet_response
The packet_response source is defined by the external-integrations
PacketResponseSerializer used for webhook payloads, not a public
/retrieve endpoint:
{
"id": "82ad28e4-38c6-4ce4-870b-6734dc14a61f",
"timestamp": "2025-11-10T22:49:41.773393",
"event_type": "updated",
"source": "packet_response",
"data": {
"id": "pr_123456",
"packet": {
"id": "p_98765",
"name": "Intake Packet",
"text": "Patient onboarding packet",
"forms": [
{
"id": "9d2f730d-bcea-4cae-9e0e-28eeb3542230",
"name": "Demographics",
"text": "Demographics",
"enabled": true,
"created_at": "2025-10-01T13:22:09Z",
"updated_at": "2025-10-01T13:22:09Z",
"questions": []
}
]
},
"is_submitted": false,
"submitted_at": null,
"is_archived": false,
"created_at": "2025-11-10T22:49:00Z",
"updated_at": "2025-11-10T22:49:41Z",
"form_index": 1,
"is_queued": false,
"responses": ["520f730d-bcea-4cae-9e0e-28eeb3542225"]
}
}
| Field | Type | Description |
|---|---|---|
id | string | uid of the packet response. |
packet | object | The associated packet, including its nested forms and questions. |
is_submitted | boolean | Whether the packet has been fully submitted. |
submitted_at | string, nullable | Timestamp of submission, if applicable. |
is_archived | boolean | Whether this packet response is archived. |
created_at / updated_at | string (ISO-8601) | Record timestamps. |
form_index | integer, nullable | Progress marker, increases as the Patient advances through the packet's forms; null means no progress recorded yet (e.g. right after the packet is sent). |
is_queued | boolean | Whether the packet response is queued for processing. |
responses | array of string | UIDs of the individual form responses in this packet. |
A packet_response.updated event with is_submitted: false and
form_index: null is typically setup/send noise (emitted when the
packet is created and its responses are initialized), not Patient
activity. Treat is_submitted: false together with a populated
form_index as partial progress, and is_submitted: true as complete.
Because payloads reflect the latest persisted state and pending events
may be de-duplicated, don't rely on receiving one event per individual
form step.
response and packet_response payloads carry the Patient's actual
answers, and patient payloads carry PII. Verify the signature (see
Signature verification)
and store/transmit these payloads as PHI.
Best practices
- Always verify the
X-Zentake-Signatureheader before trusting a payload. - Respond with a
2xxstatus code promptly to acknowledge receipt. - Process the event asynchronously if your handling logic is slow, so you don't risk a delivery timeout.