Loading...
Loading...
Subscribe to governance events and receive HMAC-signed webhook deliveries in real-time.
Every action in DRD generates an event that is added to the hash-chained audit log. Events are the foundation of the reputation system and enforcement triggers.
| Event | Description |
|---|---|
| agent.registered | New agent registered |
| agent.updated | Agent metadata or configuration changed |
| agent.suspended | Agent suspended due to enforcement |
| policy.evaluated | Policy check completed |
| policy.denied | Action blocked by policy |
| policy.violated | Agent action violated a policy rule |
| enforcement.created | Enforcement action applied |
| enforcement.appealed | Agent appealed enforcement |
| enforcement.resolved | Enforcement action resolved or expired |
| approval.requested | Operator approval needed |
| approval.decided | Approval approved/denied |
| trust.score.changed | Agent trust score updated |
| trust.tier.upgraded | Agent promoted to higher badge tier |
| trust.tier.downgraded | Agent demoted to lower badge tier |
| content.registered | New content registered for protection |
| content.match.detected | Potential infringement detected |
| content.takedown.submitted | DMCA takedown notice sent |
| badge.earned | Trust badge awarded |
| badge.revoked | Trust badge removed |
Each event stores a chain hash for tamper-evident auditing. Every event links to the previous, creating an immutable chain. Hash chain integrity is verified every 6 hours automatically.
Event N-2
SHA-256
Event N-1
SHA-256
Event N
SHA-256
chainHash = SHA-256(previousHash + eventType + data + timestamp)
// Each event links to the previous, creating an immutable chain
// Hash chain integrity is verified every 6 hours automaticallyThe dashboard uses SSE for real-time event streaming. You can also consume the stream directly.
const eventSource = new EventSource(
'https://api.drd.io/api/v1/events/stream',
{ headers: { 'Authorization': 'Bearer drd_live_sk_...' } }
);
eventSource.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log(data.type, data.payload);
};Register a webhook endpoint to receive events via HTTP POST.
POST /api/v1/webhooks
{
"url": "https://your-server.com/drd-webhook",
"events": ["enforcement.created", "approval.requested"],
"secret": "whsec_..." // Optional: auto-generated if omitted
}All webhook deliveries are signed with HMAC-SHA256. Verify the signature to ensure the payload is authentic.
import crypto from 'crypto';
function verifyWebhook(payload: string, signature: string, secret: string) {
const expected = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
// In your webhook handler:
app.post('/drd-webhook', (req, res) => {
const signature = req.headers['x-drd-signature'];
if (!verifyWebhook(req.rawBody, signature, WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}
// Process the event...
});5 attempts
Exponential (1s, 2s, 4s, 8s, 16s)
10 seconds per delivery
Any 2xx response code
After 5 failed attempts, webhook is disabled
Every webhook delivery includes a JSON payload with a consistent envelope format. The payload is signed with HMAC-SHA256 using your endpoint's signing secret.
// Webhook HTTP request headers
POST /webhooks/drd HTTP/1.1
Content-Type: application/json
X-DRD-Webhook-Id: 019delivery-abcd-...
X-DRD-Webhook-Timestamp: 1739448060
X-DRD-Webhook-Signature: v1=a1b2c3d4e5f6...
// Webhook payload body
{
"id": "019event-abcd-1234-ef56-7890abcdef01",
"type": "trust.score.changed",
"timestamp": "2026-02-13T12:00:00.000Z",
"workspaceId": "019workspace-...",
"data": {
"agentId": "01956abc-...",
"agentName": "Content Scanner v2",
"previousScore": 82,
"newScore": 87,
"delta": 5,
"reason": "Policy compliance streak (+5)",
"newTier": "gold"
},
"metadata": {
"deliveryId": "019delivery-abcd-...",
"webhookId": "019webhook-abcd-...",
"attempt": 1
}
}Failed webhook deliveries are retried with exponential backoff. A delivery is considered failed if your endpoint returns a non-2xx status code or does not respond within 30 seconds.
| Attempt | Delay After Failure | Cumulative Wait |
|---|---|---|
| 1 (initial) | Immediate | 0s |
| 2 | 30 seconds | 30s |
| 3 | 2 minutes | 2m 30s |
| 4 | 10 minutes | 12m 30s |
| 5 | 30 minutes | 42m 30s |
| 6 | 2 hours | 2h 42m |
| 7 | 8 hours | 10h 42m |
| 8 (final) | 24 hours | 34h 42m |
Pre-built connectors simplify integration with popular platforms. Connectors handle message formatting, authentication, and error handling automatically.
Slack
Rich messages with action buttons, thread replies, and channel routing.
All event types
Discord
Embedded messages with color-coded severity and quick links.
All event types
PagerDuty
Incident creation with severity mapping and auto-resolution.
enforcement.*, trust.tier.downgraded
Email (SMTP)
HTML digest emails with configurable frequency and recipients.
All event types
Microsoft Teams
Adaptive Cards with action links and approval buttons.
All event types
Jira
Automatic issue creation for enforcement actions and violations.
enforcement.*, policy.violated
Build custom connectors using the DRD Connector SDK. Custom connectors receive events through the standard webhook mechanism and can transform payloads into any format.
import { DRDConnector } from "@drd.io/sdk/connectors";
const connector = new DRDConnector({
name: "custom-crm-sync",
events: ["agent.registered", "trust.score.changed"],
});
connector.on("agent.registered", async (event) => {
await fetch("https://crm.acme.com/api/contacts", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
name: event.data.agentName,
externalId: event.data.agentId,
source: "drd",
trustScore: event.data.trustScore,
}),
});
});
connector.on("trust.score.changed", async (event) => {
await fetch(`https://crm.acme.com/api/contacts/${event.data.agentId}`, {
method: "PATCH",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
trustScore: event.data.newScore,
tier: event.data.newTier,
}),
});
});
connector.listen(8080);A complete example of setting up a webhook endpoint using Express.js that verifies signatures and processes events.
import express from "express";
import { createHmac, timingSafeEqual } from "crypto";
const app = express();
const SIGNING_SECRET = process.env.DRD_WEBHOOK_SECRET!;
app.post(
"/webhooks/drd",
express.raw({ type: "application/json" }),
(req, res) => {
const payload = req.body.toString();
const webhookId = req.headers["x-drd-webhook-id"] as string;
const timestamp = req.headers["x-drd-webhook-timestamp"] as string;
const signature = req.headers["x-drd-webhook-signature"] as string;
const signedContent = `${webhookId}.${timestamp}.${payload}`;
const expected = createHmac("sha256", SIGNING_SECRET)
.update(signedContent)
.digest("hex");
const expectedBuf = Buffer.from(`v1=${expected}`, "utf8");
const receivedBuf = Buffer.from(signature, "utf8");
if (
expectedBuf.length !== receivedBuf.length ||
!timingSafeEqual(expectedBuf, receivedBuf)
) {
return res.status(401).json({ error: "Invalid signature" });
}
const event = JSON.parse(payload);
console.log(`Received: ${event.type}`, event.data);
switch (event.type) {
case "trust.score.changed":
handleTrustChange(event.data);
break;
case "enforcement.issued":
handleEnforcement(event.data);
break;
case "content.match.detected":
handleContentMatch(event.data);
break;
}
res.status(200).json({ received: true });
}
);
app.listen(3000);Send test deliveries to your webhook endpoint to verify your integration is working correctly. Test deliveries use the same signature mechanism as production deliveries.
POST /api/v1/webhooks/:id/test
{
"eventType": "trust.score.changed"
}
// Response
{
"ok": true,
"data": {
"delivered": true,
"delivery": {
"id": "019delivery-test-...",
"status": "success",
"statusCode": 200,
"responseTimeMs": 142,
"attemptedAt": "2026-02-13T12:00:00Z"
}
}
}Inspect the delivery history for any webhook endpoint. View successful and failed deliveries, response codes, and response times. Re-deliver failed events with a single API call.
GET /api/v1/webhooks/:id?includeDeliveries=true&limit=5
{
"ok": true,
"data": {
"id": "019webhook-abcd-...",
"url": "https://api.acme.com/webhooks/drd",
"enabled": true,
"deliveries": [
{
"id": "019delivery-001-...",
"eventType": "trust.score.changed",
"status": "success",
"statusCode": 200,
"responseTimeMs": 89,
"attempt": 1
},
{
"id": "019delivery-002-...",
"eventType": "enforcement.issued",
"status": "failed",
"statusCode": 500,
"responseTimeMs": 2100,
"attempt": 3,
"nextRetryAt": "2026-02-13T12:00:00Z"
}
],
"stats": {
"total": 1847,
"successful": 1832,
"failed": 15,
"successRate": 0.992
}
}
}