Product
Webhooks & Events
Receive real-time notifications when events occur in your Twig AI organization
TL;DR
Receive real-time notifications when events occur in your Twig AI organization. Webhooks are HTTP callbacks that notify your application when specific events happen. Instead of polling for changes, Twig AI pushes updates to your server.
Key Takeaways
- What are Webhooks?
- Supported Events
- Setting Up Webhooks
- Webhook Payload
- Receiving Webhooks
- Security
Receive real-time notifications when events occur in your Twig AI organization.
What are Webhooks?
Webhooks are HTTP callbacks that notify your application when specific events happen. Instead of polling for changes, Twig AI pushes updates to your server.
Benefits:
- Real-time notifications
- No polling required
- Efficient resource usage
- Event-driven architecture
Supported Events
Interaction Events
| Event | Description | Payload |
|---|---|---|
interaction.created | New question asked | Interaction object |
interaction.completed | Response generated | Interaction + response |
interaction.feedback | User provided feedback | Interaction + feedback |
Agent Events
| Event | Description | Payload |
|---|---|---|
agent.created | New agent created | Agent object |
agent.updated | Agent configuration changed | Agent object + changes |
agent.deleted | Agent removed | Agent ID |
Data Source Events
| Event | Description | Payload |
|---|---|---|
datasource.created | New data source added | DataSource object |
datasource.processed | Processing completed | DataSource + stats |
datasource.failed | Processing failed | DataSource + error |
User Events
| Event | Description | Payload |
|---|---|---|
user.created | New user added | User object |
user.updated | User details changed | User object + changes |
Setting Up Webhooks
Via UI
- Navigate to Settings → Webhooks
- Click Create Webhook
- Configure:
- URL: Your endpoint (must be HTTPS)
- Events: Select events to receive
- Secret: Auto-generated signing secret
- Click Create
- Test webhook
- Save
Via API
curl -X POST https://api.twig.so/api/webhooks \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-server.com/webhooks/twig",
"events": [
"interaction.completed",
"agent.updated"
],
"description": "Production webhook"
}'
Response:
{
"id": "wh_abc123",
"url": "https://your-server.com/webhooks/twig",
"events": ["interaction.completed", "agent.updated"],
"secret": "whsec_xyz789...",
"status": "active",
"createdAt": "2024-01-15T10:00:00Z"
}
Webhook Payload
Standard Format
All webhooks follow this structure:
{
"id": "evt_abc123",
"type": "interaction.completed",
"timestamp": "2024-01-15T10:30:00Z",
"data": {
// Event-specific data
},
"organizationId": "org-123"
}
Event Examples
Interaction Completed:
{
"id": "evt_001",
"type": "interaction.completed",
"timestamp": "2024-01-15T10:30:00Z",
"data": {
"interaction": {
"id": "int-456",
"prompt": "What is your refund policy?",
"response": "Our refund policy allows...",
"agentId": "agent-123",
"userId": "user-789",
"sources": [...]
}
},
"organizationId": "org-123"
}
Data Source Processed:
{
"id": "evt_002",
"type": "datasource.processed",
"timestamp": "2024-01-15T11:00:00Z",
"data": {
"dataSource": {
"id": "ds-101",
"name": "Product Docs",
"type": "WEBSITE",
"status": "COMPLETED",
"stats": {
"documentsProcessed": 245,
"tokensProcessed": 125000,
"duration": 180
}
}
},
"organizationId": "org-123"
}
Receiving Webhooks
Endpoint Implementation
Node.js/Express:
const express = require('express');
const crypto = require('crypto');
const app = express();
app.post('/webhooks/twig', express.raw({type: 'application/json'}), (req, res) => {
const signature = req.headers['x-twig-signature'];
const payload = req.body;
// Verify signature
if (!verifySignature(payload, signature)) {
return res.status(401).send('Invalid signature');
}
const event = JSON.parse(payload);
// Handle event
switch(event.type) {
case 'interaction.completed':
handleInteraction(event.data);
break;
case 'agent.updated':
handleAgentUpdate(event.data);
break;
// Add more handlers
}
res.status(200).send('OK');
});
function verifySignature(payload, signature) {
const secret = process.env.WEBHOOK_SECRET;
const expected = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
Python/Flask:
from flask import Flask, request
import hmac
import hashlib
app = Flask(__name__)
@app.route('/webhooks/twig', methods=['POST'])
def webhook():
signature = request.headers.get('X-Twig-Signature')
payload = request.get_data()
# Verify signature
if not verify_signature(payload, signature):
return 'Invalid signature', 401
event = request.get_json()
# Handle event
if event['type'] == 'interaction.completed':
handle_interaction(event['data'])
elif event['type'] == 'agent.updated':
handle_agent_update(event['data'])
return 'OK', 200
def verify_signature(payload, signature):
secret = os.environ['WEBHOOK_SECRET'].encode()
expected = hmac.new(secret, payload, hashlib.sha256).hexdigest()
return hmac.compare_digest(signature, expected)
Security
Signature Verification
Always verify webhook signatures:
import crypto from 'crypto';
function verifyWebhookSignature(
payload: string,
signature: string,
secret: string
): boolean {
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
}
HTTPS Required
- All webhook URLs must use HTTPS
- Valid SSL certificate required
- Self-signed certificates not accepted
IP Whitelisting
Webhook requests come from:
34.123.45.67/32
52.89.123.45/32
Add to firewall whitelist if needed.
Best Practices
1. Respond Quickly
✅ Return 200 OK immediately ✅ Process asynchronously ✅ Use job queue for heavy processing
app.post('/webhooks/twig', async (req, res) => {
// Verify signature
if (!verifySignature(req.body, req.headers['x-twig-signature'])) {
return res.status(401).send('Invalid signature');
}
// Return 200 immediately
res.status(200).send('OK');
// Process asynchronously
const event = JSON.parse(req.body);
await queue.add('process-webhook', event);
});
2. Handle Retries
✅ Implement idempotency ✅ Store event IDs to detect duplicates ✅ Handle same event multiple times safely
const processedEvents = new Set();
function handleWebhook(event) {
// Check if already processed
if (processedEvents.has(event.id)) {
return; // Skip duplicate
}
// Process event
processEvent(event);
// Mark as processed
processedEvents.add(event.id);
}
3. Error Handling
try {
await processWebhook(event);
} catch (error) {
console.error('Webhook processing error:', error);
// Log but don't fail
// Twig will retry
}
4. Logging
✅ Log all webhook receipts ✅ Track processing time ✅ Monitor failure rates
logger.info('Webhook received', {
eventId: event.id,
type: event.type,
timestamp: event.timestamp
});
Retry Logic
Automatic Retries
Twig AI automatically retries failed webhooks:
| Attempt | Delay | Total Time |
|---|---|---|
| 1 | Immediate | 0s |
| 2 | 5 seconds | 5s |
| 3 | 30 seconds | 35s |
| 4 | 5 minutes | 5m 35s |
| 5 | 1 hour | 1h 5m 35s |
After 5 attempts, webhook is marked as failed.
Handling Failed Webhooks
View failed webhooks:
- Settings → Webhooks
- Select webhook
- View "Failed Deliveries"
- Retry manually if needed
Testing Webhooks
Test Mode
curl -X POST https://api.twig.so/api/webhooks/wh_abc123/test \
-H "Authorization: Bearer YOUR_API_KEY"
Sends test event to your endpoint.
Local Development
Use tools like ngrok for local testing:
# Start ngrok
ngrok http 3000
# Update webhook URL to ngrok URL
https://abc123.ngrok.io/webhooks/twig
Event Simulator
# Simulate interaction.completed event
curl -X POST https://your-server.com/webhooks/twig \
-H "Content-Type: application/json" \
-H "X-Twig-Signature: test_signature" \
-d '{
"id": "evt_test",
"type": "interaction.completed",
"timestamp": "2024-01-15T10:00:00Z",
"data": {...}
}'
Monitoring
Webhook Metrics
Track these metrics:
- Delivery success rate
- Average response time
- Retry rate
- Error types
Dashboard
Webhook: Production
├─ Status: Active
├─ Success Rate: 99.2%
├─ Avg Response: 145ms
├─ Failed (24h): 3
└─ Last Delivery: 2 minutes ago
Alerts
Configure alerts for:
- Failed deliveries > 10
- Response time > 5s
- Success rate < 95%
Troubleshooting
Webhook Not Received
Check:
- Webhook is active
- URL is correct and accessible
- HTTPS with valid certificate
- Events are subscribed
- Firewall allows Twig IPs
Signature Verification Fails
Check:
- Using correct secret
- Payload not modified
- Using raw body (not parsed JSON)
- Comparing hex digests correctly
High Failure Rate
Check:
- Server is responding within 10s
- Returning 200 OK status
- Not throwing errors
- Handling retries correctly
Next Steps
- REST API Overview - API basics
- Authentication - Secure your webhooks
- SDKs - Use SDK webhook helpers
- Rate Limits - Webhook rate limits
Agent Instructions: Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.
Perform an HTTP GET request on the current page URL with the ask query parameter:
GET /dev/product/developer-api/webhooks.md?ask=<question>
The question should be specific, self-contained, and written in natural language. The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.
Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
People also ask
Related Pages
Last updated January 25, 2026


