Skip to main content

Overview

Webhooks allow your application to receive real-time notifications when events occur in your Vambe account. Instead of polling for changes, Vambe will send HTTP POST requests to your specified endpoint when events happen.

Common Use Cases

  • Message Notifications: Get notified when customers send messages
  • Ticket Updates: Track when tickets are created, updated, or closed
  • Pipeline Changes: Monitor when contacts move through pipeline stages
  • Contact Events: Know when new contacts are created
  • Integration Sync: Keep external systems in sync with Vambe data

How Webhooks Work

  1. Configure Webhook: Set up a webhook URL in your Vambe account
  2. Events Occur: Customer sends message, ticket created, stage changed, etc.
  3. Vambe Sends Request: HTTP POST sent to your webhook URL
  4. Your Server Responds: Process the event and return 200 OK
  5. Retry Logic: If your server doesn’t respond, Vambe retries

Webhook Management API

Use the webhooks API to manage your webhook configurations programmatically.

Available Endpoints

Check the Automation & Pipelines API tab for webhook management endpoints:
  • Create Webhook: POST /api/webhooks
  • List Webhooks: GET /api/webhooks
  • Get Webhook: GET /api/webhooks/{id}
  • Update Webhook: PUT /api/webhooks/{id}
  • Delete Webhook: DELETE /api/webhooks/{id}
  • Restore Webhook: POST /api/webhooks/{id}/restore
  • Get Topics: GET /api/webhooks/topics
  • Get Webhook Calls: GET /api/webhooks/calls

Event Types (Topics)

Subscribe to specific events to receive notifications:

Message Events

message.received - Customer sends an inbound message
{
  "event": "message.received",
  "timestamp": "2024-09-30T10:00:00.000Z",
  "data": {
    "contact_id": "df980fc8-b6db-4820-bf22-2969482d106d",
    "message_id": "46964423",
    "message_body": "Hello, I need help with my order",
    "platform": "whatsapp",
    "direction": "inbound",
    "contact_name": "Maria Rodriguez",
    "contact_phone": "+56912345678"
  }
}
message.sent - Your team sends an outbound message
{
  "event": "message.sent",
  "timestamp": "2024-09-30T10:01:00.000Z",
  "data": {
    "contact_id": "df980fc8-b6db-4820-bf22-2969482d106d",
    "message_id": "46964424",
    "message_body": "Hi! How can I help you?",
    "platform": "whatsapp",
    "direction": "outbound",
    "sent_by": "agent" // or "ai"
  }
}

Ticket Events

ticket.created - New support ticket opened
{
  "event": "ticket.created",
  "timestamp": "2024-09-30T10:00:00.000Z",
  "data": {
    "ticket_id": "ticket-123",
    "contact_id": "df980fc8-b6db-4820-bf22-2969482d106d",
    "contact_name": "Maria Rodriguez",
    "pipeline_id": "pipeline-456",
    "stage_id": "stage-789",
    "status": "OPEN"
  }
}
ticket.updated - Ticket status or assignment changes ticket.closed - Ticket is resolved/closed

Contact Events

contact.created - New contact added to system contact.updated - Contact information modified

Pipeline Events

stage.changed - Contact moves to different pipeline stage
{
  "event": "stage.changed",
  "timestamp": "2024-09-30T10:05:00.000Z",
  "data": {
    "contact_id": "df980fc8-b6db-4820-bf22-2969482d106d",
    "previous_stage_id": "stage-789",
    "new_stage_id": "stage-890",
    "pipeline_id": "pipeline-456",
    "changed_by": "agent-123"
  }
}

Webhook Payload Structure

When an event occurs, Vambe sends a POST request to your webhook URL:
{
  "event": "message.received",
  "timestamp": "2024-09-30T10:00:00.000Z",
  "data": {
    "contact_id": "df980fc8-b6db-4820-bf22-2969482d106d",
    "message_id": "46964423",
    "message_body": "Hello, I need help",
    "platform": "whatsapp",
    "direction": "inbound"
  }
}

Setting Up Webhooks

1. Create a Webhook Endpoint

Your server must have an endpoint that:
  • Accepts POST requests
  • Returns 200 OK status
  • Responds within 5 seconds
  • Processes events asynchronously (don’t block the response)
