Skip to main content
Every webhook is signed with the signing secret of its endpoint. Verify the signature before you process or store anything from the request, and reject requests that fail.

Algorithm

  1. Read the headers X-Talario-Timestamp, X-Talario-Delivery-Id, X-Talario-Key-Id and X-Talario-Signature.
  2. Select the signing secret for X-Talario-Key-Id. Reject unknown key IDs.
  3. Reject the request if the timestamp differs from your clock by more than 5 minutes. Keep your server clock synchronized (NTP).
  4. Decode the secret from base64url (without padding) to its 32 raw bytes. Use these bytes as the HMAC key, not the secret string.
  5. Build the signed content from the timestamp, the delivery ID and the raw request body, joined by dots:
  6. Compute HMAC-SHA256 and encode it as lowercase hex.
  7. Compare it with the value after v1= in X-Talario-Signature using a constant-time comparison.
Use the body bytes exactly as received. Do not decode and re-encode the JSON before verifying: a different key order, whitespace or escaping changes the signature.
The signature covers the timestamp and the delivery ID, so a captured request cannot be replayed later or for a different delivery. Each attempt has a new timestamp and a new signature.

Laravel

Store the secret and its key ID in your configuration:
config/services.php
Verify the signature in a middleware:
app/Http/Middleware/VerifyTalarioWebhook.php
Register the route in routes/api.php, which has no CSRF protection:
routes/api.php
If your application has no routes/api.php yet, create it with php artisan install:api. If you register the route in routes/web.php instead, exclude it from CSRF protection in bootstrap/app.php with $middleware->validateCsrfTokens(except: ['webhooks/talario']).

Plain PHP

Header names are case-insensitive. Normalize them before the lookup if your framework does not.

Rotate the secret

In the dashboard, choose Generate new secret for the endpoint. Talario shows the new secret and its new key ID once. The mail servers sign with the new secret as soon as they have applied the change, usually within a minute; attempts already running may still use the previous secret.
  1. Add the new key ID and secret to your configuration, keeping the previous one.
  2. Generate the new secret in the dashboard and deploy the configuration.
  3. Remove the previous secret once deliveries arrive with the new key ID.
Because Talario selects the secret by X-Talario-Key-Id, both secrets work during the change.

Test your endpoint with cURL

Send a signed test request to your endpoint to check your verification. You need openssl and the secret of the endpoint:
Use --data-binary, not -d: -d removes line breaks and changes the signed bytes. Your endpoint should answer with 2xx, and with 401 after you change a single character in payload.json without signing again.
A test request with a made-up message ID is not a real message: downloads and API calls for it return 404.