Webhooks
⚠️ Future Content: The detailed webhook implementation guides and technical specifications below will be provided in future documentation updates. Core webhook concepts are being developed based on the platform's event-driven architecture.
Webhooks provide real-time notifications when events occur in your Filedgr vaults and data streams. Instead of polling for changes, webhooks push updates directly to your applications.
Overview
Webhooks are HTTP POST requests sent to your specified endpoints when specific events occur. They enable:
- Real-time notifications - Instant updates when data changes
- Event-driven automation - Trigger workflows based on Filedgr events
- System integration - Keep external systems synchronized
- Compliance monitoring - Track access and modifications in real-time
Webhook Events
Vault Events
vault.created
Triggered when a new vault is created.
{
"eventType": "vault.created",
"eventId": "evt_abc123",
"timestamp": "2024-01-15T10:30:00Z",
"data": {
"vaultId": "vault_xyz789",
"name": "Product Certification Vault",
"templateId": "certification-template-v1",
"owner": "user_def456",
"createdAt": "2024-01-15T10:30:00Z"
}
}
vault.data_updated
Triggered when data is added to or modified in a vault.
{
"eventType": "vault.data_updated",
"eventId": "evt_def456",
"timestamp": "2024-01-15T14:22:00Z",
"data": {
"vaultId": "vault_xyz789",
"updateType": "file_uploaded",
"fileId": "file_abc123",
"fileName": "quality-certificate.pdf",
"fileSize": 245760,
"uploadedBy": "user_ghi789",
"metadata": {
"type": "certificate",
"category": "quality"
}
}
}
vault.permission_changed
Triggered when vault permissions are modified.
{
"eventType": "vault.permission_changed",
"eventId": "evt_ghi789",
"timestamp": "2024-01-15T16:45:00Z",
"data": {
"vaultId": "vault_xyz789",
"userId": "user_jkl012",
"permission": "VIEWER",
"previousPermission": "NONE",
"changedBy": "user_def456",
"expiresAt": "2024-12-31T23:59:59Z"
}
}
vault.verification_complete
Triggered when blockchain verification completes.
{
"eventType": "vault.verification_complete",
"eventId": "evt_jkl012",
"timestamp": "2024-01-15T10:35:00Z",
"data": {
"vaultId": "vault_xyz789",
"fileId": "file_abc123",
"verificationStatus": "verified",
"blockchainTx": "0x1234567890abcdef...",
"network": "polygon",
"gasUsed": 21000,
"proofHash": "0xabcdef1234567890..."
}
}
Data Stream Events
stream.data_received
Triggered when new data arrives in a stream.
{
"eventType": "stream.data_received",
"eventId": "evt_mno345",
"timestamp": "2024-01-15T11:00:00Z",
"data": {
"streamId": "stream_temp001",
"vaultId": "vault_xyz789",
"dataPoints": 1,
"latestData": {
"temperature": 23.5,
"humidity": 65.2,
"timestamp": "2024-01-15T11:00:00Z"
},
"source": "TEMP-SENSOR-001"
}
}
stream.alert_triggered
Triggered when stream data exceeds defined thresholds.
{
"eventType": "stream.alert_triggered",
"eventId": "evt_pqr678",
"timestamp": "2024-01-15T15:30:00Z",
"data": {
"streamId": "stream_temp001",
"vaultId": "vault_xyz789",
"alertRule": "high_temperature",
"severity": "critical",
"triggerValue": 35.2,
"threshold": 30.0,
"message": "Temperature exceeded safe operating range"
}
}
Access Events
vault.accessed
Triggered when someone accesses vault data.
{
"eventType": "vault.accessed",
"eventId": "evt_stu901",
"timestamp": "2024-01-15T09:15:00Z",
"data": {
"vaultId": "vault_xyz789",
"userId": "user_vwx234",
"accessType": "view",
"resource": "file_abc123",
"ipAddress": "192.168.1.100",
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)..."
}
}
Setting Up Webhooks
Creating a Webhook
curl -X POST "https://api.filedgr.io/webhooks" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"url": "https://api.yourcompany.com/filedgr-webhook",
"events": ["vault.created", "vault.data_updated", "vault.verification_complete"],
"secret": "your-webhook-secret-key",
"active": true
}'
Response:
{
"webhookId": "webhook_abc123",
"url": "https://api.yourcompany.com/filedgr-webhook",
"events": ["vault.created", "vault.data_updated", "vault.verification_complete"],
"secret": "your-webhook-secret-key",
"active": true,
"createdAt": "2024-01-15T10:30:00Z"
}
Webhook Configuration Options
const webhookConfig = {
// Required
url: "https://api.yourcompany.com/webhooks/filedgr",
// Event filters
events: [
"vault.created",
"vault.data_updated",
"vault.verification_complete",
"stream.data_received",
"stream.alert_triggered"
],
// Optional filters
filters: {
vaultIds: ["vault_xyz789", "vault_abc456"], // Only these vaults
templateIds: ["template_manufacturing"], // Only specific templates
userIds: ["user_important"], // Only events by specific users
},
// Security
secret: "your-webhook-secret-key",
// Delivery options
timeout: 30, // seconds
retryCount: 3,
retryDelay: 5, // seconds between retries
// Status
active: true,
// Headers
headers: {
"Authorization": "Bearer your-api-token",
"X-Custom-Header": "custom-value"
}
};
const webhook = await filedgr.webhooks.create(webhookConfig);
Handling Webhooks
Basic Webhook Handler
const express = require('express');
const crypto = require('crypto');
const app = express();
// Middleware to capture raw body for signature verification
app.use('/webhooks/filedgr', express.raw({ type: 'application/json' }));
app.post('/webhooks/filedgr', (req, res) => {
try {
// Verify webhook signature
const signature = req.headers['x-filedgr-signature'];
const payload = req.body;
if (!verifySignature(payload, signature, process.env.WEBHOOK_SECRET)) {
return res.status(401).send('Unauthorized');
}
// Parse the event
const event = JSON.parse(payload);
// Handle different event types
handleWebhookEvent(event);
// Respond quickly to acknowledge receipt
res.status(200).send('OK');
} catch (error) {
console.error('Webhook handling error:', error);
res.status(500).send('Internal Server Error');
}
});
function verifySignature(payload, signature, secret) {
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature, 'hex'),
Buffer.from(expectedSignature, 'hex')
);
}
function handleWebhookEvent(event) {
console.log(`Received event: ${event.eventType}`);
switch (event.eventType) {
case 'vault.created':
handleVaultCreated(event.data);
break;
case 'vault.data_updated':
handleDataUpdated(event.data);
break;
case 'vault.verification_complete':
handleVerificationComplete(event.data);
break;
case 'stream.alert_triggered':
handleStreamAlert(event.data);
break;
default:
console.log(`Unhandled event type: ${event.eventType}`);
}
}
Advanced Event Processing
class FiledgrWebhookProcessor {
constructor() {
this.eventQueue = [];
this.processing = false;
}
async processEvent(event) {
// Add to queue for processing
this.eventQueue.push(event);
if (!this.processing) {
this.processing = true;
await this.processQueue();
this.processing = false;
}
}
async processQueue() {
while (this.eventQueue.length > 0) {
const event = this.eventQueue.shift();
try {
await this.handleEvent(event);
} catch (error) {
console.error(`Failed to process event ${event.eventId}:`, error);
// Implement retry logic
if (!event.retryCount || event.retryCount < 3) {
event.retryCount = (event.retryCount || 0) + 1;
// Re-queue with delay
setTimeout(() => {
this.eventQueue.unshift(event);
}, 1000 * event.retryCount);
}
}
}
}
async handleEvent(event) {
const handler = this.getEventHandler(event.eventType);
if (handler) {
await handler(event.data, event);
} else {
console.warn(`No handler for event type: ${event.eventType}`);
}
}
getEventHandler(eventType) {
const handlers = {
'vault.created': this.handleVaultCreated.bind(this),
'vault.data_updated': this.handleDataUpdated.bind(this),
'vault.verification_complete': this.handleVerificationComplete.bind(this),
'stream.data_received': this.handleStreamData.bind(this),
'stream.alert_triggered': this.handleStreamAlert.bind(this),
'vault.accessed': this.handleVaultAccess.bind(this)
};
return handlers[eventType];
}
async handleVaultCreated(data, event) {
console.log(`New vault created: ${data.vaultId}`);
// Update local database
await database.vaults.create({
id: data.vaultId,
name: data.name,
templateId: data.templateId,
owner: data.owner,
createdAt: data.createdAt
});
// Notify relevant teams
await notifications.send({
type: 'vault_created',
recipients: ['compliance@company.com'],
data: data
});
}
async handleDataUpdated(data, event) {
console.log(`Data updated in vault: ${data.vaultId}`);
// Update search index
await searchIndex.updateVault(data.vaultId);
// Trigger compliance check if needed
if (data.metadata?.type === 'certificate') {
await complianceSystem.checkCertificate(data.fileId);
}
// Update analytics
await analytics.recordDataUpdate(data.vaultId, data.updateType);
}
async handleVerificationComplete(data, event) {
console.log(`Verification complete for vault: ${data.vaultId}`);
if (data.verificationStatus === 'verified') {
// Update local status
await database.files.updateStatus(data.fileId, 'verified');
// Notify stakeholders
await notifications.send({
type: 'verification_complete',
vaultId: data.vaultId,
fileId: data.fileId
});
} else {
// Handle verification failure
console.error(`Verification failed for file: ${data.fileId}`);
await this.handleVerificationFailure(data);
}
}
async handleStreamAlert(data, event) {
console.log(`Stream alert triggered: ${data.alertRule}`);
// Create incident ticket
const incident = await ticketingSystem.createIncident({
type: 'stream_alert',
severity: data.severity,
title: `Stream Alert: ${data.message}`,
description: `Alert triggered for stream ${data.streamId}`,
metadata: data
});
// Send notifications based on severity
if (data.severity === 'critical') {
await notifications.sendImmediate({
type: 'critical_alert',
recipients: ['ops@company.com', 'manager@company.com'],
data: data
});
}
}
}
Webhook Retry Logic
// Implement exponential backoff for failed webhook deliveries
class WebhookRetryHandler {
constructor(maxRetries = 5) {
this.maxRetries = maxRetries;
}
async deliverWebhook(webhookConfig, event) {
let attempt = 0;
while (attempt < this.maxRetries) {
try {
const response = await fetch(webhookConfig.url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Filedgr-Signature': this.generateSignature(event, webhookConfig.secret),
'X-Filedgr-Event-Type': event.eventType,
'X-Filedgr-Event-ID': event.eventId,
...webhookConfig.headers
},
body: JSON.stringify(event),
timeout: webhookConfig.timeout || 30000
});
if (response.ok) {
console.log(`Webhook delivered successfully on attempt ${attempt + 1}`);
return true;
} else {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
} catch (error) {
attempt++;
console.error(`Webhook delivery attempt ${attempt} failed:`, error.message);
if (attempt < this.maxRetries) {
const delay = Math.pow(2, attempt) * 1000; // Exponential backoff
console.log(`Retrying in ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
console.error(`Webhook delivery failed after ${this.maxRetries} attempts`);
return false;
}
generateSignature(event, secret) {
return crypto
.createHmac('sha256', secret)
.update(JSON.stringify(event))
.digest('hex');
}
}
Webhook Security
Signature Verification
// Verify webhook authenticity
function verifyWebhookSignature(payload, signature, secret) {
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(payload, 'utf8')
.digest('hex');
// Use timing-safe comparison
return crypto.timingSafeEqual(
Buffer.from(signature, 'hex'),
Buffer.from(expectedSignature, 'hex')
);
}
// Express middleware for webhook verification
function webhookVerificationMiddleware(secret) {
return (req, res, next) => {
const signature = req.headers['x-filedgr-signature'];
if (!signature) {
return res.status(401).json({ error: 'Missing signature header' });
}
if (!verifyWebhookSignature(req.body, signature, secret)) {
return res.status(401).json({ error: 'Invalid signature' });
}
next();
};
}
// Usage
app.use('/webhooks/filedgr', webhookVerificationMiddleware(process.env.WEBHOOK_SECRET));
Rate Limiting
const rateLimit = require('express-rate-limit');
// Apply rate limiting to webhook endpoints
const webhookRateLimit = rateLimit({
windowMs: 1 * 60 * 1000, // 1 minute
max: 100, // Limit each IP to 100 requests per windowMs
message: 'Too many webhook requests from this IP',
standardHeaders: true,
legacyHeaders: false
});
app.use('/webhooks/filedgr', webhookRateLimit);
Input Validation
const Joi = require('joi');
// Define webhook event schema
const webhookEventSchema = Joi.object({
eventType: Joi.string().required(),
eventId: Joi.string().required(),
timestamp: Joi.string().isoDate().required(),
data: Joi.object().required()
});
function validateWebhookEvent(req, res, next) {
const { error, value } = webhookEventSchema.validate(req.body);
if (error) {
return res.status(400).json({
error: 'Invalid webhook payload',
details: error.details
});
}
req.body = value;
next();
}
Testing Webhooks
Webhook Testing Tool
// Test webhook endpoint locally
const express = require('express');
const ngrok = require('ngrok');
class WebhookTester {
constructor() {
this.app = express();
this.events = [];
this.setupRoutes();
}
setupRoutes() {
this.app.use(express.json());
// Test endpoint
this.app.post('/test-webhook', (req, res) => {
console.log('Received webhook:', JSON.stringify(req.body, null, 2));
// Store event for inspection
this.events.push({
timestamp: new Date().toISOString(),
headers: req.headers,
body: req.body
});
res.status(200).json({ received: true });
});
// Web interface to view events
this.app.get('/events', (req, res) => {
res.json(this.events);
});
this.app.get('/', (req, res) => {
res.send(`
<h1>Webhook Tester</h1>
<p>Received ${this.events.length} events</p>
<a href="/events">View Events</a>
`);
});
}
async start(port = 3000) {
// Start local server
this.server = this.app.listen(port, () => {
console.log(`Webhook tester running on port ${port}`);
});
// Create ngrok tunnel
const url = await ngrok.connect(port);
console.log(`Public URL: ${url}/test-webhook`);
return `${url}/test-webhook`;
}
stop() {
if (this.server) {
this.server.close();
}
ngrok.kill();
}
}
// Usage
const tester = new WebhookTester();
const webhookUrl = await tester.start();
// Now configure this URL in Filedgr dashboard
console.log(`Use this URL for testing: ${webhookUrl}`);
Manual Webhook Testing
# Test webhook endpoint manually
curl -X POST "https://yourapp.com/webhooks/filedgr" \
-H "Content-Type: application/json" \
-H "X-Filedgr-Signature: your-test-signature" \
-H "X-Filedgr-Event-Type: vault.created" \
-d '{
"eventType": "vault.created",
"eventId": "test-event-123",
"timestamp": "2024-01-15T10:30:00Z",
"data": {
"vaultId": "vault_test123",
"name": "Test Vault",
"templateId": "test-template",
"owner": "user_test",
"createdAt": "2024-01-15T10:30:00Z"
}
}'
Webhook Management
List Webhooks
curl -X GET "https://api.filedgr.io/webhooks" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
Update Webhook
curl -X PUT "https://api.filedgr.io/webhooks/webhook_abc123" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"events": ["vault.created", "vault.data_updated"],
"active": true
}'
Delete Webhook
curl -X DELETE "https://api.filedgr.io/webhooks/webhook_abc123" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
Webhook Logs
# View webhook delivery logs
curl -X GET "https://api.filedgr.io/webhooks/webhook_abc123/logs" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
Response:
{
"webhookId": "webhook_abc123",
"logs": [
{
"eventId": "evt_def456",
"eventType": "vault.created",
"deliveryStatus": "success",
"httpStatus": 200,
"responseTime": 245,
"timestamp": "2024-01-15T10:30:00Z",
"attempts": 1
},
{
"eventId": "evt_ghi789",
"eventType": "vault.data_updated",
"deliveryStatus": "failed",
"httpStatus": 500,
"responseTime": 30000,
"timestamp": "2024-01-15T11:15:00Z",
"attempts": 3,
"lastError": "Connection timeout"
}
]
}
Common Use Cases
ERP Integration
// Sync vault data with ERP system
async function handleVaultDataUpdate(data, event) {
if (data.vaultId.startsWith('product_')) {
// Update product information in ERP
await erp.updateProduct({
productId: data.vaultId,
lastModified: event.timestamp,
documents: data.fileName ? [data.fileName] : []
});
}
}
async function handleVerificationComplete(data, event) {
// Update compliance status in ERP
await erp.updateComplianceStatus({
productId: data.vaultId,
documentId: data.fileId,
verified: data.verificationStatus === 'verified',
verificationDate: event.timestamp,
blockchainProof: data.blockchainTx
});
}
Compliance Monitoring
// Real-time compliance monitoring
async function handleVaultAccess(data, event) {
// Log access for audit trail
await auditLog.record({
type: 'vault_access',
userId: data.userId,
vaultId: data.vaultId,
resource: data.resource,
timestamp: event.timestamp,
ipAddress: data.ipAddress
});
// Check for suspicious activity
if (await detectSuspiciousAccess(data)) {
await securityTeam.alert({
type: 'suspicious_access',
details: data,
severity: 'high'
});
}
}
async function detectSuspiciousAccess(data) {
// Multiple access attempts from different IPs
const recentAccess = await auditLog.getRecentAccess(data.userId, '5m');
const uniqueIPs = new Set(recentAccess.map(a => a.ipAddress));
return uniqueIPs.size > 3;
}
Automated Workflows
// Trigger automated workflows based on events
async function handleStreamAlert(data, event) {
const workflow = await workflowEngine.getWorkflow('stream_alert_response');
await workflowEngine.execute(workflow, {
streamId: data.streamId,
severity: data.severity,
alertRule: data.alertRule,
triggerValue: data.triggerValue,
threshold: data.threshold
});
}
// Example workflow definition
const streamAlertWorkflow = {
name: 'stream_alert_response',
triggers: ['stream.alert_triggered'],
steps: [
{
name: 'assess_severity',
condition: 'data.severity === "critical"',
actions: [
'create_incident',
'notify_on_call_engineer',
'escalate_to_management'
]
},
{
name: 'standard_response',
condition: 'data.severity === "warning"',
actions: [
'create_ticket',
'notify_operations_team'
]
},
{
name: 'log_event',
condition: 'true',
actions: [
'update_monitoring_dashboard',
'record_metrics'
]
}
]
};
Best Practices
Webhook Endpoint Design
- Fast response - Acknowledge quickly, process asynchronously
- Idempotent handling - Handle duplicate events gracefully
- Error handling - Return appropriate HTTP status codes
- Security - Always verify signatures and validate input
Event Processing
- Queue processing - Use message queues for reliable processing
- Retry logic - Implement exponential backoff for failures
- Dead letter queues - Handle events that repeatedly fail
- Monitoring - Track webhook delivery success rates
Security
- Signature verification - Always verify webhook signatures
- HTTPS only - Use secure endpoints for webhook URLs
- IP filtering - Restrict access to known Filedgr IP ranges
- Rate limiting - Protect against webhook flooding
Monitoring
- Delivery tracking - Monitor webhook delivery success
- Performance metrics - Track response times and error rates
- Alerting - Get notified of webhook delivery failures
- Logging - Maintain detailed logs for troubleshooting
Troubleshooting
Common Issues
Webhook Not Receiving Events
- Check URL accessibility - Ensure endpoint is publicly accessible
- Verify SSL certificate - Use valid HTTPS certificate
- Check firewall rules - Allow inbound connections
- Review webhook configuration - Ensure correct events are selected
Signature Verification Failing
- Check secret key - Ensure correct webhook secret is used
- Verify payload format - Use raw request body for verification
- Check header name - Look for 'X-Filedgr-Signature' header
- Review signature generation - Use HMAC-SHA256 with hex encoding
High Latency or Timeouts
- Optimize endpoint performance - Respond quickly, process async
- Check network connectivity - Ensure stable internet connection
- Review server resources - Ensure adequate CPU/memory
- Implement proper error handling - Return appropriate status codes
Debugging Tips
// Debug webhook processing
function debugWebhook(req, res, next) {
console.log('Webhook Debug Info:');
console.log('Headers:', req.headers);
console.log('Body:', req.body);
console.log('Signature:', req.headers['x-filedgr-signature']);
next();
}
// Enhanced error logging
function logWebhookError(error, event) {
console.error('Webhook Processing Error:', {
eventId: event.eventId,
eventType: event.eventType,
timestamp: event.timestamp,
error: error.message,
stack: error.stack
});
}
Next Steps
- Explore SDKs - Use SDKs for easier webhook handling
- Learn Error Handling - Handle API and webhook errors
- Review Examples - See real webhook implementations
- Integration Guide - Connect webhooks to your systems
Webhooks enable real-time, event-driven integration with Filedgr. Start building responsive applications that react instantly to changes in your data.