Webhook API
Receive real-time notifications when events occur. Build reactive integrations with webhook callbacks.
Overview
What Are Webhooks?
Webhooks push data to your server when events happen:
- Workflow executed
- Form submitted
- Integration connected
- Error occurred
How They Work
Event occurs → Our server → POST to your URL → Your server processes
Setting Up Webhooks
Create Webhook
- Go to Settings > Webhooks
- Click Create Webhook
- Enter your endpoint URL
- Select events to receive
- Save
Via API
POST /webhooks
{
"url": "https://yoursite.com/webhook",
"events": ["workflow.executed", "form.submitted"],
"secret": "your-webhook-secret"
}
Webhook Events
Workflow Events
| Event | Description |
|-------|-------------|
| workflow.created | New workflow created |
| workflow.updated | Workflow modified |
| workflow.published | Workflow published |
| workflow.deleted | Workflow deleted |
| workflow.executed | Execution completed |
| workflow.failed | Execution failed |
Form Events
| Event | Description |
|-------|-------------|
| form.created | New form created |
| form.updated | Form modified |
| form.submitted | Form submission received |
Integration Events
| Event | Description |
|-------|-------------|
| integration.connected | New integration connected |
| integration.disconnected | Integration removed |
| integration.error | Integration error |
Team Events
| Event | Description |
|-------|-------------|
| member.invited | Team invite sent |
| member.joined | Member accepted invite |
| member.removed | Member removed |
Webhook Payload
Payload Structure
{
"id": "evt_123abc",
"event": "workflow.executed",
"created_at": "2024-01-20T15:30:00Z",
"data": {
"workflow_id": "wf_456",
"execution_id": "exec_789",
"status": "completed",
"duration_ms": 1234,
"output": {...}
}
}
Common Fields
All payloads include:
id- Unique event IDevent- Event typecreated_at- Timestampdata- Event-specific data
Event Details
workflow.executed
{
"event": "workflow.executed",
"data": {
"workflow_id": "wf_456",
"workflow_name": "Lead Notification",
"execution_id": "exec_789",
"status": "completed",
"trigger": "webhook",
"started_at": "2024-01-20T15:30:00Z",
"completed_at": "2024-01-20T15:30:05Z",
"duration_ms": 5000,
"input": {...},
"output": {...}
}
}
workflow.failed
{
"event": "workflow.failed",
"data": {
"workflow_id": "wf_456",
"execution_id": "exec_789",
"status": "failed",
"error": {
"code": "NODE_ERROR",
"message": "API returned 500",
"node_id": "node_123"
}
}
}
form.submitted
{
"event": "form.submitted",
"data": {
"form_id": "form_123",
"form_name": "Contact Form",
"submission_id": "sub_456",
"submitted_at": "2024-01-20T15:30:00Z",
"data": {
"name": "John Doe",
"email": "john@example.com",
"message": "Hello!"
}
}
}
Security
Verifying Signatures
We sign all webhooks with HMAC-SHA256:
X-Webhook-Signature: sha256=abc123...
Verification Code
const crypto = require('crypto');
function verifyWebhook(payload, signature, secret) {
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
// In your handler
app.post('/webhook', (req, res) => {
const signature = req.headers['x-webhook-signature'];
const payload = JSON.stringify(req.body);
if (!verifyWebhook(payload, signature, WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}
// Process webhook...
res.status(200).send('OK');
});
Python Example
import hmac
import hashlib
def verify_webhook(payload, signature, secret):
expected = 'sha256=' + hmac.new(
secret.encode(),
payload.encode(),
hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected)
Handling Webhooks
Best Practices
- Respond quickly - Return 200 within 5 seconds
- Process async - Queue for later processing
- Handle duplicates - Check event ID
- Verify signatures - Always validate
Example Handler
app.post('/webhook', async (req, res) => {
// 1. Verify signature
if (!verifySignature(req)) {
return res.status(401).send('Invalid');
}
// 2. Check for duplicates
const eventId = req.body.id;
if (await isProcessed(eventId)) {
return res.status(200).send('Already processed');
}
// 3. Queue for processing
await queue.add('webhook', req.body);
// 4. Respond immediately
res.status(200).send('Accepted');
});
// Process async
queue.process('webhook', async (job) => {
const event = job.data;
switch (event.event) {
case 'workflow.executed':
await handleWorkflowExecuted(event.data);
break;
case 'form.submitted':
await handleFormSubmitted(event.data);
break;
}
await markProcessed(event.id);
});
Retry Policy
Automatic Retries
Failed deliveries are retried:
- Retry 1: After 1 minute
- Retry 2: After 5 minutes
- Retry 3: After 30 minutes
- Retry 4: After 2 hours
- Retry 5: After 24 hours
Failure Conditions
We retry when:
- Connection timeout (30s)
- 5xx response codes
- Connection refused
We don't retry when:
- 2xx response (success)
- 4xx response (client error)
- Invalid URL
Monitoring
Webhook Logs
View delivery history:
- Go to Settings > Webhooks
- Click your webhook
- View Delivery Log
Log Details
Each delivery shows:
- Event type
- Payload
- Response code
- Response time
- Retry count
Alerts
Set up alerts for:
- Failed deliveries
- High latency
- Consecutive failures
Managing Webhooks
List Webhooks
GET /webhooks
Get Webhook Details
GET /webhooks/:id
Update Webhook
PATCH /webhooks/:id
{
"events": ["workflow.executed"],
"active": true
}
Delete Webhook
DELETE /webhooks/:id
Test Webhook
POST /webhooks/:id/test
Sends a test event to your endpoint.
Troubleshooting
Not Receiving Webhooks
- Check URL is publicly accessible
- Verify firewall allows our IPs
- Check webhook is active
- Review delivery logs
Invalid Signature
- Check secret matches
- Verify payload not modified
- Check encoding (UTF-8)
Timeouts
- Respond quickly (< 5s)
- Process async
- Optimize endpoint
Next Steps
- API Reference - Full API docs
- Webhook Triggers - Trigger workflows
- Building MCPs - AI integrations