Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Implement AssemblyAI webhook handling for transcription completion events. Use when setting up webhook endpoints, handling transcription callbacks, or processing async transcription results via webhooks. Trigger with phrases like "assemblyai webhook", "assemblyai events", "assemblyai transcription callback", "handle assemblyai webhook".
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-10 | ✗→✓ | ▲ Improved | 61% | 0% |
| case-01 | ✗→✓ | ▲ Improved | 60% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 95% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 85% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 65% | 0% |
Handle AssemblyAI webhooks for transcription completion. When you submit a transcript with webhook_url, AssemblyAI sends a POST request to your URL when the transcript is completed or fails. One webhook per transcript — no complex event routing needed.
assemblyai package installedwebhook_url parameterKey difference from other APIs: AssemblyAI webhooks are per-transcript (set at submission time), not a global webhook registration. There are no event types to subscribe to — you get one callback per transcript.
typescriptimport { AssemblyAI } from 'assemblyai'; const client = new AssemblyAI({ apiKey: process.env.ASSEMBLYAI_API_KEY!, }); // submit() queues the job and returns immediately (doesn't poll) const transcript = await client.transcripts.submit({ audio: 'https://example.com/meeting-recording.mp3', webhook_url: 'https://your-app.com/webhooks/assemblyai', // Optional: auth header for webhook verification webhook_auth_header_name: 'X-Webhook-Secret', webhook_auth_header_value: process.env.ASSEMBLYAI_WEBHOOK_SECRET!, // Enable features — results will be available when webhook fires speaker_labels: true, sentiment_analysis: true, auto_highlights: true, }); console.log('Submitted:', transcript.id); // Returns immediately, webhook fires when processing completes
typescriptimport express from 'express'; import { AssemblyAI, type Transcript } from 'assemblyai'; const app = express(); const client = new AssemblyAI({ apiKey: process.env.ASSEMBLYAI_API_KEY!, }); app.post('/webhooks/assemblyai', express.json(), async (req, res) => { // Step 1: Verify authenticity via custom auth header const secret = req.headers['x-webhook-secret']; if (secret !== process.env.ASSEMBLYAI_WEBHOOK_SECRET) { console.warn('Webhook auth failed'); return res.status(401).json({ error: 'Unauthorized' }); } // Step 2: Extract payload const { transcript_id, status } = req.body; console.log(`Webhook received: ${transcript_id} — ${status}`); // Step 3: Respond quickly (within 10 seconds) res.status(200).json({ received: true }); // Step 4: Process asynchronously try { if (status === 'completed') { const transcript = await client.transcripts.get(transcript_id); await processCompletedTranscript(transcript); } else if (status === 'error') { await handleFailedTranscript(transcript_id, req.body.error); } } catch (error) { console.error('Webhook processing error:', error); } }); async function processCompletedTranscript(transcript: Transcript) { console.log(`Processing transcript ${transcript.id}:`); console.log(` Text: ${transcript.text?.length} chars`); console.log(` Duration: ${transcript.audio_duration}s`); console.log(` Speakers: ${transcript.utterances?.length ?? 0} utterances`); // Store in database, notify user, trigger LeMUR analysis, etc. // Example: Run LeMUR summarization after transcription completes if (transcript.text && transcript.text.length > 100) { const { response } = await client.lemur.summary({ transcript_ids: [transcript.id], answer_format: 'bullet points', }); console.log('Auto-summary:', response); } } async function handleFailedTranscript(transcriptId: string, error?: string) { console.error(`Transcript ${transcriptId} failed: ${error}`); // Alert ops team, retry with different settings, etc. } app.listen(3000, () => console.log('Listening on :3000'));
typescript// app/api/webhooks/assemblyai/route.ts import { AssemblyAI } from 'assemblyai'; import { NextRequest, NextResponse } from 'next/server'; const client = new AssemblyAI({ apiKey: process.env.ASSEMBLYAI_API_KEY!, }); export async function POST(req: NextRequest) { const secret = req.headers.get('x-webhook-secret'); if (secret !== process.env.ASSEMBLYAI_WEBHOOK_SECRET) { return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); } const body = await req.json(); const { transcript_id, status } = body; if (status === 'completed') { const transcript = await client.transcripts.get(transcript_id); // Process transcript... console.log(`Completed: ${transcript_id}, ${transcript.text?.length} chars`); } return NextResponse.json({ received: true }); }
typescript// Prevent duplicate processing if webhook is retried const processedTranscripts = new Set<string>(); // In production, use Redis or a database instead of in-memory Set async function idempotentProcess(transcriptId: string, handler: () => Promise<void>) { if (processedTranscripts.has(transcriptId)) { console.log(`Already processed: ${transcriptId}`); return; } await handler(); processedTranscripts.add(transcriptId); } // Usage in webhook handler: await idempotentProcess(transcript_id, async () => { const transcript = await client.transcripts.get(transcript_id); await processCompletedTranscript(transcript); });
bash# Option 1: ngrok ngrok http 3000 # Use the HTTPS URL as your webhook_url # Option 2: Simulate webhook manually curl -X POST http://localhost:3000/webhooks/assemblyai \ -H "Content-Type: application/json" \ -H "X-Webhook-Secret: your-secret" \ -d '{ "transcript_id": "test-id-123", "status": "completed" }'
AssemblyAI sends a POST with this JSON body:
json{ "transcript_id": "6wij2z3g66-...", "status": "completed" }
For errors:
json{ "transcript_id": "6wij2z3g66-...", "status": "error", "error": "Download error: unable to download audio from URL" }
If redact_pii_audio was enabled, a second webhook fires when redacted audio is ready.
| Issue | Cause | Solution | |-------|-------|----------| | Webhook not received | URL not accessible from internet | Verify HTTPS URL, check firewall | | 401 on webhook | Wrong auth header value | Match webhook_auth_header_value from submission | | Duplicate processing | Webhook retried after timeout | Implement idempotency (check transcript_id) | | Webhook timeout | Processing > 10 seconds | Return 200 immediately, process async | | Missing transcript data | Fetching too early | Fetch with client.transcripts.get() after webhook |
For performance optimization, see assemblyai-performance-tuning.
Other measured skills in the registry, with their headline benchmark lift.