Docs/Webhooks
Webhooks

Real-time security events

The audit log is your record of what happened. Webhooks are how you find out the moment it happens — an agent gets denied, revoked, force-reauthed, or starts calling tools at an abnormal rate. Every payload is HMAC-SHA256 signed so you can verify it actually came from ModGate.

1. Event types

EventFires when
agent.deniedAn agent calls a tool outside its scope policy
agent.revokedAn agent identity is revoked, from the dashboard or API
token.rejectedA token fails validation — bad signature, wrong audience, unknown client
delegation.chain_brokenA delegated request fails because an ancestor in its xAA chain was revoked or expired
anomaly.rate_exceededAn agent exceeds its per-minute call-velocity threshold
session.reauth_requiredA request is rejected by continuous authorization — live revocation, a reverify-window timeout, or an admin force-reauth
quota.thresholdA tenant crosses 80% or 100% of its monthly authentication quota

2. Register a webhook

Point ModGate at any HTTPS endpoint and choose which events you want. Omit events to subscribe to everything.

POST https://api.modgate.io/webhooks/{tenantId}
X-ModGate-API-Key: YOUR_API_KEY
Content-Type: application/json

{
  "url": "https://yourapp.com/hooks/modgate",
  "events": ["agent.revoked", "session.reauth_required", "anomaly.rate_exceeded"]
}

# Response:
{
  "id": "wh_a1b2c3...",
  "tenantId": "org_...",
  "url": "https://yourapp.com/hooks/modgate",
  "secret": "whsec_...",
  "events": ["agent.revoked", "session.reauth_required", "anomaly.rate_exceeded"],
  "active": true,
  "createdAt": "2026-07-16T00:00:00Z",
  "secret_hint": "Save this secret — it will not be shown again: whsec_..."
}
The signing secret is only ever returned once, at creation. GET /webhooks/{tenantId} redacts it on every subsequent read. Store it somewhere safe the moment you create the webhook.

3. Payload and headers

Every delivery is a POST with a JSON body wrapping the event-specific data:

POST https://yourapp.com/hooks/modgate
Content-Type: application/json
X-ModGate-Signature: sha256=3f2e1a...
X-ModGate-Timestamp: 1752624000
X-ModGate-Event: agent.revoked
X-ModGate-Delivery: evt_9f8e7d...

{
  "id": "evt_9f8e7d...",
  "type": "agent.revoked",
  "tenantId": "org_...",
  "timestamp": 1752624000,
  "api_version": "2026-07",
  "data": {
    "agentId": "agent_xyz789",
    "agentName": "billing-bot",
    "wasOrchestrator": false,
    "revokedAt": "2026-07-16T00:00:00Z"
  }
}

X-ModGate-Delivery is unique per attempt — use it to de-duplicate if your endpoint might receive the same event more than once (see retries, below).

4. Verify the signature

The signature is HMAC-SHA256 over {timestamp}.{raw body}, keyed with your webhook's secret. Always verify against the raw request body — not a re-serialized copy.

import crypto from 'crypto'

function verify(secret, timestamp, rawBody, signatureHeader) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(`${timestamp}.${rawBody}`)
    .digest('hex')

  const provided = signatureHeader.replace('sha256=', '')

  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(provided)
  )
}
Use a constant-time comparison (crypto.timingSafeEqual), never ===, when comparing signatures.

5. Delivery and retries

ModGate retries a failed delivery (non-2xx response, timeout, or network error) up to 3 times with exponential backoff — 500ms, 1s, then 2s. A 10-second timeout applies to each attempt. Webhook delivery never blocks or delays the underlying request that triggered it.

GET https://api.modgate.io/webhooks/{tenantId}/{webhookId}/deliveries
X-ModGate-API-Key: YOUR_API_KEY

# Response:
{
  "deliveries": [
    {
      "id": "del_...",
      "eventType": "agent.revoked",
      "status": "success",
      "statusCode": 200,
      "attempts": 1,
      "deliveredAt": "2026-07-16T00:00:01Z"
    }
  ],
  "total": 1
}

Delivery logs are retained for 30 days.

6. Best practices

  • HTTPS only — ModGate rejects non-HTTPS endpoints at registration.
  • Always verify the signature before trusting the payload — anyone can POST to a public URL.
  • Respond 2xx as soon as you've durably queued the event; do the actual processing asynchronously.
  • De-duplicate on X-ModGate-Delivery — retries mean the same event can arrive more than once.
  • Subscribe only to the events you act on — fewer moving parts, easier to reason about.