Skip to main content

Overview

Webhooks allow you to receive real-time HTTP notifications when events occur in your Chatwoot account. Use webhooks to integrate Chatwoot with external systems and automate workflows.

List All Webhooks

Retrieve all webhooks configured in your account.
GET /api/v1/accounts/{account_id}/webhooks

Path Parameters

account_id
integer
required
The ID of the account

Response

id
integer
Webhook ID
url
string
URL where webhook events will be sent
name
string
Webhook name
subscriptions
array
Array of event types this webhook is subscribed to
account_id
integer
Account ID
inbox_id
integer
Inbox ID (if webhook is inbox-specific)

Example Request

curl -X GET https://app.chatwoot.com/api/v1/accounts/1/webhooks \
  -H "api_access_token: YOUR_ACCESS_TOKEN"

Example Response

[
  {
    "id": 1,
    "url": "https://example.com/webhooks/chatwoot",
    "name": "Main Webhook",
    "subscriptions": [
      "conversation_created",
      "conversation_status_changed",
      "message_created"
    ],
    "account_id": 1,
    "inbox_id": null
  },
  {
    "id": 2,
    "url": "https://example.com/webhooks/support",
    "name": "Support Inbox Webhook",
    "subscriptions": [
      "message_created",
      "message_updated"
    ],
    "account_id": 1,
    "inbox_id": 1
  }
]

Create Webhook

Create a new webhook to receive event notifications.
POST /api/v1/accounts/{account_id}/webhooks

Path Parameters

account_id
integer
required
The ID of the account

Request Body

webhook[url]
string
required
URL endpoint to receive webhook events (must be HTTPS)
webhook[name]
string
required
Descriptive name for the webhook
webhook[subscriptions]
array
required
Array of event types to subscribe to
webhook[inbox_id]
integer
Inbox ID (optional, for inbox-specific webhooks)

Example Request

curl -X POST https://app.chatwoot.com/api/v1/accounts/1/webhooks \
  -H "api_access_token: YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "webhook": {
      "url": "https://example.com/webhooks/chatwoot",
      "name": "Production Webhook",
      "subscriptions": [
        "conversation_created",
        "conversation_status_changed",
        "conversation_updated",
        "message_created",
        "message_updated",
        "contact_created",
        "contact_updated"
      ]
    }
  }'

Example Response

{
  "id": 3,
  "url": "https://example.com/webhooks/chatwoot",
  "name": "Production Webhook",
  "subscriptions": [
    "conversation_created",
    "conversation_status_changed",
    "conversation_updated",
    "message_created",
    "message_updated",
    "contact_created",
    "contact_updated"
  ],
  "account_id": 1,
  "inbox_id": null
}

Update Webhook

Update an existing webhook configuration.
PATCH /api/v1/accounts/{account_id}/webhooks/{id}

Path Parameters

account_id
integer
required
The ID of the account
id
integer
required
The ID of the webhook to update

Request Body

Accepts the same parameters as Create Webhook.

Example Request

curl -X PATCH https://app.chatwoot.com/api/v1/accounts/1/webhooks/3 \
  -H "api_access_token: YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "webhook": {
      "name": "Updated Webhook Name",
      "subscriptions": [
        "conversation_created",
        "message_created"
      ]
    }
  }'

Delete Webhook

Delete a webhook from your account.
DELETE /api/v1/accounts/{account_id}/webhooks/{id}

Path Parameters

account_id
integer
required
The ID of the account
id
integer
required
The ID of the webhook to delete

Example Request

curl -X DELETE https://app.chatwoot.com/api/v1/accounts/1/webhooks/3 \
  -H "api_access_token: YOUR_ACCESS_TOKEN"

Response

Returns 200 OK with no body.

Webhook Events

Available webhook event types:

Conversation Events

Message Events

Contact Events

Widget Events

Webhook Payload

Webhook events are sent as HTTP POST requests to your configured URL.

Headers

Content-Type: application/json
User-Agent: Chatwoot

Payload Structure

