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": "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": "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": "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: {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 |
mentionedProfiles | array | Mentioned profile IDs (in mention order) |
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 |
Mentions
To create mentions via External API message sending, use <@profileId> in message text.
Example request text:
Hi <@550e8400-e29b-41d4-a716-446655440001>, can you review this?
In webhook payloads, mentions are returned under message.mentions.
message.mentionedProfiles may also be present as a compact ID list.
{
"message": {
"text": "Hi <@550e8400-e29b-41d4-a716-446655440001>, can you review this?",
"mentionedProfiles": ["550e8400-e29b-41d4-a716-446655440001"],
"mentions": [
{
"id": "550e8400-e29b-41d4-a716-446655440001",
"name": "Alice Johnson"
}
]
}
}
message.text keeps the mention token format (<@profileId>), while mentions[] provides resolved display names.
mentions fields:
| Field | Type | Description |
|---|
id | string | Mentioned profile ID |
name | string | Mention display name |
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": "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": "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": "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": "550e8400-e29b-41d4-a716-446655440003",
"memberName": "Carol Williams",
"addedBy": "550e8400-e29b-41d4-a716-446655440001"
}
}
member.removed
{
"id": "evt_...",
"type": "member.removed",
"eventVersion": 1,
"timestamp": 1699564800000,
"data": {
"topicId": "550e8400-e29b-41d4-a716-446655440000",
"memberId": "550e8400-e29b-41d4-a716-446655440003",
"memberName": "Carol Williams",
"removedBy": "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": "550e8400-e29b-41d4-a716-446655440001"
}
}
Only changed fields are included in the payload. If only the description was updated, name would be absent.
Poll Vote Events
poll_vote.created
Sent when a user votes on a poll in a topic where your bot is a member.
{
"id": "evt_...",
"type": "poll_vote.created",
"eventVersion": 1,
"timestamp": 1699564800000,
"data": {
"pollVoteId": "att_550e8400_opt1_550e8400-e29b-41d4-a716-446655440001",
"attachmentId": "att_550e8400-e29b-41d4-a716-446655440088",
"messageId": "660e8400-e29b-41d4-a716-446655440001",
"topicId": "550e8400-e29b-41d4-a716-446655440000",
"optionId": "opt1",
"voterId": "550e8400-e29b-41d4-a716-446655440001",
"createdAt": 1699564800000
}
}
poll_vote.deleted
Sent when a user removes their vote from a poll.
{
"id": "evt_...",
"type": "poll_vote.deleted",
"eventVersion": 1,
"timestamp": 1699564900000,
"data": {
"pollVoteId": "att_550e8400_opt1_550e8400-e29b-41d4-a716-446655440001",
"attachmentId": "att_550e8400-e29b-41d4-a716-446655440088",
"messageId": "660e8400-e29b-41d4-a716-446655440001",
"topicId": "550e8400-e29b-41d4-a716-446655440000",
"optionId": "opt1",
"voterId": "550e8400-e29b-41d4-a716-446655440001"
}
}
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