Documentation Index
Fetch the complete documentation index at: https://docs.vambe.me/llms.txt
Use this file to discover all available pages before exploring further.
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
- Configure Webhook: Set up a webhook URL in your Vambe account
- Events Occur: Customer sends message, ticket created, stage changed, etc.
- Vambe Sends Request: HTTP POST sent to your webhook URL
- Your Server Responds: Process the event and return 200 OK
- 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
{
"aiContactId": "38f37371-aa05-477a-b6c1-d161417@ed8d",
"data": {
"fromNumber": "56998389641",
"message": "Ok",
"messageId": "0f475695-ec1b-4ca8-b03d-a1c8d066b999",
"messageType": "text",
"name": "Waita",
"stageId": "3abb9d28-141c-45ab-87f8-52919caf63a7",
"toNumber": "56961210530"
},
"type": "message.received"
}
message.sent - Your team sends an outbound message
{
"aiContactId": "e8e08253-aa12-4f32-b4c9-b1fbd180c26b",
"data": {
"error_details": [
{
"code": 131026,
"error_data": {
"details": "..."
},
"message": "Message undeliverable",
"title": "Message undeliverable"
}
],
"fromNumber": "56961210530",
"message": "¡Listo Eduardo! ✨ Aquí tienes tu propuesta definitiva...",
"messageId": "467ae215-f27a-4c79-ac44-f46fd8a3b28d",
"messageType": "template",
"name": "Eduardo Mella Navarrete",
"status": "failed",
"toNumber": "56986874408"
},
"type": "message.sent"
}
Note: The error_details field in message.sent is optional and only present when there are delivery errors.
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.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:
{
"aiContactId": "38f37371-aa05-477a-b6c1-d161417@ed8d",
"data": {
"fromNumber": "56998389641",
"message": "Hello, I need help",
"messageId": "46964423",
"messageType": "text",
"name": "Maria Rodriguez",
"stageId": "3abb9d28-141c-45ab-87f8-52919caf63a7",
"toNumber": "56961210530"
},
"type": "message.received"
}
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.type}`);
} catch (error) {
console.error(`❌ Error processing ${event.type}:`, error);
}
});
async function processVambeEvent(event) {
switch (event.type) {
case 'message.received':
await handleNewMessage(event.data);
break;
case 'message.sent':
await handleMessageSent(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.type);
}
}
app.listen(3000);
Use the API to create a webhook configuration:
curl --request POST \
'https://api.vambe.me/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
- Validate Requests: Verify requests are from Vambe
- Use HTTPS: Always use secure endpoints
- Verify Signatures: Check webhook signatures if provided
- Rate Limiting: Implement rate limiting on your endpoint
- 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.toLowerCase().includes('urgent')) {
// Tag contact as high priority
await fetch(
`https://api.vambe.me/api/public/channels/whatsapp/${data.fromNumber}/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.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.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
// Note: Use the aiContactId from the parent webhook event
await fetch(
`https://api.vambe.me/api/public/contact/{aiContactId}/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.me/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.me/api/webhooks' \
--header 'x-api-key: your_api_key_here'
Update Webhook
curl --request PUT \
'https://api.vambe.me/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.me/api/webhooks/webhook-id-123' \
--header 'x-api-key: your_api_key_here'
Get Available Topics
curl --request GET \
'https://api.vambe.me/api/webhooks/topics' \
--header 'x-api-key: your_api_key_here'
View Webhook Call History
curl --request GET \
'https://api.vambe.me/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.type);
console.log('AI Contact ID:', req.body.aiContactId);
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.type}-${event.aiContactId}-${event.data.messageId}`;
// 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.type === 'message.received' &&
event.data.message.toLowerCase().includes('order')
) {
await processOrderInquiry(event.data);
}
// Filter: Only process tickets in specific pipeline
if (
event.type === 'ticket.created' &&
event.data.pipeline_id === 'sales-pipeline-id'
) {
await processSalesTicket(event.data);
}
});