Webhooks

Markdown

A webhook delivers alert hits to your own endpoint, one POST per hit. Use it to put hits into your CRM, your database, or a Slack app you built yourself.

Setting one up

Register the endpoint first. A webhook belongs to your team and can be used by any of its alerts.

curl -X POST -H "Authorization: Bearer $SPUTNIK_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "CRM relay", "url": "https://example.com/hooks/sputnik"}' \
  "https://sputnikintelligence.com/api/v1/webhooks"

The response carries the signing secret. Then point an alert at it.

curl -X POST -H "Authorization: Bearer $SPUTNIK_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"webhook": "<webhook-slug>"}' \
  "https://sputnikintelligence.com/api/v1/alerts/<alert-slug>/destinations"

Registering an endpoint on its own delivers nothing. An alert has to point at it.

The payload

{
  "event_id": "4821",
  "event_type": "alert.hit",
  "project": { "slug": "...", "name": "Acme" },
  "alert":   { "slug": "...", "name": "Acme brand mentions", "url": "https://..." },
  "matched_searches": ["Brand"],
  "post": {
    "id": 91823,
    "slug": "...",
    "title": "The one where we argue about pricing",
    "url": "https://example.com/episodes/214",
    "published_at": "2026-08-12T09:00:00+00:00",
    "source": "The Example Show",
    "type": "podcast"
  },
  "snippet": "...the matched passage..."
}

Dedupe on event_id. It is the hit id. A retry sends the same id, so your handler can be idempotent.

The payload has no full text or transcript in it. Fetch those from the API using the post slug.

Verifying the signature

We sign with the Standard Webhooks scheme, so any library for that will verify it.

webhook-id:        <event id>
webhook-timestamp: <unix seconds>
webhook-signature: v1,<base64 HMAC-SHA256 of "{id}.{timestamp}.{raw body}">

The key is the base64-decoded secret with its whsec_ prefix stripped.

Verify against the raw body

Verify before you parse. Decoding the JSON and re-encoding it changes the bytes, and the signature will fail. This is the mistake most people make once.

Check the timestamp as well, and reject anything more than a few minutes old, so an old request cannot be replayed at you.

The secret is returned on every read of the webhook, so you can look it up again. Endpoints that cannot verify signatures, such as Zapier, can ignore it.

When your endpoint fails

We retry. After repeated failures the webhook switches itself off, and deactivated_reason says why.

While it is off, any alert destination pointing at it reports is_delivering: false. Watch that field. An alert can look healthy while its webhook has stopped delivering.

Fix the endpoint, then PATCH the webhook with is_active: true. That also clears the failure count, so it gets a fresh run of attempts.

Deleting

You cannot delete a webhook while an alert still delivers to it. Deleting would silently mute those alerts. Set is_active: false instead, which is reversible.