Skip to main content

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​

  1. Create an endpoint on your server that accepts POST requests.
  2. Register your endpoint through the Webhook Configuration API.
  3. Select the event types and source entities you want to listen to (e.g., created events on form, response, or packet).
  4. 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-Signature header 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​

FieldTypeDescription
idstring (UUID)Unique identifier of this webhook event.
timestampstring (ISO-8601)UTC timestamp when the event was generated.
event_typestringType of event, e.g. created, updated, deleted.
sourcestringThe resource type that triggered the event. Possible values include: document, form, packet, patient, response, packet_response.
dataobjectThe 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:

SourceSchema reference
documentGET /v2/documents/{id}/
formGET /v2/forms/{id}/
packetGET /v2/packets/{id}/
patientGET /v2/patients/{id}/
responseGET /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​

FieldTypeDescription
idstringUnique identifier (uid) of the packet response.
packetobjectThe associated packet, including its nested forms and questions.
is_submittedbooleanWhether the packet has been fully submitted.
submitted_atstring (nullable)Timestamp of submission, if applicable.
is_archivedbooleanWhether this packet response is archived.
created_atstring (ISO-8601)Timestamp when the packet response was created.
updated_atstring (ISO-8601)Timestamp when the packet response was last updated.
form_indexinteger (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_queuedbooleanWhether the packet response is queued for processing.
responsesarray of stringUIDs 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 data payload always reflects the latest persisted state of the resource at the time of the event.
  • Webhook retries may occur; deduplicate by the id field if necessary.
  • Future webhook sources will follow the same pattern, always aligning with their corresponding /retrieve schema 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.