Nova Help

Receive webhooks when records change

Subscribe an endpoint to Nova events, verify the signature on every delivery, and replay what you missed.

Updated in release 0.0.1 Open this page in Nova

Webhooks tell your system when something changed so you do not have to poll. An admin opens Administration, then Webhooks under Integrations & API (/settings/org/integrations/webhooks/endpoints), presses Create endpoint, enters your HTTPS address, chooses the events, and Nova shows the signing secret once. A new endpoint starts switched off: your receiver has to pass a verification check first, then the endpoint is switched on.

Verify the endpoint and switch it on

Verify endpoint (or POST /v1/integrations/webhooks/{endpoint_uid}/verify) sends one request to your address with X-Nova-Webhook-Mode: verify and a one-time token in X-Nova-Webhook-Token; the JSON body carries the same token. Answer 2xx and echo the token back, either in an X-Nova-Webhook-Token response header or in a JSON body:

{ "token": "<the token Nova sent>" }

Once verified, switch deliveries on. Through the API you can do both in one call, POST …/verify with {"enable_on_success": true}, or switch on a verified endpoint with PATCH /v1/integrations/webhooks/{endpoint_uid} and {"enabled": true}. Switching on an endpoint that is not verified answers 400.

What a delivery looks like

Each delivery is a POST with Content-Type: application/json and these headers:

Header Value
X-Nova-Event The event name, for example crm.account.updated
X-Nova-Canonical-Event The current name of the event, when you subscribed under an older alias
X-Nova-Delivery Unique per delivery; use it to ignore duplicates
X-Nova-Timestamp Unix seconds when the delivery was signed
X-Nova-Signature t=<timestamp>,v1=<hex HMAC-SHA256>
X-Nova-Signature-Previous Same format, signed with the old secret, while a rotation is in progress
X-Nova-Occurred-At When the change happened (ISO 8601)
User-Agent Nova-Webhooks/1.0

The body is an envelope around the event's data:

{
  "spec_version": "2026-03-30",
  "schema_version": "2026-03-30",
  "organization_uid": "org_3f9a1c2b7d4e5f60",
  "event_name": "crm.account.updated",
  "canonical_event_name": "crm.account.updated",
  "event_id": "5f2c8e1a9b3d4c7e8f6a2b1c0d9e8f7a",
  "delivery_id": "0b6f5a4e-2c3d-4e5f-8a9b-1c2d3e4f5a6b",
  "timestamp": "2026-09-26T14:03:12.481Z",
  "occurred_at": "2026-09-26T14:03:11.902Z",
  "subject": { "type": "account", "id": "acct_a1b2c3d4e5f6a1b2" },
  "resource": { "type": "account", "id": "acct_a1b2c3d4e5f6a1b2" },
  "data": { "uid": "acct_a1b2c3d4e5f6a1b2", "display_name": "Harper Residence" }
}

The envelope also carries scope_tier, classification, actor, correlation_id, causation_id, ordering_key and legacy_aliases. Records are named by their opaque id (see How records are identified). Fetch the record through the API when you need more than data carries. A payload too large to send whole arrives as the envelope with a marker instead of data; fetch the record in that case.

Verify the signature

  1. Read t and v1 from X-Nova-Signature.
  2. Compute HMAC-SHA256 with your secret over t + "." + raw_body (the exact bytes you received), hex encoded.
  3. Compare it to v1 in constant time. During a rotation, also accept a match on X-Nova-Signature-Previous.
  4. Reject a t more than five minutes old, then answer 2xx quickly and do the work afterwards.
import hashlib, hmac, time

def verify(secret: str, header: str, body: bytes) -> bool:
    parts = dict(p.split("=", 1) for p in header.split(","))
    signed = parts["t"].encode() + b"." + body
    expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
    fresh = abs(time.time() - int(parts["t"])) < 300
    return fresh and hmac.compare_digest(expected, parts["v1"])

Events

The Event catalog tab on the webhooks page lists every event with a sample payload. The same list is at GET /v1/integrations/webhooks/catalog.

Retries, dead letters and replay

A delivery that does not get a 2xx is retried with growing, jittered waits, up to the endpoint's attempt limit (10 at most). After that it is marked dead-lettered. An endpoint that keeps failing is paused for a cool-down and shows as unhealthy under Endpoint health.

From Delivery history an admin can Retry one delivery or Retry filtered, and Replay a time range after an outage. Make your handler idempotent on X-Nova-Delivery, because a retry or a replay can send the same change twice.

If something looks different

The webhooks page cannot open an endpoint's edit panel today (it shows "Invalid webhook link."), so switching an endpoint on, renaming it or changing its events has to go through the API calls above until that is fixed.

Rotating the secret

Press Rotate secret on the endpoint, or call POST /v1/integrations/webhooks/{endpoint_uid}/rotate-secret. For a grace period Nova signs with both secrets and sends the old signature in X-Nova-Signature-Previous, so you can switch without dropping deliveries.

Was this helpful?