Skip to main content
Hopae Connect signs every outgoing webhook delivery with an HMAC-SHA256 signature so you can verify that requests genuinely come from Hopae and have not been tampered with in transit.

How signing works

Each delivery includes an X-Hopae-Signature header with two comma-separated fields:
The signed payload is constructed as:
where <raw-request-body> is the exact UTF-8 bytes of the JSON body received in the HTTP request.

Verifying the signature

Always use the raw request body bytes for signature verification. Do not parse the JSON and re-stringify it — serialisation order differences will cause the comparison to fail.

End-to-end Express example

Use express.raw() middleware on the webhook route so the handler receives the untouched body. Express’s default JSON parser destroys the exact bytes the signature was computed over.

Event types

Subscribe to any of these event types via the events array on PATCH /apps/{clientId}/webhook-config. Each delivery includes the event type in the event field of the payload. Each payload has the shape:
The top-level event is a string; the nested data.event is an object with its own type, timestamp, and optional metadata — they carry the same event type value, but data.event.metadata lets you pass extra context through the delivery pipeline.

Obtaining your webhook secret

Webhook secrets are provisioned server-side when you call POST /apps/{client_id}/webhook-config/rotate-secret. Store the secret securely (e.g. as an environment variable). The secret is also available via GET /apps/{clientId}/webhook-config.

Replay protection

The t timestamp in the signature header reflects the moment the delivery was sent. Hopae recommends rejecting webhooks where |now - t| > 300 seconds (5 minutes) to prevent replay attacks, as shown in the examples above.

Backwards compatibility

Apps that have never called rotate-secret do not have a signing secret configured. For those apps, Hopae delivers webhooks without an X-Hopae-Signature header. Call rotate-secret once to enable signing on your app.