Spavik
GuidesIntegrate

Webhooks

Rather than polling the API, get notified.

Rather than polling the API, get notified.

The four events

EventWhen it firesdata
model.traineda version has just been trained and serves predictionsmodel_id, version, target, task, n_train, metrics
model.staleenough outcomes have piled up, a refresh would helpmodel_id, pending_outcomes, hint
model.refresheda refresh produced a new versionmodel_id, version, outcomes_applied
credits.lowyour credit balance fell below the alert threshold, the same signal as credits_low in GET /v1/usagecredit_balance

Every event shares the same envelope:

{
  "id": "evt_example",
  "type": "model.refreshed",
  "created_at": "2026-10-11T12:00:00.000Z",
  "workspace_id": "ws_example",
  "data": { "model_id": "mdl_example", "version": 2, "outcomes_applied": 12 }
}

The event payload page details each schema.

Configure a destination

A workspace admin creates the destination on the Webhooks page of the Spavik console, with an HTTPS URL. The signing secret is shown once, at creation: store it in your receiver's secret manager and load it at runtime. Never put it in your source code.

Verifying the signature

Every delivery is signed. Verify it before doing anything with the content: without that check, anyone who knows your URL can tell you anything.

Each request carries a Spavik-Signature header:

Spavik-Signature: t=1760184000,v1=5f2b...e91c
  • t is the signing time, in Unix seconds.
  • v1 is the hex HMAC-SHA256 of t, a dot, then the raw request body, keyed with the webhook secret as a UTF-8 string.
  • Compare signatures in constant time, and reject a t more than 300 seconds away from your clock: this blocks replaying an intercepted call.
  • Each attempt is signed again with a fresh t; event.id and the payload stay the same.

The Python SDK and the TypeScript SDK both provide verify, which does all of this and returns the decoded event.

The bytes, not the object

The signature covers the bytes as received. A body parsed into JSON and re-serialised does not produce the same signature, even with exactly the same data. Read the raw body before a JSON parser transforms it.

Acknowledge delivery

Return any 2xx response once the event is stored durably, then process it in your application. Delivery is at least once:

  • Each attempt waits 5 seconds for your response.
  • Network errors, timeouts, HTTP 408, 429 and 5xx are retried, up to 8 attempts, with exponential delays from 30 seconds capped at 1 hour. A Retry-After from your receiver is honored up to 24 hours.
  • Any other non-2xx response is final, and redirects are not followed.

These are the default values. Deduplicate on event.id so that a second delivery of the same event does not repeat a business action.

Events can arrive out of order. For a given model, compare data.version with the version you already hold, or read the current state with GET /v1/models/{id} rather than trusting the order of arrival.

Disabling a webhook or changing its URL cancels its pending deliveries.

How is this guide?

Last updated on

On this page