Webhooks
Adjudon sends HTTP POST requests to your URL when events occur in your organization.
Creating a webhook
curl -X POST https://api.adjudon.com/api/v1/webhooks \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <jwt>" \
-d '{
"url": "https://your-server.com/adjudon-webhook",
"events": ["review.completed", "trace.blocked"],
"filterAgentId": "my-agent"
}'
filterAgentId is optional — omit it to receive events for all agents.
Event types
| Event | When it fires |
|---|---|
review.completed | A review queue item was approved, rejected, or escalated |
trace.blocked | A trace was blocked by a policy |
trace.flagged | A trace was flagged for review |
policy.violated | Any policy rule was triggered |
Payload format
All webhook payloads share a common envelope:
{
"event": "trace.blocked",
"timestamp": "2026-04-13T12:00:00.000Z",
"organizationId": "org_abc123",
"data": { ... }
}
trace.blocked payload
{
"event": "trace.blocked",
"timestamp": "2026-04-13T12:00:00.000Z",
"organizationId": "org_abc123",
"data": {
"traceId": "trace_abc123",
"agentId": "my-agent",
"status": "blocked",
"confidence": 0.21,
"policyId": "policy_xyz",
"policyName": "Block harmful content",
"reason": "Output matched harmful content policy"
}
}
trace.flagged payload
{
"event": "trace.flagged",
"timestamp": "2026-04-13T12:00:00.000Z",
"organizationId": "org_abc123",
"data": {
"traceId": "trace_abc123",
"agentId": "my-agent",
"status": "flagged",
"confidence": 0.64
}
}
review.completed payload
{
"event": "review.completed",
"timestamp": "2026-04-13T12:00:00.000Z",
"organizationId": "org_abc123",
"data": {
"reviewItemId": "review_abc123",
"traceId": "trace_abc123",
"decision": "approve",
"reviewerId": "user_xyz",
"notes": "Reviewed and confirmed safe"
}
}
Signature verification
Every request includes an X-Adjudon-Signature header with an HMAC-SHA256 signature of the payload body, signed with your webhook's secret.
import hmac
import hashlib
def verify_signature(payload_body: bytes, secret: str, signature: str) -> bool:
expected = hmac.new(
secret.encode(),
payload_body,
hashlib.sha256,
).hexdigest()
return hmac.compare_digest(expected, signature)
import crypto from 'crypto';
function verifySignature(payloadBody: Buffer, secret: string, signature: string): boolean {
const expected = crypto
.createHmac('sha256', secret)
.update(payloadBody)
.digest('hex');
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
}
Always verify the signature before processing the payload.
Retry logic
If your endpoint returns a non-2xx response, Adjudon retries with exponential backoff:
| Attempt | Delay |
|---|---|
| 1st retry | 30 seconds |
| 2nd retry | 5 minutes |
| 3rd retry | 30 minutes |
| 4th retry | 2 hours |
After 4 retries, the delivery is marked as failed.
Testing
Send a test event to verify your endpoint is configured correctly:
curl -X POST https://api.adjudon.com/api/v1/webhooks/WEBHOOK_ID/test \
-H "Authorization: Bearer <jwt>"