Webhooks
Webhooks deliver supported CHAMPREP events to your HTTPS endpoint. They reduce polling and let integrations respond to changes as they happen.
Webhook management requires the webhooks:manage scope.
Management endpoints
Section titled “Management endpoints”| Method | Path | Purpose |
|---|---|---|
GET |
/v1/webhooks/events |
List the event types currently supported. |
GET |
/v1/webhooks |
List configured webhook endpoints. |
POST |
/v1/webhooks |
Register an HTTPS endpoint and event list. |
PATCH |
/v1/webhooks/{webhookId} |
Update the URL, events, description, or active state. |
DELETE |
/v1/webhooks/{webhookId} |
Delete an endpoint. |
POST |
/v1/webhooks/{webhookId}/rotate-secret |
Rotate its signing secret. |
Query /v1/webhooks/events instead of hard-coding an event catalog. Supported
event types can expand as services add public events.
Register an endpoint
Section titled “Register an endpoint”curl --fail-with-body \ --request POST \ --header "Authorization: Bearer $CHAMPREP_TOKEN" \ --header "Content-Type: application/json" \ --data '{ "url": "https://example.com/webhooks/champrep", "events": ["contact.created"], "description": "Contact synchronization" }' \ https://api.champrep.com/v1/webhooksThe signing secret is returned when the endpoint is created or its secret is rotated. Store it immediately in a secret manager; it is not a bearer API key and should be unique to the webhook endpoint.
Delivery format
Section titled “Delivery format”Each delivery is an HTTPS POST with a JSON body:
{ "id": "delivery_identifier", "type": "contact.created", "timestamp": "2026-08-08T12:00:00Z", "data": {}}Delivery headers include:
| Header | Purpose |
|---|---|
X-CHAMPREP-Signature |
HMAC SHA-256 signature in sha256=<hex> form. |
X-CHAMPREP-Timestamp |
Unix timestamp used in the signed content. |
X-CHAMPREP-Delivery-Id |
Unique identifier for deduplication. |
X-CHAMPREP-Event |
Event type for routing. |
Verify the signature
Section titled “Verify the signature”Compute HMAC SHA-256 over the exact UTF-8 bytes of
<timestamp>.<raw-request-body> using the endpoint’s signing secret. Compare
the expected and received signatures with a constant-time comparison.
import crypto from 'node:crypto';
export function verifyChamprepWebhook(rawBody, headers, secret) { const timestamp = String(headers['x-champrep-timestamp'] || ''); const received = String(headers['x-champrep-signature'] || ''); const expected = `sha256=${crypto .createHmac('sha256', secret) .update(`${timestamp}.${rawBody}`) .digest('hex')}`;
const receivedBytes = Buffer.from(received); const expectedBytes = Buffer.from(expected); return receivedBytes.length === expectedBytes.length && crypto.timingSafeEqual(receivedBytes, expectedBytes);}Verify the raw body before a framework parses and re-serializes it. A re-serialized object can produce different bytes and fail verification.
Prevent replay and duplicate work
Section titled “Prevent replay and duplicate work”- Reject a timestamp outside your chosen short tolerance window.
- Store processed delivery IDs for at least the duration of your retry window.
- Treat a repeated delivery ID as already processed and return a successful response.
- Perform business work idempotently whenever possible.
Delivery behavior
Section titled “Delivery behavior”Return a successful 2xx response promptly and move expensive work to a queue.
Timeouts, network failures, 429, and server errors can be retried. Persistent
failures can cause an endpoint to be disabled, so monitor delivery state and
correct failing URLs promptly.
Do not log signing secrets or complete sensitive payloads. Rotate the secret if it may have been exposed, then update the receiver before accepting new deliveries.