When you configure a webhook, Zenzap sends HTTP POST requests to your URL when events occur. This page documents the payload structure for each event type.
Event Envelope
All webhook events share the same envelope structure:
{
"id": "evt_550e8400-e29b-41d4-a716-446655440099",
"type": "message.created",
"eventVersion": 1,
"timestamp": 1699564800000,
"data": { ... }
}
| Field | Type | Description |
|---|
id | string | Unique event ID (use for deduplication) |
type | string | Event type (see below) |
eventVersion | integer | Schema version (currently 1) |
timestamp | integer | Unix timestamp in milliseconds |
data | object | Event-specific payload |
Each webhook delivery includes these headers:
| Header | Description |
|---|
X-Zenzap-Event | Event type (e.g., message.created) |
X-Zenzap-Signature | HMAC-SHA256 signature for verification |
X-Zenzap-Timestamp | Unix timestamp (milliseconds) when sent |
X-Zenzap-Delivery-Id | Unique delivery ID (for deduplication) |
Signature Verification
When a secret is configured, verify webhooks by calculating:
import hmac
import hashlib
def verify_webhook(payload: bytes, signature: str, timestamp: str, secret: str) -> bool:
expected = hmac.new(
secret.encode(),
f"{timestamp}.{payload.decode()}".encode(),
hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature)
Message Events
message.created
Sent when a new message is posted in a topic where your bot is a member.
{
"id": "evt_550e8400-e29b-41d4-a716-446655440099",
"type": "message.created",
"eventVersion": 1,
"timestamp": 1699564800000,
"data": {
"message": {
"id": "660e8400-e29b-41d4-a716-446655440001",
"topicId": "550e8400-e29b-41d4-a716-446655440000",
"senderId": "u@550e8400-e29b-41d4-a716-446655440001",
"senderName": "Alice Johnson",
"senderType": "user",
"type": "text",
"text": "Hello team!",
"createdAt": 1699564800000
}
}
}
message.updated
Sent when a message is edited or when voice message transcription completes.
{
"id": "evt_550e8400-e29b-41d4-a716-446655440099",
"type": "message.updated",
"eventVersion": 1,
"timestamp": 1699564850000,
"data": {
"message": {
"id": "660e8400-e29b-41d4-a716-446655440001",
"topicId": "550e8400-e29b-41d4-a716-446655440000",
"senderId": "u@550e8400-e29b-41d4-a716-446655440001",
"senderName": "Alice Johnson",
"senderType": "user",
"type": "text",
"text": "Hello team! (edited)",
"createdAt": 1699564800000,
"updatedAt": 1699564850000
}
}
}
message.deleted
Sent when a message is deleted.
{
"id": "evt_550e8400-e29b-41d4-a716-446655440099",
"type": "message.deleted",
"eventVersion": 1,
"timestamp": 1699564900000,
"data": {
"messageId": "660e8400-e29b-41d4-a716-446655440001",
"topicId": "550e8400-e29b-41d4-a716-446655440000",
"deletedBy": "u@550e8400-e29b-41d4-a716-446655440001"
}
}
Message Object
The message object in message events contains:
| Field | Type | Description |
|---|
id | string | Message UUID |
topicId | string | Topic UUID |
senderId | string | Sender ID (format: u@{uuid} for users, b@{uuid} for bots) |
senderName | string | Sender’s display name |
senderType | string | user, bot, or system |
type | string | Message type (see below) |
text | string | Message text (may be empty for non-text types) |
createdAt | integer | Creation timestamp (ms) |
updatedAt | integer | Last update timestamp (ms) |
parentId | string | Replied-to message ID |
attachments | array | File attachments |
mentions | array | User mentions |
location | object | Location data (for location messages) |
task | object | Task data (for task messages) |
contact | object | Contact data (for contact messages) |
Message Types
| Type | Description | Additional Fields |
|---|
text | Regular text message | text |
image | Image attachment | attachments[] |
file | File attachment | attachments[] |
video | Video attachment | attachments[] |
audio | Voice message | attachments[] (with transcription) |
location | Shared location | location |
task | Task snapshot | task |
contact | Shared contact | contact |
Attachments
File attachments include a signed download URL:
{
"attachments": [
{
"id": "att_550e8400-e29b-41d4-a716-446655440088",
"type": "image",
"name": "screenshot.png",
"url": "https://storage.zenzap.co/attachments/...?token=...&expires=..."
}
]
}
Attachment URLs expire after 60 minutes. Download files promptly after receiving the webhook.
Voice Message Transcription
Voice messages (audio attachments) are automatically transcribed. The flow is:
message.created arrives with transcription.status: "Pending"
message.updated arrives when transcription completes
{
"type": "message.updated",
"data": {
"message": {
"type": "audio",
"attachments": [
{
"id": "att_...",
"type": "audio",
"name": "voice-note.mp3",
"url": "https://storage.zenzap.co/...",
"transcription": {
"status": "Done",
"text": "Hey team, just a quick update on the project..."
}
}
]
}
}
}
Transcription Status Values:
| Status | Description |
|---|
Pending | Queued for transcription |
Started | Transcription in progress |
Done | Complete - text field populated |
Failed | Transcription failed |
Reply Messages
Messages that reply to other messages include:
{
"message": {
"id": "msg_new",
"text": "I agree!",
"parentId": "msg_original"
}
}
Location Messages
{
"message": {
"type": "location",
"text": "",
"location": {
"latitude": "37.7749",
"longitude": "-122.4194",
"name": "San Francisco Office",
"address": "123 Market St, San Francisco, CA"
}
}
}
address is optional and is included only when available.
Task Messages
Task snapshots are sent when tasks are created, updated, or their status changes:
{
"message": {
"type": "task",
"text": "",
"task": {
"id": "task_...",
"action": "Added",
"title": "Review documentation",
"text": "Please review the API docs by Friday",
"status": "Open",
"assignee": "u@550e8400-e29b-41d4-a716-446655440001",
"dueDate": 1699651200000,
"isDueDateTimeSelected": true,
"parentId": null,
"subItemsCount": 0
}
}
}
Task Actions:
| Action | Description |
|---|
Added | Task created |
Updated | Task details changed |
Deleted | Task removed |
MarkedAsDone | Task completed |
MarkedAsOpened | Task reopened |
Replied | Comment added to task |
{
"message": {
"type": "contact",
"text": "",
"contact": {
"name": "John Doe",
"phoneNumbers": ["+1234567890"],
"emails": ["john@example.com"],
"role": "Engineer",
"location": "San Francisco",
"linkedIn": "https://linkedin.com/in/johndoe",
"profileId": "u@550e8400-e29b-41d4-a716-446655440001"
}
}
}
Reaction Events
reaction.added
{
"id": "evt_...",
"type": "reaction.added",
"eventVersion": 1,
"timestamp": 1699564800000,
"data": {
"messageId": "660e8400-e29b-41d4-a716-446655440001",
"topicId": "550e8400-e29b-41d4-a716-446655440000",
"emoji": "👍",
"userId": "u@550e8400-e29b-41d4-a716-446655440001",
"userName": "Alice Johnson"
}
}
reaction.removed
Same structure as reaction.added.
Member Events
member.added
{
"id": "evt_...",
"type": "member.added",
"eventVersion": 1,
"timestamp": 1699564800000,
"data": {
"topicId": "550e8400-e29b-41d4-a716-446655440000",
"memberId": "u@550e8400-e29b-41d4-a716-446655440003",
"memberName": "Carol Williams",
"addedBy": "u@550e8400-e29b-41d4-a716-446655440001"
}
}
member.removed
{
"id": "evt_...",
"type": "member.removed",
"eventVersion": 1,
"timestamp": 1699564800000,
"data": {
"topicId": "550e8400-e29b-41d4-a716-446655440000",
"memberId": "u@550e8400-e29b-41d4-a716-446655440003",
"memberName": "Carol Williams",
"removedBy": "u@550e8400-e29b-41d4-a716-446655440001"
}
}
Topic Events
topic.updated
Sent when a topic’s name or description is changed.
{
"id": "evt_...",
"type": "topic.updated",
"eventVersion": 1,
"timestamp": 1699564800000,
"data": {
"topicId": "550e8400-e29b-41d4-a716-446655440000",
"name": "Project Updates - Q4",
"updatedBy": "u@550e8400-e29b-41d4-a716-446655440001"
}
}
Only changed fields are included in the payload. If only the description was updated, name would be absent.
Retry & Auto-Pause Behavior
- Webhooks are retried up to 3 times with exponential backoff on failure
- After 10 consecutive failures, the webhook is automatically paused
- Re-enable a paused webhook via the Update webhook configuration endpoint