// Express.js webhook endpoint
const express = require('express');
const app = express();

app.use(express.json());

app.post('/webhooks/vambe', async (req, res) => {
const event = req.body;

// Immediately return 200 OK
res.status(200).json({ received: true });

// Process event asynchronously
try {
await processVambeEvent(event);
console.log(`βœ… Processed ${event.event}`);
} catch (error) {
console.error(`❌ Error processing ${event.event}:`, error);
}
});

async function processVambeEvent(event) {
switch (event.event) {
case 'message.received':
await handleNewMessage(event.data);
break;
case 'ticket.created':
await handleNewTicket(event.data);
break;
case 'stage.changed':
await handleStageChange(event.data);
break;
default:
console.log('Unknown event:', event.event);
}
}

app.listen(3000);

2. Configure in Vambe

Use the API to create a webhook configuration:
curl --request POST \
  'https://api.vambe.ai/api/webhooks' \
  --header 'Content-Type: application/json' \
  --header 'x-api-key: your_api_key_here' \
  --data-raw '{
    "url": "https://your-domain.com/webhooks/vambe",
    "topics": ["message.received", "ticket.created"],
    "active": true
  }'

3. Test Your Webhook

  • Send a test message to trigger the webhook
  • Check your server logs to verify receipt
  • Ensure you’re returning 200 OK

Security Best Practices

  1. Validate Requests: Verify requests are from Vambe
  2. Use HTTPS: Always use secure endpoints
  3. Verify Signatures: Check webhook signatures if provided
  4. Rate Limiting: Implement rate limiting on your endpoint
  5. Idempotency: Handle duplicate events gracefully

Error Handling

If your webhook endpoint fails to respond:
  • Retry Logic: Vambe will retry failed webhooks
  • Backoff Strategy: Increasing delays between retries
  • Max Retries: After several failures, webhook may be disabled
  • Monitor Logs: Check webhook call logs via API

Real-World Use Cases

Use Case 1: Auto-Response to High-Priority Messages

async function handleNewMessage(data) {
  // If message contains "urgent" keyword
  if (data.message_body.toLowerCase().includes('urgent')) {
    // Tag contact as high priority
    await fetch(
      `https://api.vambe.ai/api/public/channels/whatsapp/${data.contact_phone}/tags`,
      {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'x-api-key': process.env.VAMBE_API_KEY,
        },
        body: JSON.stringify({ tags: ['urgent', 'high-priority'] }),
      },
    );

    // Send notification to team
    await sendSlackAlert(`🚨 Urgent message from ${data.contact_name}`);
  }
}

Use Case 2: CRM Integration on Ticket Creation

async function handleNewTicket(data) {
  // Create deal in external CRM
  await externalCRM.createDeal({
    contact_id: data.contact_id,
    contact_name: data.contact_name,
    source: 'Vambe Chat',
    stage: 'New Lead',
    vambe_ticket_id: data.ticket_id,
  });

  console.log('Deal created in CRM for', data.contact_name);
}

Use Case 3: Update Analytics on Stage Change

async function handleStageChange(data) {
  // Track conversion funnel
  await analytics.track('Stage Changed', {
    contact_id: data.contact_id,
    from_stage: data.previous_stage_id,
    to_stage: data.new_stage_id,
    pipeline: data.pipeline_id,
    timestamp: new Date(),
  });

  // If moved to "Qualified" stage, notify sales team
  if (data.new_stage_id === 'qualified-stage-id') {
    await notifySalesTeam(data.contact_id);
  }
}

Use Case 4: Auto-Assignment Based on Message Content

async function handleNewMessage(data) {
  const messageText = data.message_body.toLowerCase();

  // Route based on message content
  let assigneeId;

  if (messageText.includes('billing') || messageText.includes('payment')) {
    assigneeId = 'billing-team-member-id';
  } else if (messageText.includes('technical') || messageText.includes('bug')) {
    assigneeId = 'tech-support-member-id';
  } else {
    assigneeId = 'general-support-member-id';
  }

  // Auto-assign to appropriate team member
  await fetch(
    `https://api.vambe.ai/api/public/contact/${data.contact_id}/assign-team-member/${assigneeId}`,
    {
      method: 'POST',
      headers: { 'x-api-key': process.env.VAMBE_API_KEY },
    },
  );
}

