Skip to content
Pathbound DOCS

Webhooks

Pathbound can deliver any tracked event to your own URLs. Use webhooks to keep an external system in sync — fire a Slack message when a high-intent visitor identifies, push form submissions to your data warehouse, trigger a workflow when a contact’s lifecycle stage changes.

In the dashboard:

  1. Open Settings → Webhooks and click New webhook.
  2. Pick the event type to subscribe to. Any event your tenant has ever recorded is selectable (page views, form submissions, custom track() events, lifecycle changes, etc.).
  3. Set the destination URL. It must be https:// and reachable from the public internet — local URLs and internal IP ranges are rejected.
  4. Optionally add URL filters (AND / OR patterns) to scope deliveries — e.g. only deliver page_view events whose URL contains /pricing.
  5. Optionally add custom headers that should be sent with each request (encrypted at rest).
  6. Save. Copy the signing secret — it is shown once.

Each tenant can have up to 25 active subscriptions.

Pathbound sends a POST to your destination URL with a JSON body and these headers:

Content-Type: application/json
X-Pathbound-Signature: sha256=<hex>
X-Pathbound-Event: page_view
X-Pathbound-Subscription: sub_abc

Plus any custom headers you configured.

{
"event": "page_view",
"subscription_id": "sub_abc",
"event_id": "evt_xyz",
"tenant_id": "tenant_123",
"timestamp": "2026-04-29T12:00:00.000Z",
"data": {
"url": "https://example.com/pricing",
"domain": "example.com",
"title": "Pricing — Example",
"path": "/pricing",
"visitorId": "vis_123",
"internal_contact_id": "ct_abc",
"contact": {
"_id": "ct_abc",
"email": "[email protected]",
"firstname": "Ada"
}
}
}

The exact data shape varies by event type — it mirrors the response of GET /v1/events/:event_id plus any joined contact/company.

Compute an HMAC-SHA256 of the raw request body (not the parsed JSON) using your subscription’s signing secret. Compare it to the value in X-Pathbound-Signature (after stripping the sha256= prefix). Use a constant-time comparison.

Node
import crypto from 'node:crypto';
function verify(rawBody, header, secret) {
if (!header?.startsWith('sha256=')) return false;
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex');
const provided = header.slice('sha256='.length);
if (provided.length !== expected.length) return false;
return crypto.timingSafeEqual(Buffer.from(provided), Buffer.from(expected));
}
Python
import hmac, hashlib
def verify(raw_body: bytes, header: str, secret: str) -> bool:
if not header.startswith('sha256='):
return False
expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
provided = header[len('sha256='):]
return hmac.compare_digest(expected, provided)

If you can’t access the raw body in your framework (e.g. Express that already parsed JSON), capture it before parsing — re-stringifying does not preserve byte-for-byte equality.

Pathbound expects a 2xx response within 10 seconds. Anything else (timeout, 4xx, 5xx) is retried with exponential backoff:

AttemptDelay after previous
1(initial delivery)
21 minute
35 minutes
430 minutes
52 hours
612 hours

After 6 failed attempts, the delivery is marked failed and not retried further. The subscription itself stays active.

A 2xx response is treated as success regardless of body. Non-2xx responses with body "ignore" (case-insensitive) are also treated as success — useful for filtering at the receiver without burning retries.

The dashboard shows the recent delivery history per subscription — destination response code, latency, body, retry count. Replay a failed delivery from the dashboard with the Re-send button.

  • Treat the destination URL as public. Anyone with the URL can attempt delivery; the signature is your only authenticator. Verify it on every request.
  • Process asynchronously. Acknowledge with 200 OK quickly, then process the event in a background job. Long synchronous handlers eat into the 10 s window.
  • Be idempotent. Pathbound delivers at-least-once. Use event_id as a dedup key.
  • Log X-Pathbound-Event and event_id so you can correlate with the dashboard delivery log.