Back to blog
webhooksintegrationsopenclawautomationtutorial

OpenClaw Webhooks: Connect Your Agent to Any App

How to use OpenClaw webhooks to trigger external actions — CRM updates, ticket creation, Slack notifications, and more. Includes example payloads and real use cases.

By ClawPort Team

OpenClaw webhooks let your AI agent do more than just chat — they let conversations trigger real actions in other systems. A user reports a bug → Jira ticket created. A lead qualifies → HubSpot contact updated. A customer says they want to cancel → Slack message to the CS team.

This post covers how webhooks work in OpenClaw, the event types available, example payloads, and practical integration patterns.

How OpenClaw Webhooks Work

When configured, OpenClaw sends an HTTP POST request to your endpoint whenever a defined event occurs. Your server receives the payload and does whatever it needs to — create a record, send a notification, trigger a workflow.

User message → OpenClaw processes → Webhook event fires → Your endpoint → Your action

The latency from event to webhook delivery is typically under 200ms.

Configuring Webhooks

In your OpenClaw config or dashboard, define your webhook endpoints:

webhooks:
  - name: "crm-lead-capture"
    url: "https://your-app.com/webhooks/openclaw"
    secret: "${WEBHOOK_SECRET}"
    events:
      - "conversation.started"
      - "lead.qualified"
      - "message.received"
    headers:
      Authorization: "Bearer ${CRM_API_KEY}"

The secret is used to sign the payload so you can verify it's actually from OpenClaw.

Verifying Webhook Signatures

Always verify incoming webhooks. OpenClaw signs every payload using HMAC-SHA256:

import crypto from 'crypto';

function verifyWebhook(payload, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(JSON.stringify(payload))
    .digest('hex');
  
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signature)
  );
}

// In your Express route:
app.post('/webhooks/openclaw', (req, res) => {
  const sig = req.headers['x-openclaw-signature'];
  
  if (!verifyWebhook(req.body, sig, process.env.WEBHOOK_SECRET)) {
    return res.status(401).json({ error: 'Invalid signature' });
  }
  
  // Handle the event
  handleEvent(req.body);
  res.status(200).json({ received: true });
});

Respond with 200 quickly. If your handler takes more than 5 seconds, OpenClaw will retry.

Available Event Types

EventWhen It Fires
conversation.startedNew conversation begins
conversation.endedConversation ends (timeout or user closes)
message.receivedUser sends any message
message.sentAgent sends a response
lead.qualifiedAgent marks conversation as qualified lead
intent.detectedSpecific intent is detected (configurable)
handoff.requestedUser requests human agent
custom.triggeredAgent explicitly triggers a custom event

Example Payloads

conversation.started

{
  "event": "conversation.started",
  "timestamp": "2026-03-10T14:22:01Z",
  "conversation_id": "conv_abc123",
  "channel": "telegram",
  "user": {
    "id": "user_xyz789",
    "username": "johndoe",
    "first_name": "John"
  },
  "metadata": {
    "referrer": "https://yoursite.com/pricing",
    "utm_source": "google",
    "utm_campaign": "spring-2026"
  }
}

lead.qualified

{
  "event": "lead.qualified",
  "timestamp": "2026-03-10T14:25:44Z",
  "conversation_id": "conv_abc123",
  "user": {
    "id": "user_xyz789",
    "email": "[email protected]",
    "company": "Acme Corp"
  },
  "lead_data": {
    "name": "John Doe",
    "email": "[email protected]",
    "company": "Acme Corp",
    "team_size": "50-200",
    "use_case": "customer support automation",
    "urgency": "high",
    "score": 87
  }
}

custom.triggered

{
  "event": "custom.triggered",
  "event_name": "cancellation_intent",
  "timestamp": "2026-03-10T15:01:22Z",
  "conversation_id": "conv_def456",
  "trigger_message": "I want to cancel my subscription",
  "context": {
    "last_5_messages": ["..."],
    "sentiment": "frustrated",
    "account_age_days": 45
  }
}

