Channel Webhooks
This guide explains how to integrate with ChatWerk channel webhooks.
What channel webhooks are
Channel webhooks let ChatWerk send HTTP POST notifications to your endpoint whenever selected events happen on a specific channel. This is probably one the most important feature you are looking for.
Typical use cases:
- synchronize conversations or contacts into an external system
- trigger workflows after inbound or outbound messages
- react to status changes such as message delivery state or conversation assignment
- keep a lightweight near-real-time mirror of channel activity
How the integration works
- Your system exposes an HTTPS endpoint that accepts JSON
POSTrequests. - You create a webhook for a specific channel in ChatWerk.
- You choose either:
- all events with
all_events: true - a selected subset with
events: [...]
- all events with
- ChatWerk sends matching events to your endpoint.
- Your endpoint validates the secret header, parses the payload, processes it idempotently, and returns a
2xxresponse quickly.
Management API
Channel webhooks are managed per channel.
Available endpoints:
- list webhooks
GET /core/channels/{channel_id}/webhooks - create webhook
POST /core/channels/{channel_id}/webhooks - update webhook
PUT /core/channels/{channel_id}/webhooks/{whid} - delete webhook
DELETE /core/channels/{channel_id}/webhooks/{whid}
Access requirements:
- the caller must be authenticated
- the caller must have access to the specific channel
Step-by-step setup
1. Prepare your receiver endpoint
Your endpoint should:
- accept
POSTrequests withContent-Type: application/json - validate the
x-webhook-secretheader - tolerate duplicate deliveries
- process optional fields defensively
- return
2xxas soon as the payload is safely accepted
Minimal example:
POST /integrations/chatwerk/channel-events HTTP/1.1
Content-Type: application/json
x-webhook-secret: 3d9f...
user-agent: chatwerk/1.0With payloads described below.
2. Create a webhook
Example request: POST /core/channels/channel-00/webhooks
{
"url": "https://example.com/integrations/chatwerk/channel-events",
"headers": {
"X-Partner-Id": "acme"
},
"enabled": true,
"events": [
"message_received",
"message_status_changed",
"conversation_created"
]
}Example response:
{
"id": "channelWh0",
"url": "https://example.com/integrations/chatwerk/channel-events",
"secret": "generated-secret",
"headers": {
"X-Partner-Id": "acme"
},
"enabled": true,
"created_at": "2026-06-09T10:00:00Z",
"updated_at": "2026-06-09T10:00:00Z",
"events": [
"message_received",
"message_status_changed",
"conversation_created"
]
}Notes:
secretis generated by ChatWerk on creation- the secret is sent back in webhook deliveries as
x-webhook-secret - custom headers are forwarded as configured
- if you omit
events, the current implementation falls back toall_events: true
3. Store the secret securely
Treat the returned secret like a credential:
- store it encrypted or in a secret manager
- compare it exactly on every incoming request
- never rely only on source IP
If you need additional authentication, configure extra static headers in the webhook definition and validate them too.
4. Test the payload shape
Use the preview endpoint to inspect example payloads before enabling production processing:
GET /core/channel_webhooks/preview?event_type=message_received
GET /core/channel_webhooks/preview?event_type=conversation_created
GET /core/channel_webhooks/preview?event_type=com_channel_createdThis is useful for:
- bootstrapping parsers
- checking which top-level objects are present for a given event type
- generating internal fixtures
5. Enable processing in production
Once your receiver validates the secret and successfully parses the payload:
- keep the webhook
enabled: true - monitor non-
2xxresponses - log event IDs for traceability
Webhook request format
ChatWerk sends POST requests with:
- header
Content-Type: application/json - header
user-agent: chatwerk/1.0 - header
x-webhook-secret: <webhook-secret> - any custom headers configured in the webhook
The target URL may include query parameters. They are preserved when ChatWerk calls the endpoint.
Payload structure
Top-level payload:
{
"id": "event-id",
"type": "message_status_changed",
"channel_id": "channel-01",
"contact": {},
"com_channel": {},
"conversation": {},
"gateway": {},
"message": {},
"message_status": {},
"timestamp": "2026-06-09T10:00:00Z"
}Important behavior:
idis the event ID; use it for deduplicationtypeis the event namechannel_idis always included for channel webhooksmessage_statusis currently populated formessage_status_changedmessage,conversation,com_channel,contact, andgatewayare optional and depend on the event
Example payloads
Message status changed
{
"id": "some_id",
"type": "message_status_changed",
"channel_id": "channel-01",
"com_channel": {
"id": "0000000000",
"messenger_id": "some-messenger-id",
"type": "person"
},
"conversation": {
"id": "0000000000",
"blocked": false,
"opted_in": false,
"created_at": "0001-01-01T00:00:00Z"
},
"gateway": {
"id": "0000000000"
},
"message": {
"id": "msg-id",
"type": "text",
"direction": "in",
"text": "[email protected]",
"author_id": "testUser1",
"timestamp": "2024-01-01T00:00:00Z"
},
"message_status": {
"id": "msg-id",
"status": "read"
},
"timestamp": "2026-06-09T10:00:00Z"
}Message received
Representative shape:
{
"id": "some_id",
"type": "message_received",
"channel_id": "channel_id",
"com_channel": {
"id": "com_channel_id",
"messenger_id": "messenger_id",
"name": "name",
"attributes": {
"key": "value"
},
"type": "person",
"overridden_name": true,
"notes": "some notes"
},
"conversation": {
"id": "conversation_id",
"labels": [
"label_1",
"label_2"
],
"blocked": true,
"opted_in": true
},
"gateway": {
"id": "gateway_id",
"messenger_id": "messenger_id",
"provider": "WhatsApp",
"messenger_type": "WhatsApp"
},
"message": {
"id": "message_id",
"author_id": "author_id",
"type": "image",
"direction": "in",
"text": "some text",
"labels": [
"some",
"label"
],
"data": {
"media": [
{
"url": "https://signed.example/media",
"mime_type": "image/gif",
"status": "synced"
}
],
"error": {
"text": "error msg",
"code": "error code"
},
"location": {
"latitude": 54.89587784,
"longitude": 19.15745544
},
"items": [
{
"payload": "item payload",
"text": "item text",
"description": "item description"
}
],
"pressed_buttons": [
{
"payload": "some payload",
"text": "any"
}
]
},
"timestamp": "2026-06-09T10:00:00Z"
},
"timestamp": "2026-06-09T10:00:00Z"
}Supported event types
The current implementation accepts the following channel webhook events.
Message events
message_received: incoming message received on the channelmessage_sent: outbound message sent from ChatWerkmessage_edited: existing message content changedmessage_deleted: message removedmessage_reacted: reaction added to a messagemessage_unreacted: reaction removed from a messagemessage_blocked: message blockedmessage_status_changed: delivery or read status changedmessage_media_synchronized: media became available in synchronized storagemessage_media_synchronization_error: media synchronization failedmessage_media_transformed: media was transformedmessage_labels_changed: message labels changedbutton_message_replied: interactive button reply received
Conversation events
conversation_created: conversation createdconversation_deleted: conversation deletedconversation_opened: conversation openedconversation_closed: conversation closedconversation_read: conversation marked as readconversation_unread: conversation marked as unreadconversation_blocked: conversation blockedconversation_unblocked: conversation unblockedconversation_assigned: conversation assigned to a userconversation_unassigned: conversation unassignedconversation_labels_changed: conversation labels changedconversation_opted_in: conversation opted inconversation_opted_out: conversation opted outconversation_imported: conversation imported
Communication channel events
com_channel_created: communication channel createdcom_channel_deleted: communication channel deletedcom_channel_name_changed: communication channel name changedcom_channel_notes_changed: communication channel notes changedcom_channel_email_changed: communication channel email changedcom_channel_phone_changed: communication channel phone changedcom_channel_mid_changed: messenger identifier changedcom_channel_attributes_changed: communication channel attributes changed
Marketing and invitation events
marketing_opted_in: marketing consent grantedmarketing_opted_out: marketing consent revokedclear_marketing_opted_in: marketing opt-in state clearedmarketing_opt_in_question: opt-in question flow triggeredconversation_invited: number upload invitation sentconversation_invitation_accepted: invitation acceptedconversation_invitation_rejected: invitation rejected
Event selection strategy
Use narrow subscriptions when possible.
Recommended patterns:
- CRM sync:
com_channel_*,conversation_created,message_received - delivery tracking:
message_sent,message_status_changed - bot or workflow triggering:
message_received,button_message_replied - conversation operations:
conversation_created,conversation_opened,conversation_closed,conversation_assigned
Avoid subscribing to all events unless you really need them. It increases processing volume and makes versioning harder on your side.
Potential problems and how to handle them
1. Duplicate deliveries
The sender uses retries. Your endpoint may receive the same event more than once.
How to handle it:
- deduplicate by payload
id - make handlers idempotent
- store processed IDs for an appropriate retention period
2. Slow endpoint responses
The sender timeout is currently 5 seconds and the sender retries up to 3 times.
How to handle it:
- acknowledge quickly with
2xx - move heavy work to an internal queue
- avoid synchronous calls to slow downstream systems
3. Delivery is best effort
Failed webhook calls are logged by the sender, but they are not escalated into a replay mechanism in the channel webhook sender itself.
How to handle it:
- monitor your availability and error rates
- alert on non-
2xxresponses - persist raw webhook payloads on your side for troubleshooting
- if data completeness matters, build a reconciliation process against your source of truth
4. Changes may not apply immediately
Webhook definitions are cached by channel for up to 1 minute in the sender process.
What this means:
- a newly created webhook may not start receiving events instantly
- a deleted or disabled webhook may still receive events for a short period
- event filter changes may take up to 1 minute to fully propagate
How to handle it:
- expect short propagation delay after create, update, delete, enable, or disable
- do not treat one or two transitional events as a bug immediately
5. Optional fields are not guaranteed
Not every payload contains every object. The payload depends on the event type and available context.
How to handle it:
- treat
message,conversation,contact,com_channel,gateway, andmessage_statusas optional - validate only the fields required by your use case
- use tolerant JSON parsing
6. Media URL availability
Media URLs are only attached when media is already synchronized and a signed URL can be generated. In practice, do not assume every media item includes a usable url.
How to handle it:
- check
message.data.media[].status - treat missing
urlas a valid case - retry your own media fetch logic only if your business process requires it
7. Preview payloads vs live payloads
The preview endpoint is intentionally illustrative. It helps with schema discovery, but it is not a contract that every example field will always appear in live traffic.
One current implementation detail worth knowing:
- preview examples may show richer media objects than the live sender currently populates
How to handle it:
- use preview payloads for onboarding
- validate your parser against real deliveries before going live
8. URL validation during configuration
Webhook create and update requests validate that the target URL is reachable.
How to handle it:
- expose an externally reachable endpoint before creating the webhook
- verify DNS, TLS, firewall, and routing first
- expect
422withurl_reachableif the URL cannot be reached
Error cases when managing webhooks
Typical API responses:
401: unauthenticated request403: authenticated user without write permission404: webhook not found on update or delete422: invalid request data
Known validation errors:
url_reachable: the configured URL is not reachablenotification_events: at least one event type is invalid
Good practices
- use one webhook endpoint per integration purpose when responsibilities differ
- validate
x-webhook-secretbefore parsing deeply - log event
id,type, andchannel_id - process asynchronously
- design for retries and duplicates
- keep event subscriptions as narrow as possible
- version your internal mapping from ChatWerk events to your domain events
- keep a dead-letter or quarantine path for malformed or unsupported payloads
- test with the preview endpoint and then with live deliveries
Recommended receiver flow
- Receive the request.
- Verify
x-webhook-secret. - Parse JSON.
- Reject unsupported channels or event types if needed.
- Deduplicate by event
id. - Persist the raw payload for audit/debugging.
- Enqueue business processing.
- Return
200 OK.
Summary
Channel webhooks are a straightforward push integration for channel-level events. The important implementation constraints are:
- secret-based header validation
- optional and event-dependent payload sections
- retries and duplicate tolerance
- best-effort delivery
- up to 1 minute propagation delay for webhook changes
If you build your receiver around idempotency, fast acknowledgements, and good observability, the integration is robust and simple to operate.
Updated about 2 months ago
