Skip to main content

HMAC Request Signing

Every callback we send to your endpoint is signed using HMAC-SHA256. This lets you verify that the request genuinely came from us and has not been tampered with.

Signature Headers

Each signed callback includes two additional HTTP headers:

How the Signature Is Computed

The signature is computed over the timestamp and the raw request body: HMAC-SHA256(api_client_secret, "{X-Timestamp}.{body}")\text{HMAC-SHA256}(\text{api\_client\_secret},\ \texttt{"\{X\text{-}Timestamp\}.\{body\}"}) where body is the exact JSON string of the request body (no reformatting or whitespace changes).

Verifying the Signature

Follow these steps in your callback handler to authenticate each inbound request:
1

Read the headers

Extract the X-Timestamp and X-Signature headers from the incoming request.
2

Check the timestamp

Reject the request if X-Timestamp is too old or too far in the future (we recommend a 5-minute tolerance window). This protects against replay attacks where an attacker resends a previously captured request.
3

Recompute the signature

Using your API client secret (UTF-8 encoded), compute:
Make sure you use the raw, unmodified request body bytes — do not parse and re-serialize the JSON.
4

Compare the signatures

Base64-encode your computed HMAC, then compare it against the value in X-Signature (strip the sha256= prefix first). If they match, the request is authentic. If they don’t match, reject it with 401 Unauthorized.
When you regenerate your API client secret, signed callbacks may continue to be generated with your previous secret for up to 5 minutes. During this propagation window, verify signatures against both the old and new secrets.

Code Examples

Always use a timing-safe comparison (e.g. crypto.timingSafeEqual, hmac.compare_digest, hash_equals) when comparing signatures. Regular string equality is vulnerable to timing attacks.

IP Whitelisting

As an additional layer of defense, you can restrict your callback endpoint to only accept inbound requests from our IP address ranges. Any request arriving from an address outside this list can be rejected at the network or application level before your handler code even runs.

Our Callback IP Ranges

All outbound callback requests originate from the following IP addresses:

Circuit Breaking for Unreachable Callback URLs

To protect both your infrastructure and ours from cascading failures, we apply a per-URL circuit breaker to all outbound callbacks. If your endpoint becomes temporarily unavailable, the circuit breaker automatically backs off and retries without flooding your server.

Circuit Breaker States

Each callback URL you register gets its own independent circuit breaker with three states:

Configuration

The circuit breaker behaviour is governed by the following parameters:

Backoff Sequence

With the default settings, successive circuit opens produce the following reset timeouts (before jitter is applied):
The ±20 % jitter means the actual wait time varies slightly around each value above — for example, a 4-minute timeout becomes somewhere between 3 min 12 s and 4 min 48 s. This prevents multiple circuit breakers from all attempting recovery at exactly the same moment.

Recommendations

  • Return HTTP 200 quickly. Acknowledge the callback as soon as you receive it and process it asynchronously. This keeps your response time well under the 5-second call timeout.
  • Keep your endpoint healthy. A single URL accumulates failures independently — a slow endpoint will trip its own breaker without affecting other URLs you have registered.
  • Monitor for silent skips. While the circuit is open, callbacks are not delivered and are not retried after the circuit closes. Make sure your integration does not rely solely on callbacks for critical state updates.