Apply webhooks
In webhook mode (applyMode = 'webhook'), a completed import is delivered to your backend as
an HTTP POST — HMAC-signed, retried durably, and recorded in a delivery audit log.
Register your endpoint
Section titled “Register your endpoint”Register the URL once; the response carries the signing secret exactly once — store it. Rotating the secret re-issues it the same way.
POST /api/import/v1/webhookscontent-type: application/json
{ "url": "https://api.your-app.example/mildport/apply" }The request
Section titled “The request”POST /your/apply-endpointx-import-event: import.applyx-import-delivery-id: dlv_4f2…x-import-timestamp: 1717000000x-import-signature: t=…,v1=hmac_sha256(body)…content-type: application/json{ "event": "import.apply", "deliveryId": "dlv_4f2…", "tenantId": "tnt_…", "recordId": "rec_…", "action": "submit", "rowCount": 128, "mapping": { "Email": "person.email", "First name": "person.firstName" }, "occurredAt": "2026-07-12T00:00:00.000Z"}meta (host-supplied context), entities and links (the resolved reference graph) appear
when the import used them.
Verify the signature
Section titled “Verify the signature”Compute an HMAC-SHA256 over the raw request body using your delivery secret, and compare it in
constant time to the v1 value in x-import-signature. The t= value is the signed
timestamp — reject deliveries whose timestamp is too old to blunt replay attacks.
import { createHmac, timingSafeEqual } from 'node:crypto';
function verify(rawBody: string, header: string, secret: string): boolean { const parts = Object.fromEntries(header.split(',').map(kv => kv.split('='))); const expected = createHmac('sha256', secret).update(`${parts.t}.${rawBody}`).digest('hex'); const got = Buffer.from(parts.v1 ?? '', 'hex'); const exp = Buffer.from(expected, 'hex'); return got.length === exp.length && timingSafeEqual(got, exp);}Retries & idempotency
Section titled “Retries & idempotency”Deliveries retry with backoff until your endpoint returns 2xx — a 4xx is terminal (the
body was rejected; retrying won’t change it). Dedupe on deliveryId (also carried as the
x-import-delivery-id header) — the same import may be delivered more than once.
Payload stability
Section titled “Payload stability”The headers, signature scheme, and payload fields above are part of the public contract: new fields may be added (ignore what you don’t recognize), but removals, renames, and scheme changes follow the versioning & breaking-change policy.