Skip to main content

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": {}
}
FieldTypeDescription
idstring (UUID)Unique identifier of this webhook event.
timestampstring (ISO-8601)UTC timestamp when the event was generated.
event_typestringcreated, updated, or deleted.
sourcestringThe resource type that triggered the event, see Event types.
dataobjectThe resource payload. Its format matches the schema of the resource named in source.
data always reflects current state

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 than GET /v2/patients/{id}. data contains only id, first_name, last_name, email, date_birth, and external_id, not the full patient detail shape (no middle_name, phone numbers, address, patient_tags, custom_fields, etc.). It's the same shape as the nested patient object inside a response payload.
  • document. The webhook payload always includes input_fields. The GET /v2/documents/{id} endpoint only returns that shape when you send the X-API-VERSION: v2.1 header; without it, the default response omits input_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"]
}
}
FieldTypeDescription
idstringuid of the packet response.
packetobjectThe associated packet, including its nested forms and questions.
is_submittedbooleanWhether the packet has been fully submitted.
submitted_atstring, nullableTimestamp of submission, if applicable.
is_archivedbooleanWhether this packet response is archived.
created_at / updated_atstring (ISO-8601)Record timestamps.
form_indexinteger, nullableProgress 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_queuedbooleanWhether the packet response is queued for processing.
responsesarray of stringUIDs of the individual form responses in this packet.
Interpreting packet_response.updated

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.

Contains PHI

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-Signature header before trusting a payload.
  • Respond with a 2xx status code promptly to acknowledge receipt.
  • Process the event asynchronously if your handling logic is slow, so you don't risk a delivery timeout.