Webhooks
The CERTEN Gateway sends webhook notifications to your configured URL when key events occur. Webhooks enable real-time integration without polling.
Configuration
Set a webhook URL for your organization when creating it, or update it later via the admin API. Each organization also has a webhook_secret used to verify payload authenticity.
Event Types
| Event | Description |
|---|---|
identity.created |
An identity has been fully provisioned |
transaction.completed |
A transaction has been confirmed on-chain |
transaction.failed |
A transaction has failed |
governance.completed |
A governance operation has been submitted |
inbox.action_signed |
A pending action has been signed |
proof.ready |
A proof bundle is ready for download |
Payload Format
All webhook payloads follow this structure:
{
"event": "transaction.completed",
"timestamp": "2025-01-01T00:00:00.000Z",
"data": {
"intent_id": "uuid",
"tx_hash": "abcdef...",
"status": "completed"
}
}
The data object varies by event type and contains the relevant resource details.
HMAC-SHA256 Verification
Every webhook request includes an X-Certen-Signature header containing an HMAC-SHA256 hex digest of the JSON payload, computed using your organization's webhook_secret.
Verifying in Node.js
const crypto = require('crypto');
function verifyWebhook(payload, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(JSON.stringify(payload))
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature, 'hex'),
Buffer.from(expected, 'hex'),
);
}
Verifying in Python
import hmac, hashlib, json
def verify_webhook(payload: dict, signature: str, secret: str) -> bool:
expected = hmac.new(
secret.encode(),
json.dumps(payload).encode(),
hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected)
Headers Included
| Header | Description |
|---|---|
Content-Type |
application/json |
X-Certen-Signature |
HMAC-SHA256 hex digest |
X-Certen-Event |
Event type (e.g., transaction.completed) |
Retry Policy
If your endpoint does not respond with a 2xx status code within 10 seconds, the delivery is retried with exponential backoff:
| Attempt | Delay |
|---|---|
| 1 | Immediate |
| 2 | 4 seconds |
| 3 | 8 seconds |
| 4 | 16 seconds |
| 5 | 32 seconds |
| 6 | 64 seconds |
| 7 | 128 seconds |
| 8 | 256 seconds |
After 8 failed attempts, the delivery is marked as permanently failed. No further retries will be attempted for that event.
Best Practices
- Respond quickly: Return a 200 status as soon as the payload is received. Process the event asynchronously.
- Handle duplicates: Webhooks may be delivered more than once. Use the event data (e.g.,
intent_id,tx_hash) to deduplicate. - Verify signatures: Always validate the
X-Certen-Signatureheader before processing the payload to prevent forgery. - Use HTTPS: Configure your webhook URL with HTTPS to ensure payloads are encrypted in transit.