Skip to Content
Welcome to the Novantra documentation.

Webhooks

Webhooks deliver outbound notifications from Novantra to your integration when important state changes happen in your workspace. They let you avoid polling list endpoints just to discover new findings, completed assessments, or submission events.

Event catalogue

The v1 webhook surface covers the workspace state changes external integrations most often need:

EventWhen it fires
governance.finding.created.v1A new finding was recorded in your workspace (via the UI or POST /api/v1/governance/findings).
governance.evidence_export.ready.v1An evidence export your workspace requested has finished and is ready to retrieve.

Always read the live catalogue from GET /api/v1/webhooks/events and subscribe to the event types it returns; that endpoint is the authoritative list for your workspace.

Payload shape

Every webhook delivery is a POST to your configured URL with this JSON body:

{ "id": "evt_01HXY...", "type": "governance.finding.created.v1", "createdAt": "2026-05-21T12:34:56.789Z", "organization": { "id": "org_01H...", "slug": "acme" }, "data": { "finding": { "id": "find_01H...", "title": "Annual control review overdue", "severity": "medium", "status": "open", "subjectModuleKey": "controls", "subjectResourceType": "control", "subjectResourceId": "ctrl_01H...", "createdAt": "2026-05-21T12:34:56.123Z" } } }

Fields you can rely on across every event type:

  • id - stable event ID. Use it for replay-safe deduplication.
  • type - the event type. Branch on this.
  • createdAt - when the event happened in the workspace.
  • organization - which workspace it came from (always set).
  • data - the event-specific payload. The shape inside data matches the public resource shape for that event type.
  • X-Novantra-Delivery-Id - unique delivery attempt ID for delivery support and audit review.

Signature verification

Every webhook delivery must be verified. Without verification, an attacker who learns your webhook URL could impersonate Novantra and forge events.

Each delivery includes:

X-Novantra-Signature: t=1779366896,v1=<signature> X-Novantra-Timestamp: 2026-05-21T12:34:56.789Z X-Novantra-Delivery-Id: whd_01EXAMPLE00000000000000000 X-Novantra-Event: governance.finding.created.v1

To verify:

  1. Concatenate the timestamp and the raw request body with a . separator: <timestamp>.<body>.
  2. Compute HMAC-SHA256 of that string, using your webhook’s signing secret as the key.
  3. Compare the hex digest to the v1= value in the X-Novantra-Signature header. Use a constant-time comparison.
  4. Reject the delivery if the digest doesn’t match.
  5. Reject the delivery if the timestamp is older than 5 minutes (replay defense).

Pseudocode:

def verify(headers, raw_body, signing_secret): signature = parse_v1(headers["X-Novantra-Signature"]) timestamp = headers["X-Novantra-Timestamp"] if not within_5_minutes(timestamp): return False expected = hmac.new( signing_secret.encode(), f"{timestamp}.{raw_body}".encode(), hashlib.sha256, ).hexdigest() return hmac.compare_digest(expected, signature)

Compute the HMAC over the raw request body, exactly as sent. Re-serializing the JSON before hashing breaks verification because key order and whitespace matter.

Subscribing

Create a webhook subscription for a service account from Developers -> Webhooks -> Endpoints, or programmatically with a token that has the webhooks:manage scope:

POST /api/v1/webhooks Authorization: Bearer <access-token> Content-Type: application/json { "url": "https://example.com/novantra/webhook", "events": ["governance.finding.created.v1"], "description": "ServiceNow incident sync", "reason": "Wire findings into ServiceNow incident queue." }

Response:

{ "webhook": { "id": "whe_01EXAMPLE00000000000000000", "name": "ServiceNow incident sync", "urlHost": "example.com", "subscribedEvents": ["governance.finding.created.v1"], "status": "active", "createdAt": "2026-05-21T12:34:56.789Z", "updatedAt": "2026-05-21T12:34:56.789Z" }, "signingSecret": "<webhook-signing-secret>" }

The signingSecret is returned only once, at creation time. Store it in your secrets store. You can rotate it later, but you cannot retrieve it.

Subscribe to the specific event types your receiver is designed to process. Use GET /api/v1/webhooks/events to refresh the current event catalogue.

Signing-secret posture

