Webhooks Summary
Webhooks allow you to receive real-time updates whenever specific events occur in your Zentake account. Instead of polling our API for changes, you can configure Zentake to automatically send HTTP POST requests to your server when relevant events happen.
This mechanism is ideal for integrating with internal systems, triggering workflows, or keeping external services in sync.
π§ Setting Up Webhooksβ
- Create an endpoint on your server that accepts
POSTrequests. - Register your endpoint through the Webhook Configuration API.
- Select the event types and source entities you want to listen to (e.g.,
createdevents onform,response, orpacket). - Zentake will automatically send a webhook to your endpoint every time a matching event is triggered.
π Security & Verificationβ
- All outgoing webhook requests are signed using your private webhook secret.
- Use the
X-Zentake-Signatureheader to verify the authenticity of incoming requests. - We strongly recommend using HTTPS to secure data transmission over the internet.
π‘οΈ Signature Validation Exampleβ
To ensure that webhook requests are authentic and were sent by Zentake, you should validate the signature included in the X-Zentake-Signature header.
Here's an example in Python using Flask:
import hmac
import hashlib
from flask import Flask, request, abort
app = Flask(__name__)
WEBHOOK_SECRET = "your_webhook_secret_here" # Provided in Zentake panel or via API
@app.route("/webhook", methods=["POST"])
def webhook_receiver():
payload = request.data.decode("utf-8")
received_signature = request.headers.get("X-Zentake-Signature")
if not is_valid_signature(payload, received_signature, WEBHOOK_SECRET):
abort(400, "Invalid signature")
# Process payload safely
print("Webhook received and verified:", payload)
return "", 200
def is_valid_signature(payload, received_signature, secret):
if not received_signature:
return False
expected_signature = hmac.new(
key=secret.encode("utf-8"),
msg=payload.encode("utf-8"),
digestmod=hashlib.sha256,
).hexdigest()
return hmac.compare_digest(expected_signature, received_signature)
Webhook Eventsβ
When subscribed to Zentake webhooks, your endpoint will receive POST requests with a JSON body in the following structure:
{
"id": "82ad28e4-38c6-4ce4-870b-6734dc14a61f",
"timestamp": "2025-11-10T22:49:41.773393",
"event_type": "updated",
"source": "packet_response",
"data": {}
}
Top-level fieldsβ
| 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 | Type of event, e.g. created, updated, deleted. |
| source | string | The resource type that triggered the event. Possible values include: document, form, packet, patient, response, packet_response. |
| data | object | The resource payload. Its format matches the schema of the resource indicated in source. |
data payload formatsβ
For most webhook sources, the data object will follow the same schema used by their corresponding GET /{resource}/{id}/ βretrieveβ endpoint in the public API:
| Source | Schema reference |
|---|---|
document | GET /v2/documents/{id}/ |
form | GET /v2/forms/{id}/ |
packet | GET /v2/packets/{id}/ |
patient | GET /v2/patients/{id}/ |
response | GET /v2/responses/{id}/ |
packet_response schemaβ
The packet_response source is the only one that does not correspond to a public /retrieve endpoint.
Its structure is defined by the external integrations PacketResponseSerializer used for webhook payloads,
expanded below for reference:
{
"id": "string", // uid of the packet response
"is_submitted": true,
"submitted_at": "2025-11-10T22:49:41Z",
"is_archived": false,
"created_at": "2025-11-10T22:49:41Z",
"updated_at": "2025-11-10T22:49:41Z",
"form_index": 1,
"is_queued": false,
"responses": ["520f730d-bcea-4cae-9e0e-28eeb3542225", "520f730d-bcea-4cae-9e0e-28eeb3542226"],
"packet": {
"id": "string",
"name": "Packet name",
"text": "Packet description",
"forms": [
{
"id": "string",
"name": "Form name",
"text": "Form description",
"enabled": true,
"created_at": "2025-11-01T13:22:09Z",
"updated_at": "2025-11-03T09:51:44Z",
"questions": [
{
"id": "string",
"question_type": {
"question_type": "short_text",
"question_subtype": null
},
"text": "What is your name?",
"help_text": "",
"order": 1
}
]
}
]
}
}
Field referenceβ
| Field | Type | Description |
|---|---|---|
| id | string | Unique identifier (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 | string (ISO-8601) | Timestamp when the packet response was created. |
| updated_at | string (ISO-8601) | Timestamp when the packet response was last updated. |
| form_index | integer (nullable) | Client-supplied progress marker indicating how far the patient has advanced through the packet's forms. It increases as the patient progresses; treat a populated (> 0) value as patient progress. null means no patient progress has been recorded yet (e.g. immediately after the packet is sent/set up). |
| is_queued | boolean | Whether the packet response is queued for processing. |
| responses | array of string | UIDs of the individual form responses in this packet. |
Example webhook for a packet_response.updated eventβ
{
"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": [...]
},
"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"]
}
}
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 patient
partial progress. Because payloads reflect the latest persisted state at delivery time and pending
events may be de-duplicated, do not rely on receiving one event per individual form step.
Notesβ
- The
datapayload always reflects the latest persisted state of the resource at the time of the event. - Webhook retries may occur; deduplicate by the
idfield if necessary. - Future webhook sources will follow the same pattern, always aligning with their corresponding
/retrieveschema unless otherwise specified.
β Best Practicesβ
- Always verify signatures to ensure the request is from Zentake.
- Respond with a 2xx HTTP status code to acknowledge receipt.
- Process events asynchronously to avoid timeout issues.
- Implement retry logic on your side in case a webhook is missed.
- Use the Webhook Events API to monitor delivery status and troubleshoot issues.
For more details on configuring and managing webhooks, explore the endpoints below.
Retrieve a list of webhook configurations
Returns all webhook configurations associated with the authenticated team.
Create a new webhook configuration
Creates a webhook configuration that defines how and when Zentake will send event notifications to your external system.
Update a webhook configuration
Fully updates the fields of a webhook configuration identified by its `id`.
Partial update a webhook configuration
Applies a partial update to a webhook configuration by its `id`. Only the provided fields will be modified.
Delete a webhook configuration
Deletes the webhook configuration identified by `id`. This stops all future events from being sent to the registered endpoint.
Retrieve a list of webhook events
Returns a paginated list of webhook delivery attempts triggered by your configurations. Each record includes:
Test a webhook configuration
Test a webhook configuration by sending a simulated event to the configured target URL.