Webhooks

Register an endpoint in Developers. Every delivery is signed with HMAC-SHA256 over timestamp.body and retried five times with backoff.

Events

Envelope

POST https://your-app.com/hooks/transcriptanything
x-td-event: transcription.completed
x-td-delivery: evt_…
x-td-signature: t=1761600000,v1=5f2c…

{
  "id": "evt_…",
  "type": "transcription.completed",
  "created_at": "2026-10-27T09:00:00.000Z",
  "data": {
    "transcription_id": "trj_…",
    "status": "succeeded",
    "duration_seconds": 842,
    "charged_credits": 41,
    "result": {
      "json": "/api/v1/transcriptions/trj_…/result?format=json",
      "srt": "/api/v1/transcriptions/trj_…/result?format=srt",
      "txt": "/api/v1/transcriptions/trj_…/result?format=txt",
      "vtt": "/api/v1/transcriptions/trj_…/result?format=vtt"
    }
  }
}

Verify the signature

// Node/Workers: the secret is returned once when the endpoint is created
const [timestampPart, signaturePart] = header.split(",");
const timestamp = Number(timestampPart.split("=")[1]);
const signature = signaturePart.split("=")[1];
if (Math.abs(Date.now() / 1000 - timestamp) > 300) return reject("stale");

const key = await crypto.subtle.importKey(
  "raw",
  new TextEncoder().encode(secret),
  { name: "HMAC", hash: "SHA-256" },
  false,
  ["sign"],
);
const mac = await crypto.subtle.sign(
  "HMAC",
  key,
  new TextEncoder().encode(timestamp + "." + rawBody),
);
const expected = [...new Uint8Array(mac)].map((b) => b.toString(16).padStart(2, "0")).join("");
if (expected !== signature) return reject("bad signature");

Return 2xx within 5 seconds. Twenty consecutive failures disable the endpoint and you get an email. Re-enable it after fixing the receiver in Developers → Webhooks.