Generic outbound webhooks
Signing secret, payload shape, and retry behaviour.
Last updated May 12, 2026
Why generic webhooks
For anything beyond our packaged integrations, generic outbound webhooks let you POST our events to an arbitrary URL — your own backend, a third-party tool, a serverless function. You handle the receiver.
Setting up
- Settings → Integrations → Webhooks → Add endpoint.
- URL. The full https URL we should POST to.
- Events. Pick which event types fire. See the list below.
- Description. For your own future reference.
- Click Save. The signing secret is shown once — copy it now.
Events available
| Event | When |
|---|---|
audit.completed |
An audit finishes |
audit.scheduled.fired |
A scheduled audit kicks off |
audit.score.changed |
A dimension score crosses a threshold |
mention.created |
A new mention is detected |
mention.sentiment.negative |
A negative-sentiment mention |
content.created |
A new draft |
content.approved |
Draft moves to Approved |
content.published |
Draft published |
content.compliance.flagged |
Draft fails compliance |
competitor.added |
Competitor added to a company |
api.key.created |
API key created |
api.key.revoked |
API key revoked |
Payload shape
Every event has the same envelope:
{
"id": "evt_01HSXXXXX",
"type": "audit.completed",
"createdAt": "2026-05-12T14:30:00Z",
"organizationId": "org_abc123",
"actorUserId": "usr_def456",
"data": {
/* event-specific payload */
}
}
The data block varies by event type. For audit.completed it includes the company id, the headline score, and the six dimension scores. Full payload reference at /docs/api (Domination tier).
Signature verification
Every request includes a X-AID-Signature header. Verify it server-side:
import { createHmac } from "crypto";
function verify(rawBody: string, signature: string, secret: string): boolean {
const expected = "sha256=" + createHmac("sha256", secret)
.update(rawBody)
.digest("hex");
return signature === expected;
}
If verification fails, return 401 and we'll consider the delivery failed (which triggers retries — see below).
Idempotency
Every event has a stable id. If you receive the same id twice, it's a retry. Use the id as your idempotency key to avoid double-processing.
Retry behaviour
Failed deliveries (any non-2xx response, timeout, or DNS failure) retry on this schedule:
| Attempt | Delay |
|---|---|
| 1 | immediate |
| 2 | 30 seconds |
| 3 | 2 minutes |
| 4 | 10 minutes |
| 5 | 1 hour |
| 6 | 6 hours |
| 7 | 24 hours |
After the seventh failure, the delivery is abandoned. The endpoint stays active — future events still fire — but the failed event won't retry again.
Per-event delivery log
Settings → Integrations → Webhooks → [endpoint] → Deliveries. Shows every delivery for the last 30 days with:
- Event id and type.
- HTTP response code we received.
- Response body (first 1KB).
- Number of attempts.
- Manual retry button.
Rotating the secret
Settings → Integrations → Webhooks → [endpoint] → Rotate secret. Generates a new secret and shows it once. We send signatures using the new secret immediately — make sure your verifier has both old and new secrets configured during the rollover window (24 hours).
Disabling an endpoint
Click the endpoint row → Disable. Events that would have fired during the disabled period are NOT queued — the endpoint simply skips them. Re-enable to resume future deliveries.
Security best practices
- Always verify signatures. Anyone with your URL can POST garbage.
- Use HTTPS. We refuse to register http URLs.
- Check the event
idfor replay. Even with signature verification, treat ids as the source of truth. - Don't trust
actorUserIduntil you've checked the signature. The payload is server-attested only after the HMAC is verified.
Was this article helpful?