Webhook Integration
FireBackup can send HTTP webhooks to notify your systems about backup events in real-time. Use webhooks to integrate with monitoring systems, trigger automation workflows, or build custom notification systems.
Overview
Webhooks provide:
- Real-time notifications for all backup events
- Customizable payloads with template variables
- Retry logic for failed deliveries
- HMAC signatures for payload verification
- Delivery history for debugging
Quick Start
Create a Webhook
Using the Dashboard:
- Go to Settings → Webhooks
- Click Add Webhook
- Enter your endpoint URL
- Select events to monitor
- Click Save
Using the API:
curl -X POST https://api.firebackup.io/api/v1/webhooks \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "My Webhook",
"url": "https://your-server.com/webhooks/firebackup",
"events": ["backup.completed", "backup.failed"],
"secret": "your-webhook-secret"
}'
Webhook Events
Available Events
| Event | Description | Trigger |
|---|---|---|
backup.started | Backup job initiated | Immediate |
backup.progress | Backup progress update | Every 10% |
backup.completed | Backup finished successfully | On completion |
backup.failed | Backup encountered an error | On failure |
restore.started | Restore operation initiated | Immediate |
restore.progress | Restore progress update | Every 10% |
restore.completed | Restore finished successfully | On completion |
restore.failed | Restore encountered an error | On failure |
pitr.enabled | PITR enabled for project | On change |
pitr.disabled | PITR disabled for project | On change |
pitr.change_captured | New PITR changes recorded | Batched |
schedule.created | New schedule created | On creation |
schedule.updated | Schedule modified | On update |
schedule.deleted | Schedule removed | On deletion |
schedule.triggered | Scheduled backup started | On trigger |
schedule.failed | Scheduled backup missed | On miss |
storage.connected | Storage destination added | On creation |
storage.error | Storage connectivity issue | On error |
project.created | New project added | On creation |
project.deleted | Project removed | On deletion |
Subscribing to Events
Subscribe to specific events or use wildcards:
{
"events": ["backup.completed", "backup.failed"]
}
{
"events": ["backup.*"]
}
{
"events": ["*"]
}
Webhook Payload
Standard Payload Format
{
"id": "evt_abc123def456",
"type": "backup.completed",
"timestamp": "2024-01-15T10:30:45.123Z",
"data": {
"backup": {
"id": "bkp_xyz789",
"projectId": "proj_abc123",
"type": "full",
"status": "completed",
"size": 131072000,
"sizeFormatted": "125 MB",
"duration": 272000,
"durationFormatted": "4m 32s",
"storageDestination": {
"id": "stor_def456",
"name": "Production S3",
"type": "s3"
},
"collections": ["users", "orders", "products"],
"documentCount": 125000,
"createdAt": "2024-01-15T10:26:13.000Z",
"completedAt": "2024-01-15T10:30:45.123Z"
},
"project": {
"id": "proj_abc123",
"name": "Production App",
"firebaseProjectId": "my-app-prod"
},
"organization": {
"id": "org_abc123",
"name": "Acme Corp"
}
},
"metadata": {
"trigger": "scheduled",
"scheduleId": "sch_abc123",
"attempt": 1,
"deliveryId": "del_abc123"
}
}
Event-Specific Payloads
backup.failed:
{
"id": "evt_abc123def456",
"type": "backup.failed",
"timestamp": "2024-01-15T10:30:45.123Z",
"data": {
"backup": {
"id": "bkp_xyz789",
"status": "failed",
"error": {
"code": "FIREBASE_AUTH_ERROR",
"message": "Failed to authenticate with Firebase",
"details": "Token expired or invalid"
}
},
"project": { ... },
"organization": { ... }
},
"metadata": {
"trigger": "scheduled",
"attempt": 3,
"maxAttempts": 3,
"willRetry": false
}
}
pitr.change_captured:
{
"id": "evt_abc123def456",
"type": "pitr.change_captured",
"timestamp": "2024-01-15T10:30:45.123Z",
"data": {
"project": {
"id": "proj_abc123",
"name": "Production App"
},
"changes": {
"count": 150,
"fromTimestamp": "2024-01-15T10:25:00.000Z",
"toTimestamp": "2024-01-15T10:30:00.000Z",
"collections": ["users", "orders"]
}
}
}
Security
HMAC Signature Verification
FireBackup signs all webhook payloads with HMAC-SHA256. Verify signatures to ensure authenticity:
Headers sent with each request:
X-FireBackup-Signature: sha256=abc123...
X-FireBackup-Timestamp: 1705315845
X-FireBackup-Delivery-Id: del_abc123
Verification process:
const crypto = require('crypto');
function verifyWebhook(payload, signature, timestamp, secret) {
// Check timestamp is recent (within 5 minutes)
const now = Math.floor(Date.now() / 1000);
if (Math.abs(now - parseInt(timestamp)) > 300) {
return false;
}
// Compute expected signature
const signedPayload = `${timestamp}.${payload}`;
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(signedPayload)
.digest('hex');
// Compare signatures
const expected = `sha256=${expectedSignature}`;
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
// Express middleware example
app.post('/webhooks/firebackup', (req, res) => {
const signature = req.headers['x-firebackup-signature'];
const timestamp = req.headers['x-firebackup-timestamp'];
const payload = JSON.stringify(req.body);
if (!verifyWebhook(payload, signature, timestamp, WEBHOOK_SECRET)) {
return res.status(401).json({ error: 'Invalid signature' });
}
// Process webhook
handleWebhookEvent(req.body);
res.status(200).json({ received: true });
});
Python example:
import hmac
import hashlib
import time
def verify_webhook(payload, signature, timestamp, secret):
# Check timestamp
now = int(time.time())
if abs(now - int(timestamp)) > 300:
return False
# Compute signature
signed_payload = f"{timestamp}.{payload}"
expected = hmac.new(
secret.encode(),
signed_payload.encode(),
hashlib.sha256
).hexdigest()
return hmac.compare_digest(f"sha256={expected}", signature)
Delivery & Retries
Retry Policy
Failed webhook deliveries are retried with exponential backoff:
| Attempt | Delay |
|---|---|
| 1 | Immediate |
| 2 | 1 minute |
| 3 | 5 minutes |
| 4 | 30 minutes |
| 5 | 2 hours |
After 5 failed attempts, the delivery is marked as failed.
Success Criteria
A webhook is considered successful when:
- HTTP status code is 2xx (200-299)
- Response is received within 30 seconds
Viewing Delivery History
curl -X GET "https://api.firebackup.io/api/v1/webhooks/whk_abc123/deliveries" \
-H "Authorization: Bearer YOUR_API_KEY"
Response:
{
"deliveries": [
{
"id": "del_abc123",
"eventType": "backup.completed",
"status": "delivered",
"statusCode": 200,
"timestamp": "2024-01-15T10:30:45Z",
"duration": 245,
"attempts": 1
},
{
"id": "del_def456",
"eventType": "backup.failed",
"status": "failed",
"statusCode": 500,
"timestamp": "2024-01-15T09:15:30Z",
"duration": 5000,
"attempts": 5,
"lastError": "Connection timeout"
}
]
}
Retry a Delivery
curl -X POST "https://api.firebackup.io/api/v1/webhooks/whk_abc123/deliveries/del_abc123/retry" \
-H "Authorization: Bearer YOUR_API_KEY"
Configuration
Webhook Settings
| Setting | Type | Description | Default |
|---|---|---|---|
name | string | Display name | Required |
url | string | Endpoint URL | Required |
events | array | Events to subscribe | Required |
secret | string | HMAC signing secret | Auto-generated |
enabled | boolean | Enable/disable | true |
headers | object | Custom headers | {} |
timeout | number | Request timeout (ms) | 30000 |
retryEnabled | boolean | Enable retries | true |
Custom Headers
Add custom headers to webhook requests:
curl -X PATCH https://api.firebackup.io/api/v1/webhooks/whk_abc123 \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"headers": {
"X-Custom-Header": "my-value",
"Authorization": "Bearer my-service-token"
}
}'
Filters
Filter events by project or other criteria:
curl -X PATCH https://api.firebackup.io/api/v1/webhooks/whk_abc123 \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"filters": {
"projects": ["proj_abc123", "proj_def456"],
"trigger": ["scheduled"],
"excludeTrigger": ["manual"]
}
}'
Integration Examples
AWS Lambda
// Lambda handler
exports.handler = async (event) => {
const body = JSON.parse(event.body);
// Verify signature
if (!verifySignature(event)) {
return { statusCode: 401 };
}
// Handle event
switch (body.type) {
case 'backup.completed':
await notifySlack(body.data);
break;
case 'backup.failed':
await createPagerDutyIncident(body.data);
break;
}
return { statusCode: 200 };
};
Google Cloud Functions
const functions = require('@google-cloud/functions-framework');
functions.http('firebackupWebhook', async (req, res) => {
if (!verifySignature(req)) {
return res.status(401).send('Unauthorized');
}
const event = req.body;
if (event.type === 'backup.failed') {
await sendAlertToCloudMonitoring(event.data);
}
res.status(200).send('OK');
});
Zapier
Create a Zapier integration:
- Create a new Zap
- Choose "Webhooks by Zapier" as trigger
- Select "Catch Hook"
- Copy the webhook URL
- Add to FireBackup as a webhook
- Configure Zapier actions (email, Slack, etc.)
Make (Integromat)
- Create a new scenario
- Add "Webhooks" module → "Custom webhook"
- Copy the webhook URL
- Add to FireBackup
- Build your automation flow
Testing
Test Endpoint
Send a test webhook to verify your endpoint:
curl -X POST https://api.firebackup.io/api/v1/webhooks/whk_abc123/test \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"event": "backup.completed"
}'
Local Development
Use ngrok or similar tools to test webhooks locally:
# Start ngrok
ngrok http 3000
# Use the ngrok URL as your webhook endpoint
# https://abc123.ngrok.io/webhooks/firebackup
Webhook Debugging
View raw request details:
curl -X GET "https://api.firebackup.io/api/v1/webhooks/whk_abc123/deliveries/del_abc123/request" \
-H "Authorization: Bearer YOUR_API_KEY"
Best Practices
Do's
- Respond quickly - Return 200 within 5 seconds, process async
- Verify signatures - Always validate HMAC signatures
- Handle duplicates - Use
deliveryIdfor idempotency - Log everything - Keep records for debugging
- Use HTTPS - Never use HTTP for webhooks
Don'ts
- Don't trust payload - Validate even with signatures
- Don't block - Process webhooks asynchronously
- Don't ignore retries - Handle duplicate deliveries
- Don't hardcode secrets - Use environment variables
Idempotency
Use the delivery ID to prevent duplicate processing:
const processedDeliveries = new Set();
async function handleWebhook(event, deliveryId) {
if (processedDeliveries.has(deliveryId)) {
console.log('Duplicate delivery, skipping');
return;
}
processedDeliveries.add(deliveryId);
// Process the event
await processEvent(event);
}
Troubleshooting
Webhook Not Receiving Events
- Check webhook is enabled in dashboard
- Verify URL is correct and accessible
- Check event subscription matches expected events
- Review firewall rules - allow FireBackup IPs
Signature Verification Failing
- Check secret matches webhook configuration
- Verify payload isn't modified by middleware
- Check timestamp is within 5 minutes
Timeouts
- Return 200 immediately and process async
- Increase timeout if needed (max 60 seconds)
- Check endpoint performance
Related
- Slack Integration - Slack-specific configuration
- CI/CD Integration - Automate with CI/CD
- Webhooks API Reference - API documentation