Webhooks
Receive real-time notifications about conversation session events through webhooks.
Overview
LiveTok webhooks allow you to subscribe to session events and receive HTTP POST notifications when conversations begin or end. Build real-time integrations, automate workflows, and track conversation lifecycle.
Webhook Basics
How Webhooks Work
1. Session event occurs in LiveTok (conversation begins/ends)
2. LiveTok sends HTTP POST to your endpoint
3. Your server receives and processes the event
4. Your server returns 200 OK status
5. If no 200 received, LiveTok retries
```text
### Setting Up Webhooks
**Via Dashboard:**
1. Go to **Settings** > **Webhooks**
2. Click **Add Webhook**
3. Enter your endpoint URL
4. Select events to subscribe to: `session.begin` and `session.end`
5. Save webhook
**Via API:**
```bash
curl https://api.livetok.ai/v1/webhooks \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://yourapp.com/webhooks/livetok",
"events": ["session.begin", "session.end"],
"description": "Session tracking webhook"
}'
```text
---
## Available Events
### Session Begin
Triggered when a new conversation session begins.
**session.begin**
```json
{
"type": "session.begin",
"id": "evt_abc123",
"created_at": "2024-03-15T10:30:00Z",
"livemode": true,
"data": {
"session_id": "sess_xyz789",
"conversation_id": "conv_abc123",
"channel": "whatsapp",
"customer": {
"id": "cust_def456",
"name": "John Doe",
"phone": "+1234567890",
"email": "john@example.com"
},
"agent_type": "ai_agent",
"started_at": "2024-03-15T10:30:00Z",
"metadata": {
"source": "organic",
"campaign": null
}
}
}
```text
**Fields:**
- `session_id` - Unique identifier for this session
- `conversation_id` - ID of the conversation
- `channel` - Communication channel (whatsapp, telephony, web)
- `customer` - Customer information
- `agent_type` - Type of agent handling the session (ai_agent, human_agent)
- `started_at` - Timestamp when session began
- `metadata` - Additional custom data
### Session End
Triggered when a conversation session ends.
**session.end**
```json
{
"type": "session.end",
"id": "evt_ghi789",
"created_at": "2024-03-15T10:45:00Z",
"livemode": true,
"data": {
"session_id": "sess_xyz789",
"conversation_id": "conv_abc123",
"channel": "whatsapp",
"customer": {
"id": "cust_def456",
"name": "John Doe",
"phone": "+1234567890",
"email": "john@example.com"
},
"agent_type": "ai_agent",
"started_at": "2024-03-15T10:30:00Z",
"ended_at": "2024-03-15T10:45:00Z",
"duration_seconds": 900,
"message_count": 12,
"resolution_status": "resolved",
"satisfaction_score": 5,
"metadata": {
"source": "organic",
"campaign": null
}
}
}
```text
**Fields:**
- `session_id` - Unique identifier for this session
- `conversation_id` - ID of the conversation
- `channel` - Communication channel
- `customer` - Customer information
- `agent_type` - Type of agent that handled the session
- `started_at` - Timestamp when session began
- `ended_at` - Timestamp when session ended
- `duration_seconds` - Total session duration in seconds
- `message_count` - Number of messages exchanged
- `resolution_status` - Final status (resolved, pending, transferred)
- `satisfaction_score` - Customer satisfaction rating (1-5, if available)
- `metadata` - Additional custom data
---
## Webhook Payload Structure
### Standard Format
All webhooks follow this structure:
```json
{
"id": "evt_unique_id",
"type": "event.name",
"created_at": "2024-03-15T10:30:00Z",
"livemode": true,
"data": {
// Event-specific data
}
}
```text
### Headers
LiveTok includes these headers:
```text
Content-Type: application/json
User-Agent: LiveTok-Webhook/1.0
X-LiveTok-Event: session.begin
X-LiveTok-Signature: sha256=abc123...
X-LiveTok-Delivery: unique_delivery_id
```text
---
## Security
### Verifying Webhooks
Always verify webhook signatures to ensure requests are from LiveTok:
```javascript
const crypto = require('crypto');
function verifyWebhookSignature(payload, signature, secret) {
const hmac = crypto.createHmac('sha256', secret);
const digest = 'sha256=' + hmac.update(payload).digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(digest)
);
}
// Express.js example
app.post('/webhooks/livetok', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-livetok-signature'];
const payload = req.body;
if (!verifyWebhookSignature(payload, signature, process.env.WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}
// Process webhook
const event = JSON.parse(payload);
handleEvent(event);
res.status(200).send('OK');
});
```text
**Python Example:**
```python
import hmac
import hashlib
def verify_webhook_signature(payload, signature, secret):
expected = 'sha256=' + hmac.new(
secret.encode(),
payload,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected)
```text
### Webhook Secret
Get your webhook secret:
1. Go to **Settings** > **Webhooks**
2. Click on your webhook
3. View **Signing Secret**
4. Store securely in environment variables
---
## Handling Webhooks
### Basic Handler
```javascript
app.post('/webhooks/livetok', async (req, res) => {
const event = req.body;
// Respond quickly
res.status(200).send('OK');
// Process asynchronously
processEvent(event).catch(err => {
console.error('Error processing webhook:', err);
});
});
async function processEvent(event) {
switch (event.type) {
case 'session.begin':
await handleSessionBegin(event.data);
break;
case 'session.end':
await handleSessionEnd(event.data);
break;
default:
console.log('Unhandled event type:', event.type);
}
}
```text
### Event Processing
**Best Practices:**
1. Respond with 200 immediately
2. Process events asynchronously
3. Implement idempotency
4. Handle duplicate events
5. Log all webhooks
6. Monitor failures
**Idempotent Processing:**
```javascript
const processedEvents = new Set();
async function processEvent(event) {
// Check if already processed
if (processedEvents.has(event.id)) {
console.log('Event already processed:', event.id);
return;
}
// Process event
await handleEvent(event);
// Mark as processed
processedEvents.add(event.id);
// In production, use database instead of in-memory Set
}
```text
---
## Retry Logic
### Automatic Retries
LiveTok automatically retries failed webhooks:
**Retry Schedule:**
```text
Immediately
After 5 minutes
After 15 minutes
After 1 hour
After 3 hours
After 6 hours
After 12 hours
After 24 hours
```text
**Retry Conditions:**
- HTTP status != 200
- Connection timeout (10 seconds)
- Connection error
**Retry Headers:**
```text
X-LiveTok-Delivery-Attempt: 3
X-LiveTok-First-Attempt: 2024-03-15T10:30:00Z
```text
### Handling Retries
```javascript
app.post('/webhooks/livetok', async (req, res) => {
const event = req.body;
const attempt = req.headers['x-livetok-delivery-attempt'];
// Check for duplicate processing
const alreadyProcessed = await checkIfProcessed(event.id);
if (alreadyProcessed) {
console.log(`Event ${event.id} already processed (attempt ${attempt})`);
return res.status(200).send('OK');
}
// Process event
try {
await processEvent(event);
await markAsProcessed(event.id);
res.status(200).send('OK');
} catch (error) {
console.error('Error processing webhook:', error);
// Return 200 to stop retries if it's a permanent error
if (error.isPermanent) {
res.status(200).send('Permanent error');
} else {
// Return 500 to trigger retry
res.status(500).send('Temporary error');
}
}
});
```text
---
## Use Cases
### Track Session Analytics
```javascript
async function handleSessionEnd(data) {
// Send to analytics platform
await analytics.track({
event: 'Session Completed',
userId: data.customer.id,
properties: {
session_id: data.session_id,
conversation_id: data.conversation_id,
channel: data.channel,
duration_seconds: data.duration_seconds,
message_count: data.message_count,
resolution_status: data.resolution_status,
satisfaction_score: data.satisfaction_score
}
});
// Update metrics dashboard
await metrics.increment('sessions_completed');
await metrics.timing('session_duration', data.duration_seconds);
}
```text
### Trigger Workflows
```javascript
async function handleSessionBegin(data) {
// Check if VIP customer
if (data.customer.segment === 'vip') {
// Notify VIP support team
await slack.chat.postMessage({
channel: '#vip-support',
text: `New VIP session started`,
blocks: [
{
type: 'section',
text: {
type: 'mrkdwn',
text: `*VIP Session Started*\nCustomer: ${data.customer.name}\nChannel: ${data.channel}`
}
}
]
});
}
// Log session start
await database.sessions.create({
session_id: data.session_id,
conversation_id: data.conversation_id,
customer_id: data.customer.id,
started_at: data.started_at,
channel: data.channel
});
}
```text
### Sync to External Systems
```javascript
async function handleSessionEnd(data) {
// Sync to CRM
await crm.activities.create({
contact_id: data.customer.id,
type: 'conversation',
channel: data.channel,
duration: data.duration_seconds,
status: data.resolution_status,
satisfaction_score: data.satisfaction_score,
timestamp: data.ended_at
});
// Update customer profile
await crm.contacts.update(data.customer.id, {
last_contact_date: data.ended_at,
total_sessions: data.customer.total_conversations + 1
});
}
```text
---
## Testing Webhooks
### Local Testing
**Using ngrok:**
```bash
# Start your local server
node server.js
# In another terminal, expose it
ngrok http 3000
# Use the ngrok URL in your webhook settings
https://abc123.ngrok.io/webhooks/livetok
```text
### Test Events
Send test events from dashboard:
1. Go to **Settings** > **Webhooks**
2. Click on your webhook
3. Click **Send Test Event**
4. Select event type (session.begin or session.end)
5. View delivery status
**Via API:**
```bash
curl https://api.livetok.ai/v1/webhooks/whook_123/test \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{"event_type": "session.begin"}'
```text
### Monitoring Deliveries
View webhook delivery history:
```bash
# Get recent deliveries
curl https://api.livetok.ai/v1/webhooks/whook_123/deliveries \
-H "Authorization: Bearer YOUR_API_KEY"
```text
**Response:**
```json
{
"deliveries": [
{
"id": "del_abc123",
"event_id": "evt_xyz789",
"event_type": "session.begin",
"url": "https://yourapp.com/webhooks",
"status": "succeeded",
"response_code": 200,
"attempts": 1,
"created_at": "2024-03-15T10:30:00Z",
"completed_at": "2024-03-15T10:30:01Z"
}
]
}
```text
---
## Webhook Management
### List Webhooks
```bash
curl https://api.livetok.ai/v1/webhooks \
-H "Authorization: Bearer YOUR_API_KEY"
```text
### Update Webhook
```bash
curl https://api.livetok.ai/v1/webhooks/whook_123 \
-H "Authorization: Bearer YOUR_API_KEY" \
-X PATCH \
-d '{"events": ["session.begin", "session.end"]}'
```text
### Delete Webhook
```bash
curl https://api.livetok.ai/v1/webhooks/whook_123 \
-H "Authorization: Bearer YOUR_API_KEY" \
-X DELETE
```text
### Disable Webhook
```bash
curl https://api.livetok.ai/v1/webhooks/whook_123 \
-H "Authorization: Bearer YOUR_API_KEY" \
-X PATCH \
-d '{"enabled": false}'
```text
---
## Troubleshooting
### Common Issues
**Webhook not receiving events:**
- Check URL is publicly accessible
- Verify webhook is enabled
- Ensure events are subscribed (session.begin, session.end)
- Check firewall/security settings
**Signature verification failing:**
- Using correct webhook secret
- Using raw request body (not parsed JSON)
- Secret not exposed in logs
- Signature header name is correct
**High failure rate:**
- Server responding slowly (>10s timeout)
- Server returning non-200 status
- Connection issues
- Processing errors
### Debug Mode
Enable debug logging:
```javascript
app.post('/webhooks/livetok', (req, res) => {
console.log('Webhook received:', {
event_id: req.body.id,
event_type: req.body.type,
signature: req.headers['x-livetok-signature'],
attempt: req.headers['x-livetok-delivery-attempt']
});
// Your processing logic
});
```text
---
## Best Practices
### Performance
**Do:**
- Respond with 200 immediately
- Process asynchronously in background
- Use message queue for processing
- Implement timeouts
- Monitor processing times
**Don't:**
- Block response waiting for processing
- Make synchronous external calls
- Process in webhook handler
- Ignore timeouts
### Reliability
**Do:**
- Implement idempotency
- Handle duplicate events
- Log all webhooks
- Monitor failures
- Set up alerts
- Test error scenarios
**Don't:**
- Process same event multiple times
- Ignore delivery failures
- Skip error handling
- Forget to test
### Security
**Do:**
- Always verify signatures
- Use HTTPS endpoints
- Store secrets securely
- Rotate secrets regularly
- Limit webhook access
- Monitor for anomalies
**Don't:**
- Skip signature verification
- Use HTTP (non-secure)
- Hardcode secrets
- Expose secrets in logs
- Allow public access
---
## Next Steps
- [Explore API Documentation →](/developers/api)
- [View Integration Examples →](/integrations)
- [Visit Support Page →](/support)
---
## Need Help?
- Chat with our AI Agent (knows webhooks!)
- Email developers@livetok.ai
- Check our [Support page](/support)