Skip to main content
Enoki can deliver findings events to an HTTPS endpoint you operate. You configure the destination in your workspace settings with two values: the endpoint URL and a signing secret. Every delivery is signed with that secret, and your receiver should verify the signature before it parses or trusts the body.

Delivery headers

Every delivery is a POST with a JSON body and two headers: The signed input is f"{timestamp}.{raw_body}": the timestamp header value, a literal ., then the exact bytes of the request body. Because the timestamp is part of the signed input, it cannot be altered without invalidating the signature.

Verify before you parse

  1. Read the raw request body as bytes. Do not re-serialize parsed JSON: key order and whitespace would differ and the signature would never match.
  2. Recompute HMAC-SHA256(secret, f"{timestamp}.".encode() + raw_body) using the timestamp header value.
  3. Compare the recomputed sha256=<hex> string to the X-Enoki-Signature header in constant time (hmac.compare_digest). Never use == on the digest.
  4. Reject the delivery if the signed timestamp is outside a freshness window (we recommend 5 minutes). This bounds replay of a captured request.

Delivery payload

The body is a flat JSON object. Branch on the event field and tolerate fields you do not recognize; schema_version increments when the shape changes.

Good to know

  • Retries mean possible duplicates. Failed deliveries (network errors, 5xx responses) are retried with backoff, so your receiver may see the same event more than once. Deduplicate on assessment_run_id plus event if that matters to you.
  • Respond with a 2xx quickly. Any 2xx counts as delivered. A 4xx is treated as a permanent rejection and is not retried.
  • Deliveries never follow redirects. Point the destination URL directly at your receiver.
  • HTTPS only. Plain-HTTP destination URLs are rejected when you configure the webhook, and URLs resolving to private or internal addresses are rejected before every delivery.
  • Slack destinations are not signed. This recipe applies to the generic webhook only; Slack incoming-webhook deliveries carry no signature headers.
  • Use the send-test action in workspace settings to fire a sample delivery at your receiver while you build the verifier.