Developers -> Webhooks shows safe signing-secret posture for each endpoint:

  • storage posture;
  • one-time reveal posture;
  • last rotation time;
  • next rotation due date, when configured;
  • last verification time, when available;
  • whether customer action is required.

This posture helps administrators identify endpoints that need receiver updates after creation or rotation. It never exposes the webhook signing secret after the one-time create or rotate response, and idempotency replays return only the webhook record.

Novantra evaluates this posture from safe rotation metadata and delivery evidence. Depending on your configured rotation policy, an endpoint can move from current to due soon, overdue, or customer-action-required without exposing the signing secret.

Managing endpoints

Webhook endpoint changes require the webhooks:manage scope and an Idempotency-Key header.

Update a webhook’s receiver metadata, URL, or event subscriptions:

PATCH /api/v1/webhooks/whe_01EXAMPLE00000000000000000 Authorization: Bearer <access-token> Content-Type: application/json Idempotency-Key: webhook-update-2026-05-21 { "name": "ServiceNow incident sync", "url": "https://example.com/novantra/webhook", "events": ["governance.finding.created.v1"], "description": "ServiceNow incident queue receiver", "reason": "Refresh the approved receiver endpoint and subscriptions." }

Pause or resume delivery without changing subscriptions:

POST /api/v1/webhooks/whe_01EXAMPLE00000000000000000/pause Authorization: Bearer <access-token> Content-Type: application/json Idempotency-Key: webhook-pause-2026-05-21 { "reason": "Pause delivery while the receiver is under maintenance." }

Rotate the signing secret when your credential policy requires it:

POST /api/v1/webhooks/whe_01EXAMPLE00000000000000000/rotate-secret Authorization: Bearer <access-token> Content-Type: application/json Idempotency-Key: webhook-rotate-2026-05-21 { "reason": "Rotate the webhook signing secret under the scheduled credential policy." }

The rotation response returns a new signingSecret once. Idempotency replays return the webhook record without exposing the secret again.

Delete a webhook when the receiver is no longer approved:

DELETE /api/v1/webhooks/whe_01EXAMPLE00000000000000000 Authorization: Bearer <access-token> Content-Type: application/json Idempotency-Key: webhook-delete-2026-05-21 { "reason": "Receiver endpoint is no longer approved for this integration." }

Delivery semantics

  • At-least-once. A given event may be delivered more than once if your endpoint is briefly unavailable or returns a non-2xx status. Use the event id to deduplicate.
  • Ordering is best-effort within an event type. Cross-type ordering is not guaranteed.
  • Timeouts. Your endpoint must respond within 10 seconds with a 2xx status. Otherwise the delivery is considered failed and queued for retry.
  • Synchronous vs asynchronous. Acknowledge with 2xx first, then process. Long-running processing should happen in a background queue, not in the request handler.

Retries

Failed deliveries are retried with exponential backoff:

AttemptDelay
1 (initial)immediate
21 minute
32 minutes
44 minutes
58 minutes
616 minutes
732 minutes

After 7 failed attempts, the delivery is marked dead-lettered. The webhook itself is not disabled, but the workspace surface shows a delivery failure that an admin can review and trigger manual replay.

Replay

Workspace admins can replay any past delivery from the webhook detail page.

A replayed delivery uses the same payload as the original; only X-Novantra-Delivery-Id changes. Your deduplication should key on event id (which is stable across replays) rather than the delivery ID header.

Disabling and rotating

  • Pause a webhook to temporarily stop deliveries without losing the subscription. New events accumulate and are delivered when the webhook is unpaused.
  • Rotate signing secret to invalidate the current secret. A grace window of 60 seconds keeps the old secret valid so you can update your integration’s stored secret without a window of failed verifications.
  • Delete a webhook to permanently end the subscription. Pending deliveries for the deleted endpoint are no longer delivered.

What is and isn’t a webhook event

Webhook event?
Customer-impacting state changes (find created, evidence approved, etc.)yes
Background system events (job runs, posture sweeps)no
Admin-only events (license renewals, mailer config changes)no
Readsno - webhooks are write/state-change-driven only

Compliance and audit

Every webhook delivery is recorded in the workspace audit log with its delivery ID, the event ID, and the destination URL. Webhook subscription changes (create, update, delete, rotate secret, pause) are also audited.

Next

Last updated on