Use Case 1: CRM Lead Capture

When a lead qualifies, create a contact in your CRM:

async function handleLeadQualified(data) {
  const { lead_data, conversation_id } = data;
  
  // HubSpot example
  await fetch('https://api.hubapi.com/crm/v3/objects/contacts', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.HUBSPOT_TOKEN}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      properties: {
        email: lead_data.email,
        firstname: lead_data.name.split(' ')[0],
        lastname: lead_data.name.split(' ')[1],
        company: lead_data.company,
        lead_source: 'openclaw_chatbot',
        openclaw_conversation_id: conversation_id,
        hs_lead_status: 'NEW'
      }
    })
  });
}

Use Case 2: Slack Alert on Cancellation Intent

async function handleCancellationIntent(data) {
  const { context, conversation_id } = data;
  
  await fetch(process.env.SLACK_WEBHOOK_URL, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      text: `🚨 Cancellation intent detected`,
      blocks: [
        {
          type: 'section',
          text: {
            type: 'mrkdwn',
            text: `*Cancellation risk* — ${context.account_age_days} day old account\nSentiment: ${context.sentiment}\n<https://app.yourcrm.com/conv/${conversation_id}|View conversation>`
          }
        }
      ]
    })
  });
}

Use Case 3: Jira Ticket on Bug Report

async function handleBugReport(data) {
  const { trigger_message, user, conversation_id } = data;
  
  await fetch(`${process.env.JIRA_URL}/rest/api/3/issue`, {
    method: 'POST',
    headers: {
      'Authorization': `Basic ${Buffer.from(`${process.env.JIRA_EMAIL}:${process.env.JIRA_TOKEN}`).toString('base64')}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      fields: {
        project: { key: 'BUG' },
        summary: `User report: ${trigger_message.substring(0, 80)}`,
        description: {
          type: 'doc',
          version: 1,
          content: [{
            type: 'paragraph',
            content: [{
              type: 'text',
              text: `Reported via chatbot. Conversation: ${conversation_id}\nUser: ${user.email}`
            }]
          }]
        },
        issuetype: { name: 'Bug' },
        priority: { name: 'Medium' }
      }
    })
  });
}

Triggering Custom Events from the Agent

You can instruct your agent to trigger specific events. In your SOUL.md:

## Actions

When a user expresses intent to cancel:
- Acknowledge their concern
- Offer to connect them with the retention team
- Trigger event: cancellation_intent

When a user reports a bug:
- Collect: description, steps to reproduce, severity
- Trigger event: bug_report

When a user asks for enterprise pricing:
- Collect: company name, team size, use case
- Trigger event: enterprise_lead

OpenClaw parses these instructions and fires the named events when appropriate.

Handling Retries

OpenClaw retries failed webhooks with exponential backoff:

  • 1st retry: 30 seconds
  • 2nd retry: 5 minutes
  • 3rd retry: 30 minutes
  • After that: marked as failed

Your endpoint should be idempotent — use the conversation_id as a deduplication key.

Self-Hosting Webhook Infrastructure

If you're self-hosting OpenClaw, you also own the webhook reliability problem:

  • Queue for retry management (Redis + BullMQ, or similar)
  • Delivery logs and failure alerting
  • Signature key rotation without dropping in-flight events
  • Scaling to handle webhook storms (if many conversations qualify simultaneously)

This is solvable, but it's 2-3 days of engineering work to do properly.

ClawPort Handles Webhook Delivery

ClawPort manages webhook delivery reliability for you — retries, delivery logs, and failure alerts are all in the dashboard. You configure your endpoint URLs, define your events, and watch deliveries in real time.

The $10/month plan includes full webhook support. Seven-day free trial at clawport.io.

Ready to deploy your AI agent?

Get started with ClawPort in 60 seconds. No credit card required.

Get Started Free