TransaktDocs
Guides

Verifying webhooks

Check that every webhook message comes from Transakt, with the Standard Webhooks libraries.

Transakt signs every webhook message following the Standard Webhooks specification, so you can verify it with the official libraries. Register endpoints in the Admin Portal (Partner settings › Integrations › Webhooks); each endpoint has its own signing secret, whsec_…, shown once when you create or rotate it.

What a message looks like

Every message is an HTTP POST with a JSON body (envelope and events) and these headers:

HeaderValue
webhook-idThe message id, msg_…. The same on every retry and replay: use it to deduplicate.
webhook-timestampUnix time in seconds of this attempt.
webhook-signaturev1, and the base64 HMAC-SHA-256 of {webhook-id}.{webhook-timestamp}.{body}, keyed with the secret.
user-agentTransakt-Webhooks/1.0

The key is the secret's bytes: the part after whsec_, base64-decoded. During a secret rotation the header carries two signatures separated by a space (v1,<new> v1,<previous>); accept the message when any of them matches.

Rules for your receiver

  1. Verify the raw body. Compute the signature over the exact bytes received, before any JSON parsing.
  2. Compare in constant time, against each signature in the header.
  3. Reject old timestamps: more than 5 minutes from your clock.
  4. Answer quickly with a 2xx: an attempt fails after 10 seconds, and redirects are not followed. Queue the work and do it after acknowledging.
  5. Deduplicate by webhook-id: delivery is at least once.
  6. Ignore stale events: delivery is unordered. Keep the highest data.subscription.version applied per subscription and ignore lower ones, or re-read the entitlement.

Failed attempts are retried after 5 seconds, 5 minutes, 30 minutes, 2 hours, 5 hours, 10 hours and 10 hours. An endpoint that fails continuously for 5 days is turned off and your integration managers are emailed; failed deliveries from the last 30 days can be replayed from the Admin Portal.

Examples

With the standardwebhooks package and Express:

import express from 'express';
import { Webhook } from 'standardwebhooks';

const webhook = new Webhook(process.env.TRANSAKT_WEBHOOK_SECRET!); // whsec_…
const app = express();

// Keep the raw body: the signature covers the exact bytes.
app.post('/webhooks/transakt', express.raw({ type: 'application/json' }), (req, res) => {
  let event;
  try {
    event = webhook.verify(req.body.toString('utf8'), req.headers as Record<string, string>);
  } catch {
    return res.sendStatus(400);
  }
  res.sendStatus(204); // acknowledge first
  queue.push(event); // then handle it (deduplicate by event.id)
});

Testing your endpoint

On the endpoint's page in the Admin Portal, Send test event delivers a webhook.test message at once and shows the response. Every endpoint receives webhook.test, whatever event types it subscribed to. The delivery log shows each message, its attempts and the first part of your responses for 30 days.

Deliveries come from the platform's published egress addresses, if you need to allow-list them; no cookies or other credentials are ever sent.

On this page