Webhooks

Know the moment something changes

SapanBase posts a signed JSON event to your endpoint when an article is published or updated, a reader leaves feedback, or a support request comes in or moves.

POST your-endpoint
X-SapanBase-Signature: 5d41402abc4b2a76…
X-SapanBase-Timestamp: 1793709236
X-SapanBase-Event-Id: article.published:art_123:v3

{
  "id": "article.published:art_123:v3",
  "type": "article.published",
  "createdAt": "2026-11-03T09:14:00Z",
  "data": { "id": "art_123", "title": "Invite a teammate" }
}
Events

Five events, one shape

Every event has the same envelope: an id, its type, when it happened, and the object in data.

EventSent when
article.publishedAn article goes live.
article.updatedA published article is republished with a new version.
article.feedbackA reader rates an article helpful or not helpful.
support_request.createdA new support request comes in.
support_request.updatedA support request changes status, assignee or reply count.
Verify

Check it came from SapanBase

Each delivery is signed with HMAC-SHA256 over the timestamp and the raw body, using your webhook’s signing secret. Compare in constant time, and reject old timestamps to stop replays.

verify.ts
import { createHmac, timingSafeEqual } from 'node:crypto';

const ts = req.headers['x-sapanbase-timestamp'];
const sig = req.headers['x-sapanbase-signature'];
const expected = createHmac('sha256', secret)
  .update(`${ts}.${rawBody}`)
  .digest('hex');

// reject anything older than 5 minutes
const age = Date.now() / 1000 - Number(ts);
const fresh = Math.abs(age) < 300;
const valid = fresh && sig.length === expected.length
  && timingSafeEqual(Buffer.from(sig), Buffer.from(expected));
Delivery

Built to arrive

Retried with backoff

A failed delivery is retried after 1 minute, 5 minutes, 30 minutes and 2 hours. Every attempt is re-signed with a fresh timestamp.

At least once

An event can arrive more than once. Dedupe on X-SapanBase-Event-Id and you process it exactly once.

10 second timeout

Return a 2xx within 10 seconds. Queue slow work and acknowledge first.

No redirects

Redirects are never followed. Point the webhook at the final HTTPS URL that returns 2xx.

Scoped per knowledge base

Limit article events to the knowledge bases you choose, or receive them from all of them.

A log of every delivery

See each attempt, its response code and any error in Settings, or list them from the API.

FAQ

Webhook questions

In Settings → Webhooks, or with the REST API at /v1/webhooks. Choose the events and an HTTPS URL, and copy the signing secret. It is shown once.
Rotate it from Settings or the API. Events sent after the rotation are signed with the new secret.
Public HTTPS endpoints only. Private and internal addresses are blocked, and a blocked endpoint is not retried.
The delivery is marked as failed in the log, with the last response code and error, so you can see what went wrong.

Keep your systems in step.

Join the beta, add an endpoint and get your first signed event. Free for 14 days.

Private beta · Free for 14 days · New spots every week