{
  "event": "message_created",
  "id": 12345,
  "account": {
    "id": 1,
    "name": "Acme Corporation"
  },
  "inbox": {
    "id": 1,
    "name": "Website Widget"
  },
  "conversation": {
    "id": 1,
    "display_id": 1,
    "status": "open",
    "priority": "high",
    "assignee": {
      "id": 1,
      "name": "John Doe",
      "email": "[email protected]"
    },
    "contact": {
      "id": 1,
      "name": "Customer Name",
      "email": "[email protected]"
    }
  },
  "message": {
    "id": 100,
    "content": "Hello, I need help with my order",
    "message_type": "incoming",
    "content_type": "text",
    "created_at": 1704067200,
    "sender": {
      "id": 1,
      "name": "Customer Name",
      "type": "Contact"
    }
  }
}

Event-Specific Payloads

conversation_created

{
  "event": "conversation_created",
  "id": 1,
  "account": { ... },
  "inbox": { ... },
  "conversation": { ... }
}

conversation_status_changed

{
  "event": "conversation_status_changed",
  "id": 1,
  "account": { ... },
  "conversation": {
    "id": 1,
    "status": "resolved",
    "previous_status": "open"
  }
}

message_created

{
  "event": "message_created",
  "id": 100,
  "account": { ... },
  "conversation": { ... },
  "message": { ... }
}

contact_created

{
  "event": "contact_created",
  "id": 1,
  "account": { ... },
  "contact": {
    "id": 1,
    "name": "New Contact",
    "email": "[email protected]",
    "phone_number": "+1234567890"
  }
}

Webhook Security

HTTPS Required

Webhook URLs must use HTTPS for security. HTTP URLs will be rejected.

Verify Webhook Signatures

Chatwoot includes a signature in webhook requests that you can verify:
  1. Extract the signature from the request header
  2. Compute HMAC-SHA256 of the payload using your webhook secret
  3. Compare the computed signature with the received signature
import hmac
import hashlib

def verify_webhook(payload, signature, secret):
    computed_signature = hmac.new(
        secret.encode(),
        payload.encode(),
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(signature, computed_signature)

IP Allowlisting

Consider allowlisting Chatwoot’s IP addresses in your firewall for additional security.

Handling Webhook Events

Best Practices

  1. Respond Quickly: Return a 200 OK response immediately
  2. Process Asynchronously: Queue events for background processing
  3. Handle Duplicates: Use event IDs to detect and skip duplicate events
  4. Implement Retries: Handle temporary failures gracefully
  5. Monitor Failures: Track webhook delivery failures

Example Handler (Node.js)

const express = require('express');
const app = express();

app.post('/webhooks/chatwoot', express.json(), (req, res) => {
  const event = req.body;
  
  // Respond immediately
  res.status(200).send('OK');
  
  // Process event asynchronously
  processWebhookEvent(event).catch(err => {
    console.error('Webhook processing error:', err);
  });
});

async function processWebhookEvent(event) {
  switch (event.event) {
    case 'message_created':
      await handleNewMessage(event.message);
      break;
    case 'conversation_created':
      await handleNewConversation(event.conversation);
      break;
    // Handle other events...
  }
}

Webhook Delivery

Retry Policy

Chatwoot will retry failed webhook deliveries:
  • Initial retry after 1 minute
  • Subsequent retries with exponential backoff
  • Maximum of 3 retry attempts

Timeouts

Webhook requests timeout after 10 seconds. Ensure your endpoint responds quickly.

Failed Deliveries

Webhooks that consistently fail may be automatically disabled. Monitor your webhook endpoint for errors.

Testing Webhooks

Local Development

Use tools like ngrok to expose your local server:
ngrok http 3000
Use the ngrok URL as your webhook URL:
https://abc123.ngrok.io/webhooks/chatwoot

Webhook Testing Tools

Common Issues

Webhook Not Receiving Events

Troubleshooting:
  1. Verify URL is correct and accessible
  2. Check webhook subscriptions include the event type
  3. Ensure endpoint returns 200 OK quickly
  4. Check firewall and security settings

Duplicate Events

Solution: Use event IDs to detect and skip duplicates:
const processedEvents = new Set();

function handleWebhook(event) {
  if (processedEvents.has(event.id)) {
    return; // Skip duplicate
  }
  processedEvents.add(event.id);
  // Process event...
}

Timeout Errors

Solution: Return 200 OK immediately and process events asynchronously.

Error Responses

Webhook Not Found

{
  "error": "Webhook not found"
}

Invalid URL

{
  "error": "Webhook URL must use HTTPS"
}

Invalid Subscriptions

{
  "error": "Invalid subscription event type"
}

Build docs developers (and LLMs) love