Webhooks Integration
Receive real-time notifications when events happen in EC-Permit. Webhooks push data to your server instead of requiring you to poll.
Available Events
form.created— A new form was createdform.updated— Form data was modifiedform.submitted— Form was submitted for approvalform.approved— Form was approvedform.rejected— Form was rejectedform.status_changed— Form status changed (any action)form.deleted— Form was deleteduser.invited— User was invited to projectuser.joined— User accepted invitationuser.removed— User was removed from project
Webhook Payload
All webhook payloads follow this structure:
{
"id": "evt_abc123",
"event": "form.approved",
"created_at": "2024-01-15T10:30:00Z",
"data": {
"form_id": "frm_xyz789",
"form_number": "HWP-001",
"form_type_id": "ft_456",
"project_id": "proj_123",
"status": "approved",
"action_by": {
"id": "usr_111",
"name": "John Smith",
"email": "john@example.com"
},
"comment": "All safety requirements verified"
}
}Signature Verification
Verify webhook signatures to ensure requests come from EC-Permit:
// Headers included with each webhook X-Webhook-Signature: sha256=abc123... X-Webhook-Timestamp: 1705315800 // Verification steps: 1. Concatenate: timestamp + "." + raw_body 2. Compute HMAC-SHA256 using your webhook secret 3. Compare with X-Webhook-Signature header
Replay Protection
Reject webhooks with timestamps older than 5 minutes to prevent replay attacks.
Responding to Webhooks
- Return
200 OKto acknowledge receipt - Respond within 30 seconds
- Non-2xx responses trigger retries
- Process asynchronously for long operations
Retry Behavior
Failed webhooks are retried with exponential backoff:
- 1st retry: 1 minute
- 2nd retry: 5 minutes
- 3rd retry: 30 minutes
- 4th retry: 2 hours
- 5th retry: 24 hours
- After 5 failures: webhook disabled, email notification sent
Example Handler (Node.js)
app.post('/webhooks/ecpermit', (req, res) => {
// Verify signature (see above)
if (!verifySignature(req)) {
return res.status(401).send('Invalid signature');
}
const { event, data } = req.body;
switch (event) {
case 'form.approved':
handleFormApproved(data);
break;
case 'form.rejected':
handleFormRejected(data);
break;
// ... handle other events
}
res.status(200).send('OK');
});