Webhooks

Signed webhooks for finished transcriptions

Add a transcription webhook with webhook_url when you create a job. transcript1 sends a signed POST when the job completes or fails. Your code can wait for the event instead of polling.

  • transcription.completed and transcription.failed
  • HMAC-SHA256 signature on every delivery
  • Up to 6 attempts with exponential backoff

How it works

Two events
transcription.completed and transcription.failed. A canceled transcription sends no webhook.
Signed deliveries
Every request carries X-Transcript1-Signature with a timestamp and an HMAC-SHA256 of the raw body, keyed with your team's whsec_ secret.
Retries
Any 2xx counts as delivered. Otherwise transcript1 retries with 2, 4, 8, 16 and 32 minute waits, up to 6 attempts in total.
Safe by default
The URL must be HTTPS and resolve to a public address. It is checked when you create the transcription and at every delivery.
Small payloads
The event carries the transcription object without the transcript. Fetch the transcript by id in the format you need.

Create

Add a webhook_url to the request

Works on YouTube requests, chunked upload reservations and as a form field on one-shot uploads. It cannot be combined with reuse_recent.

Request body
{
  "platform": "youtube",
  "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
  "webhook_url": "https://example.com/hooks/transcript1"
}

Deliver

Receive the event

data is the transcription object without the transcript. Use data.id and type to ignore duplicate deliveries.

POST to your URL
X-Transcript1-Signature: t=<unix seconds>,v1=<hex digest>

{
  "type": "transcription.completed",
  "created_at": "2026-10-13T09:30:04.000Z",
  "data": {
    "id": "tr_8fK2Lm0PqRsTuVwXyZaBcD",
    "object": "transcription",
    "status": "completed",
    "transcript_source": "youtube_captions",
    "billed_minutes": 4,
    "error": null
  }
}

Verify

Verify the HMAC-SHA256 signature

Sign the string t, a dot, then the raw body with HMAC-SHA256 and your whsec_ secret. Compare in constant time. Reject timestamps far from the current time.

Node.js
const expected = crypto
  .createHmac('sha256', secret)
  .update(`${t}.${rawBody}`)
  .digest('hex');
const valid = v1.length === expected.length
  && crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(expected));

Start with 60 free minutes

60 free minutes after you verify your email. No card required.

Get your API key

Frequently asked questions

Something missing? Ask support

Where do I find my signing secret?

On the Webhooks page of the dashboard. It starts with whsec_, belongs to your team and can be rotated there at any time.

How do transcription webhook retries work?

A failed delivery is retried with exponential backoff, up to 6 attempts in total over about an hour. The timeout per attempt is 10 seconds and redirects are not followed.

Do webhooks work with MCP?

No. Webhooks are part of the REST API only. The MCP tools wait for the result or return an id to read later.