Chatwoot + Webhooks: Build Custom Chatbots and Integrate with Your CRM

Chatwoot + Webhooks: Build Custom Chatbots and Integrate with Your CRM

Customer support platforms are usually closed boxes. Messages come in, agents respond, tickets get closed. But what if you need to sync conversations to your CRM? Or trigger automated responses based on customer data? Or route tickets based on custom business logic?

Chatwoot solves this with webhooks and a comprehensive API. Every conversation event can trigger external systems. Every message can be enriched with data from your existing tools.

How Chatwoot Webhooks Work

Chatwoot fires webhooks for key events: new conversations, incoming messages, conversation status changes, and more. Configure them in Settings → Integrations → Webhooks.

Each webhook receives a JSON payload with the event type and relevant data:

{
  "event": "message_created",
  "id": 1234,
  "content": "I need help with my order",
  "message_type": "incoming",
  "conversation": {
    "id": 567,
    "status": "open",
    "contact": {
      "id": 89,
      "email": "customer@example.com",
      "name": "Jane Smith"
    }
  },
  "account": {
    "id": 1
  }
}

Your endpoint receives this payload and can respond with actions, sync data, or trigger workflows.

Building a Simple Webhook Handler

Here's a Node.js endpoint that receives Chatwoot webhooks and logs them:

const express = require('express');
const app = express();
app.use(express.json());

app.post('/chatwoot-webhook', (req, res) => {
  const { event, content, conversation } = req.body;

  console.log(`Event: ${event}`);
  console.log(`Message: ${content}`);
  console.log(`Contact: ${conversation?.contact?.email}`);

  // Acknowledge receipt
  res.status(200).json({ received: true });
});

app.listen(3000);

In production, you'll want to verify the webhook signature. Chatwoot signs payloads with HMAC-SHA256 using your webhook secret:

const crypto = require('crypto');

function verifySignature(payload, signature, secret) {
  const hmac = crypto.createHmac('sha256', secret);
  const digest = hmac.update(JSON.stringify(payload)).digest('hex');
  return signature === digest;
}

app.post('/chatwoot-webhook', (req, res) => {
  const signature = req.headers['x-chatwoot-signature'];

  if (!verifySignature(req.body, signature, process.env.WEBHOOK_SECRET)) {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  // Process the webhook...
});

Syncing Conversations to Your CRM

A common integration: push new conversations to your CRM as leads. When a conversation starts, create a contact record with the customer details:

app.post('/chatwoot-webhook', async (req, res) => {
  const { event, conversation } = req.body;

  if (event === 'conversation_created') {
    const contact = conversation.contact;

    // Create lead in your CRM
    await fetch('https://your-crm.com/api/leads', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${process.env.CRM_API_KEY}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        name: contact.name,
        email: contact.email,
        phone: contact.phone_number,
        source: 'Chatwoot',
        conversation_url: `https://your-chatwoot.com/app/accounts/1/conversations/${conversation.id}`
      })
    });
  }

  res.status(200).json({ synced: true });
});

Now every new customer conversation automatically appears in your sales pipeline.

Building Automated Bot Responses

Chatwoot's API lets you send messages programmatically. Combine this with webhooks to build custom bots:

const CHATWOOT_API = 'https://your-chatwoot.com/api/v1';
const API_KEY = process.env.CHATWOOT_API_KEY;

async function sendMessage(accountId, conversationId, content) {
  await fetch(
    `${CHATWOOT_API}/accounts/${accountId}/conversations/${conversationId}/messages`,
    {
      method: 'POST',
      headers: {
        'api_access_token': API_KEY,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        content,
        message_type: 'outgoing',
        private: false
      })
    }
  );
}

app.post('/chatwoot-webhook', async (req, res) => {
  const { event, content, conversation, account } = req.body;

  if (event === 'message_created' && req.body.message_type === 'incoming') {
    // Auto-respond to common questions
    if (content.toLowerCase().includes('hours')) {
      await sendMessage(
        account.id,
        conversation.id,
        "We're open Monday-Friday, 9 AM - 6 PM EST. How can I help you today?"
      );
    }

    if (content.toLowerCase().includes('pricing')) {
      await sendMessage(
        account.id,
        conversation.id,
        "You can find our pricing at https://yoursite.com/pricing. Would you like me to connect you with sales?"
      );
    }
  }

  res.status(200).json({ processed: true });
});

This simple bot handles FAQs instantly, reducing response time for common queries.

Routing Based on Custom Logic

Route conversations to specific teams or agents based on external data. Check your database, verify subscription status, or apply any business logic:

app.post('/chatwoot-webhook', async (req, res) => {
  const { event, conversation } = req.body;

  if (event === 'conversation_created') {
    const customerEmail = conversation.contact.email;

    // Check subscription tier in your database
    const customer = await db.query(
      'SELECT subscription_tier FROM customers WHERE email = $1',
      [customerEmail]
    );

    // Assign to appropriate team
    let teamId;
    if (customer?.subscription_tier === 'enterprise') {
      teamId = ENTERPRISE_TEAM_ID;  // Priority support
    } else {
      teamId = STANDARD_TEAM_ID;
    }

    // Update conversation assignment via API
    await fetch(
      `${CHATWOOT_API}/accounts/${conversation.account_id}/conversations/${conversation.id}/assignments`,
      {
        method: 'POST',
        headers: { 'api_access_token': API_KEY },
        body: JSON.stringify({ team_id: teamId })
      }
    );
  }

  res.status(200).json({ routed: true });
});

Enterprise customers automatically get priority routing. No manual triage needed.

Available Webhook Events

Chatwoot triggers webhooks for these events:

  • conversation_created - New conversation started
  • conversation_status_changed - Status update (open, resolved, pending)
  • conversation_updated - Any conversation change
  • message_created - New message (incoming or outgoing)
  • message_updated - Message edited
  • webwidget_triggered - Chat widget interaction

Subscribe to the events relevant to your integration. Less noise, more signal.

Deploy on Elestio

Running Chatwoot with proper webhook infrastructure means managing SSL, database backups, and keeping the application updated. Elestio handles this out of the box.

Deploy Chatwoot in minutes, configure your webhooks, and start building integrations. Your customer conversations become part of your broader system instead of sitting in a silo.

Ready to connect your support platform to everything else? Deploy Chatwoot on Elestio and start building.

Thanks for reading!