Webhook Management with API

Create a Webhook

curl --request POST \
  'https://api.vambe.ai/api/webhooks' \
  --header 'Content-Type: application/json' \
  --header 'x-api-key: your_api_key_here' \
  --data-raw '{
    "url": "https://your-domain.com/webhooks/vambe",
    "topics": ["message.received", "ticket.created", "stage.changed"],
    "active": true,
    "description": "Main webhook for production"
  }'

List All Webhooks

curl --request GET \
  'https://api.vambe.ai/api/webhooks' \
  --header 'x-api-key: your_api_key_here'

Update Webhook

curl --request PUT \
  'https://api.vambe.ai/api/webhooks/webhook-id-123' \
  --header 'Content-Type: application/json' \
  --header 'x-api-key: your_api_key_here' \
  --data-raw '{
    "active": false
  }'

Delete Webhook

curl --request DELETE \
  'https://api.vambe.ai/api/webhooks/webhook-id-123' \
  --header 'x-api-key: your_api_key_here'

Get Available Topics

curl --request GET \
  'https://api.vambe.ai/api/webhooks/topics' \
  --header 'x-api-key: your_api_key_here'

View Webhook Call History

curl --request GET \
  'https://api.vambe.ai/api/webhooks/calls' \
  --header 'x-api-key: your_api_key_here'

Debugging Webhooks

Log Everything During Development

app.post('/webhooks/vambe', (req, res) => {
  console.log('=== Webhook Received ===');
  console.log('Headers:', req.headers);
  console.log('Body:', JSON.stringify(req.body, null, 2));
  console.log('Event Type:', req.body.event);
  console.log('========================');

  res.status(200).send('OK');

  // Process after response
  processEvent(req.body);
});

Test Webhook Locally with ngrok

# 1. Start ngrok tunnel
ngrok http 3000

# 2. Use ngrok URL for webhook
# https://abc123.ngrok.io/webhooks/vambe

# 3. Test by triggering events in Vambe
# 4. Watch logs in ngrok dashboard

Common Issues

Issue: Webhook not receiving events
  • βœ… Verify webhook is active (GET /api/webhooks)
  • βœ… Check URL is publicly accessible
  • βœ… Verify subscribed to correct topics
  • βœ… Check firewall/security groups
Issue: Receiving duplicate events
  • βœ… Implement idempotency with event IDs
  • βœ… Track processed events in database
  • βœ… Return 200 OK even if duplicate
Issue: Webhook timing out
  • βœ… Process events asynchronously
  • βœ… Return 200 OK immediately
  • βœ… Don’t wait for external API calls

Advanced Patterns

Idempotency Pattern

const processedEvents = new Set();

app.post('/webhooks/vambe', async (req, res) => {
  const event = req.body;
  const eventId = `${event.event}-${event.timestamp}-${event.data.contact_id}`;

  // Check if already processed
  if (processedEvents.has(eventId)) {
    console.log('Duplicate event, skipping');
    return res.status(200).send('OK');
  }

  // Mark as processed
  processedEvents.add(eventId);

  res.status(200).send('OK');

  // Process event
  await processVambeEvent(event);
});

Queue-Based Processing

// Use a queue for reliable processing
const Queue = require('bull');
const webhookQueue = new Queue('vambe-webhooks');

app.post('/webhooks/vambe', async (req, res) => {
  // Add to queue
  await webhookQueue.add(req.body);

  // Return immediately
  res.status(200).send('OK');
});

// Process from queue
webhookQueue.process(async (job) => {
  const event = job.data;
  await processVambeEvent(event);
});

Event Filtering

// Only process certain events or conditions
app.post('/webhooks/vambe', async (req, res) => {
  const event = req.body;

  res.status(200).send('OK');

  // Filter: Only process messages with "order" keyword
  if (
    event.event === 'message.received' &&
    event.data.message_body.toLowerCase().includes('order')
  ) {
    await processOrderInquiry(event.data);
  }

  // Filter: Only process tickets in specific pipeline
  if (
    event.event === 'ticket.created' &&
    event.data.pipeline_id === 'sales-pipeline-id'
  ) {
    await processSalesTicket(event.data);
  }
});