Developer docs

Verifying PulseSignal webhook signatures

PulseSignal signs every outbound webhook with HMAC-SHA256 so your server can confirm the request came from us and was not modified in transit. The signing scheme is Standard Webhooks v1, the same shape used by Stripe, Shopify, and Polar.

Headers

Every POST includes the following headers. The signature is over the exact request body bytes, joined to the timestamp with a period.

  • X-PulseSignal-Timestamp the Unix timestamp at the moment of signing.
  • X-PulseSignal-Signature of the form v1=<hex>, where hex = HMAC-SHA256(secret, "{timestamp}.{body}").
  • X-PulseSignal-Event-Id a stable event identifier; use it to dedupe retries on your side.
  • X-PulseSignal-Event-Type one of change_event, narrative, gtm_lead, alert_rule_trigger, or webhook.test.
  • X-PulseSignal-Attempt the 1-indexed delivery attempt. PulseSignal retries on 5xx responses, transport errors, and 429 throttles.

Verification in Python

import hmac, hashlib

def verify(secret: str, timestamp: str, body: bytes, sig_header: str) -> bool:
    expected = "v1=" + hmac.new(
        secret.encode(),
        f"{timestamp}.".encode() + body,
        hashlib.sha256,
    ).hexdigest()
    return hmac.compare_digest(expected, sig_header)

Verification in Node.js

const crypto = require('crypto')

function verify(secret, timestamp, body, sigHeader) {
  const expected =
    'v1=' +
    crypto
      .createHmac('sha256', secret)
      .update(`${timestamp}.`)
      .update(body)
      .digest('hex')
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(sigHeader),
  )
}

Replay protection

Reject any request whose timestamp is more than 5 minutes off from your server clock. An attacker who captures a valid request on the wire can replay it; the timestamp bound is what shuts that down. PulseSignal re-signs every retry with a fresh timestamp, so your replay window does not need to be lenient.

Retry schedule

A delivery that returns 5xx, 429, or fails to connect is retried with exponential backoff:

  • Attempt 2: 1 minute later
  • Attempt 3: 5 minutes later
  • Attempt 4: 30 minutes later
  • Attempt 5: 2 hours later
  • Attempt 6: 8 hours later
  • Attempt 7: 24 hours later

After 10 consecutive failures across all events, the endpoint is automatically disabled and you will need to re-enable it from the dashboard once your receiver is healthy.

Response contract

Return any 2xx status code within 10 seconds to acknowledge delivery. 3xx redirects are not followed (cloud-provider metadata services bait this) and are treated as failures. 4xx responses other than 429 stop the retry chain since they indicate a permanent configuration problem on the receiving side.

Secret rotation

Rotate the signing secret from the dashboard at any time. The new secret is shown exactly once; the previous secret stops verifying signatures the instant the rotate request returns. Update your receiver before rotating to avoid a window of rejected deliveries.