Skip to main content
Integrations
3 min read

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

  1. Settings → Integrations → Webhooks → Add endpoint.
  2. URL. The full https URL we should POST to.
  3. Events. Pick which event types fire. See the list below.
  4. Description. For your own future reference.
  5. 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 id for replay. Even with signature verification, treat ids as the source of truth.
  • Don't trust actorUserId until you've checked the signature. The payload is server-attested only after the HMAC is verified.

Was this article helpful?

Related docs

Generic outbound webhooks · AI Domination