Webhooks
Webhooks allow your systems to receive real-time notifications when events occur in Contract-as-Code.
Setting up webhooks
Navigate to Settings → Webhooks → Add endpoint.
| Field | Required | Description |
|---|---|---|
| URL | Yes | The HTTPS endpoint that will receive webhook events |
| Secret | Auto-generated | Used to verify webhook signatures |
| Events | Yes | Which events to subscribe to |
Endpoint requirements
- Must accept
POSTrequests - Must use HTTPS (HTTP endpoints are rejected)
- Must respond with a
2xxstatus code within 30 seconds - Should be idempotent — the same event may be delivered more than once
Event types
| Event | Trigger |
|---|---|
contract.uploaded | A new contract PDF has been uploaded |
contract.extraction_complete | AI extraction has finished for a contract |
contract.expired | A contract has passed its expiry date |
rule.approved | A rule candidate has been approved |
rule.rejected | A rule candidate has been rejected |
validation.started | A validation job has started running |
validation.completed | A validation job has finished |
validation.failed | A validation job encountered an error |
report.generated | A compliance report has been generated |
Payload format
All webhook payloads use JSON with a consistent envelope:
{
"id": "evt_abc123def456",
"type": "validation.completed",
"created_at": "2025-01-15T14:32:00Z",
"organisation_id": "org_xyz789",
"data": {
"job_id": "job_abc123",
"contract_id": "ctr_def456",
"status": "completed",
"total_findings": 14,
"findings_by_severity": {
"error": 8,
"warning": 4,
"review_needed": 2
},
"estimated_impact": {
"amount": 12450.00,
"currency": "CAD"
}
}
}Signature verification
Every webhook request includes a signature header for verification:
X-Lac-Signature: sha256=<hex-encoded HMAC-SHA256>To verify:
- Get the raw request body (do not parse JSON first)
- Compute
HMAC-SHA256(body, webhook_secret) - Compare the computed value to the
X-Lac-Signatureheader value - If they match, the request is authentic
Example (Node.js)
const crypto = require('crypto')
function verifyWebhook(body, signature, secret) {
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(body, 'utf8')
.digest('hex')
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
)
}Retry policy
If your endpoint returns a non-2xx status code or times out:
| Attempt | Delay |
|---|---|
| 1st retry | 1 minute |
| 2nd retry | 5 minutes |
| 3rd retry | 30 minutes |
| 4th retry | 2 hours |
| 5th retry | 12 hours |
After 5 failed attempts, the webhook is marked as failing and you'll receive an email notification. The webhook is not disabled automatically — you can fix the endpoint and it will resume on the next event.
Testing webhooks
Navigate to Settings → Webhooks → select an endpoint → Send test event.
This sends a test.ping event to your endpoint:
{
"id": "evt_test_abc123",
"type": "test.ping",
"created_at": "2025-01-15T14:32:00Z",
"organisation_id": "org_xyz789",
"data": {
"message": "Webhook test from Contract-as-Code"
}
}Webhook logs
Navigate to Settings → Webhooks → select an endpoint → Logs.
The log shows:
- Each delivery attempt with timestamp
- Request payload (redacted after 30 days)
- Response status code
- Response time
Logs are retained for 30 days.