Skip to content
Docs

API reference

Webhooks and the firehose

Subscribe to conversion events with signed webhooks, verify signatures, handle retries, and stream everything with the firehose.

Webhooks push events to an HTTPS endpoint you control. Create them in Automation, Webhooks or with POST /webhooks (webhooks reference).

json
{ "name": "Warehouse sync", "url": "https://hooks.example.com/dtrackier", "events": ["conversion.created", "conversion.status_changed"], "secret": "choose-a-long-random-string", "status": "active" }

The secret is write-only. List the subscribable events with GET /webhooks/events; the catalogue includes a sample payload for each.

EventWebhookFirehoseWhen
conversion.createdyesyesA conversion was recorded.
conversion.status_changedyesyesA conversion moved between pending, approved, rejected or cancelled.
clicknoyesA click was tracked.
impressionnoyesAn impression was tracked.
postback.resultnoyesAn outbound partner postback attempt finished.

Envelope

Every delivery is a JSON document (webhook_envelope):

json
{
  "id": "5d1f0c9a7e3b4a6f8c2d1e0a",
  "tenant_id": "01J8TENANT",
  "event": "conversion.created",
  "created_at": "2026-10-18T10:15:00Z",
  "api_version": "v1",
  "data": { "conversion_id": "01J8CONV", "click_id": "01J8CLICK" }
}

Headers and signature

HeaderValue
X-Metrixed-Signaturet=<unix seconds>,v1=<hex HMAC-SHA256>
X-Metrixed-EventThe event name.
X-Metrixed-DeliveryAttempt number, starting at 1.
X-Metrixed-ReplayPresent on manual replays; the id of the replayed delivery.
User-AgentMetrixed-Webhooks/1

The signature is HMAC_SHA256(secret, t + "." + raw_body) over the exact bytes received. Verify it, and reject requests whose t is more than 300 seconds old:

js
import crypto from 'node:crypto';

export function verify(signatureHeader, rawBody, secret) {
  const [t, v1] = signatureHeader.split(',').map((p) => p.split('=')[1]);
  if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;
  const mac = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
  return mac.length === v1.length && crypto.timingSafeEqual(Buffer.from(mac), Buffer.from(v1));
}

GET /webhooks/signing returns this description from the API itself.

Retries and auto-disable

Respond with any 2xx within the timeout to acknowledge. Network errors and 5xx, 429 and 408 responses retry after 1 minute, 5 minutes, 30 minutes, 2 hours and 12 hours; other 4xx responses are final failures. After the last attempt the delivery is marked failed. After DT_WH_DISABLE_AFTER consecutive final failures (default 20) the webhook is disabled and a webhook_disabled notification is raised. Deliveries are at least once: use the envelope id to de-duplicate.

Search deliveries with POST /webhooks/{id}/deliveries/search (filter by status, event, response code and time) and re-send one with the replay operation; replays carry X-Metrixed-Replay, are not retried and do not count towards auto-disable.

Firehose

GET /firehose is a server-sent-events stream of the events marked "firehose" above, filtered with ?types=. It supports Last-Event-ID replay (the most recent 2000 events per tenant, held in memory per process) and requires a staff API key. Firehose sinks (/firehose/sinks) are meant to forward the same events to SQS, Pub/Sub or Kafka. Their publishers are placeholders today: configuration is validated and events are logged, but nothing is delivered. Use the SSE stream or webhooks until real clients land.

Internal events

The same payloads travel inside the platform on NATS JetStream. They are documented, with their JSON Schemas, in event and webhook payloads.