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.
{
"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.
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.
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.
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.