Documentation

Webhooks

Stream performance events to any HTTP endpoint. Every webhook is HMAC-signed and timestamped for verification.

Webhooks let you stream PagePulse events to any HTTP endpoint. Use them to trigger incident response, sync performance data to your data warehouse, or build custom alerts in your own tooling. Every webhook is HMAC-signed and timestamped for verification.

Subscribing to events

Configure webhook destinations from the dashboard, or programmatically via the API. You can subscribe to all event types or filter by type.

typescript
await fetch('https://api.pagepulse.dev/v2/webhooks', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer ' + apiKey,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    projectId: 'site_123',
    url: 'https://api.example.com/pagepulse',
    events: [
      'budget.breached',
      'budget.recovered',
      'deployment.regression',
      'deployment.improvement',
      'alert.fired',
    ],
  }),
});

Event payload

Every webhook delivery is a POST request with a JSON body. The body includes the event type, the timestamp, the project ID, and a data object whose shape depends on the event type.

json
{
  "id": "evt_abc123",
  "type": "budget.breached",
  "createdAt": "2025-06-24T14:12:03Z",
  "projectId": "site_123",
  "data": {
    "budget": "lcp",
    "route": "/checkout",
    "threshold": 2500,
    "actual": 4100,
    "delta": 1600
  }
}

Verifying the signature

Every webhook delivery includes a PagePulse-Signature header. The header is formatted as t=<timestamp>,v1=<signature>. To verify, compute the HMAC-SHA256 of the timestamp concatenated with the request body, using your webhook signing secret as the key.

typescript
import crypto from 'crypto';

function verifySignature(
  body: string,
  signatureHeader: string,
  secret: string
): boolean {
  const parts = signatureHeader.split(',');
  const timestamp = parts.find(p => p.startsWith('t='))?.slice(2);
  const signature = parts.find(p => p.startsWith('v1='))?.slice(3);

  if (!timestamp || !signature) return false;

  const expected = crypto
    .createHmac('sha256', secret)
    .update(timestamp + body)
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

Retries

If your endpoint responds with a non-2xx status code, PagePulse will retry the delivery with exponential backoff for up to 24 hours. After 24 hours, the delivery is marked as failed and the event is available for manual replay from the dashboard.

Your endpoint must respond within 10 seconds with a 2xx status code to be considered successful.

Have a question about this doc?