elevenlabs-webhooks-events
Implement ElevenLabs webhook HMAC signature verification and event handling. Use when setting up webhook endpoints for transcription completion, call recording, or agent conversation events from ElevenLabs. Trigger: "elevenlabs webhook", "elevenlabs events", "elevenlabs webhook signature", "handle elevenlabs notifications", "elevenlabs post-call webhook", "elevenlabs transcription webhook".
What this skill does
# ElevenLabs Webhooks & Events
## Overview
ElevenLabs webhooks send HTTP POST notifications when async operations complete. Supported event types include transcription completion, post-call data from Conversational AI agents, and call initiation failures. Webhooks use HMAC-SHA256 signatures for verification.
## Prerequisites
- ElevenLabs account (webhooks configured in Settings > Webhooks)
- HTTPS endpoint accessible from the internet
- Webhook secret (generated during webhook creation in dashboard)
## Instructions
### Step 1: Webhook Event Types
| Event Type | Payload | When Triggered |
|------------|---------|----------------|
| `post_call_transcription` | Full conversation transcript, analysis, metadata | After Conversational AI call ends |
| `post_call_audio` | Base64-encoded call audio, minimal metadata | After call ends (if audio recording enabled) |
| `call_initiation_failure` | Failure reason, metadata | When an outbound call fails to connect |
| `speech_to_text.completed` | Transcription result, word timestamps | Async STT job completes |
### Step 2: Webhook Setup
```bash
# Create webhook in ElevenLabs dashboard:
# Settings > Webhooks > Create Webhook
# - URL: https://your-app.com/webhooks/elevenlabs
# - Select event types to subscribe to
# - Copy the generated HMAC secret
```
### Step 3: HMAC Signature Verification
```typescript
// src/elevenlabs/webhook-verify.ts
import crypto from "crypto";
/**
* Verify the ElevenLabs-Signature header using HMAC-SHA256.
*
* Header format: t=<unix_timestamp>,v1=<hex_signature>
* Signed payload: "<timestamp>.<raw_body>"
*/
export function verifyWebhookSignature(
rawBody: string | Buffer,
signatureHeader: string,
secret: string
): { valid: boolean; reason?: string } {
if (!signatureHeader || !secret) {
return { valid: false, reason: "Missing signature header or secret" };
}
// Parse header: t=1234567890,v1=abcdef...
const parts = new Map(
signatureHeader.split(",").map(p => {
const [key, ...val] = p.split("=");
return [key, val.join("=")] as [string, string];
})
);
const timestamp = parts.get("t");
const signature = parts.get("v1");
if (!timestamp || !signature) {
return { valid: false, reason: "Malformed signature header" };
}
// Replay protection: reject if older than 5 minutes
const age = Math.floor(Date.now() / 1000) - parseInt(timestamp);
if (age > 300) {
return { valid: false, reason: `Timestamp too old: ${age}s` };
}
// Compute expected HMAC
const signedPayload = `${timestamp}.${rawBody.toString()}`;
const expected = crypto
.createHmac("sha256", secret)
.update(signedPayload)
.digest("hex");
// Timing-safe comparison
try {
const isValid = crypto.timingSafeEqual(
Buffer.from(signature, "hex"),
Buffer.from(expected, "hex")
);
return { valid: isValid };
} catch {
return { valid: false, reason: "Signature length mismatch" };
}
}
```
### Step 4: Express Webhook Handler
```typescript
// src/api/webhooks/elevenlabs.ts
import express from "express";
import { verifyWebhookSignature } from "../../elevenlabs/webhook-verify";
const router = express.Router();
// CRITICAL: Use raw body parser for signature verification
router.post("/webhooks/elevenlabs",
express.raw({ type: "application/json" }),
async (req, res) => {
const signature = req.headers["elevenlabs-signature"] as string;
const secret = process.env.ELEVENLABS_WEBHOOK_SECRET!;
const { valid, reason } = verifyWebhookSignature(req.body, signature, secret);
if (!valid) {
console.error("Webhook verification failed:", reason);
return res.status(401).json({ error: "Invalid signature" });
}
// Return 200 immediately to prevent webhook auto-disable
res.status(200).json({ received: true });
// Process asynchronously
const event = JSON.parse(req.body.toString());
processEvent(event).catch(err =>
console.error("Webhook processing failed:", err)
);
}
);
// Event routing
async function processEvent(event: any) {
const eventType = event.type || event.event_type;
switch (eventType) {
case "post_call_transcription":
await handleTranscription(event);
break;
case "post_call_audio":
await handleCallAudio(event);
break;
case "call_initiation_failure":
await handleCallFailure(event);
break;
case "speech_to_text.completed":
await handleSTTCompleted(event);
break;
default:
console.log("Unhandled event type:", eventType);
}
}
```
### Step 5: Event Handlers
```typescript
// Conversational AI post-call transcript
async function handleTranscription(event: any) {
const {
conversation_id,
transcript, // Full conversation text
analysis, // AI analysis of the call
metadata, // Custom metadata from agent config
recording_url, // Audio recording URL (if enabled)
} = event.data;
console.log(`[Transcript] Conversation ${conversation_id}`);
console.log(`Transcript: ${transcript?.substring(0, 200)}...`);
// Store in your database
// await db.conversations.upsert({ conversation_id, transcript, analysis });
}
// Post-call audio recording
async function handleCallAudio(event: any) {
const {
conversation_id,
audio_base64, // Base64-encoded audio of the full conversation
} = event.data;
if (audio_base64) {
const audioBuffer = Buffer.from(audio_base64, "base64");
console.log(`[Audio] Received ${audioBuffer.length} bytes for ${conversation_id}`);
// Save audio: await fs.writeFile(`recordings/${conversation_id}.mp3`, audioBuffer);
}
}
// Failed outbound call
async function handleCallFailure(event: any) {
const {
conversation_id,
failure_reason,
metadata,
} = event.data;
console.error(`[Call Failed] ${conversation_id}: ${failure_reason}`);
// Alert: await alerting.notify("Call initiation failed", { conversation_id, failure_reason });
}
// Async Speech-to-Text completion
async function handleSTTCompleted(event: any) {
const {
transcription_id,
text,
words, // Word-level timestamps
language,
} = event.data;
console.log(`[STT Complete] ${transcription_id}: ${language}`);
console.log(`Text: ${text?.substring(0, 200)}...`);
// Process transcription results
}
```
### Step 6: Idempotency Protection
```typescript
// Prevent duplicate processing if ElevenLabs retries delivery
const processedEvents = new Set<string>();
async function withIdempotency(
eventId: string,
handler: () => Promise<void>
): Promise<void> {
if (processedEvents.has(eventId)) {
console.log(`Event ${eventId} already processed, skipping`);
return;
}
await handler();
processedEvents.add(eventId);
// Clean up old entries (in production, use Redis with TTL)
if (processedEvents.size > 10000) {
const oldest = Array.from(processedEvents).slice(0, 5000);
oldest.forEach(id => processedEvents.delete(id));
}
}
```
### Step 7: Local Testing with ngrok
```bash
# Expose local server to internet
ngrok http 3000
# Use the ngrok URL as webhook endpoint in ElevenLabs dashboard
# https://abc123.ngrok.io/webhooks/elevenlabs
# Test with curl (simulated event)
curl -X POST http://localhost:3000/webhooks/elevenlabs \
-H "Content-Type: application/json" \
-H "ElevenLabs-Signature: t=$(date +%s),v1=test" \
-d '{"type":"speech_to_text.completed","data":{"text":"Hello world"}}'
```
## Webhook Reliability
| Behavior | Detail |
|----------|--------|
| Retry policy | ElevenLabs retries failed deliveries |
| Auto-disable | After 10 consecutive failures AND 7+ days since last success |
| Timeout | Your endpoint must respond within a few seconds |
| Re-enable | Manually re-enable in dashboard after fixing the endpoint |
| Authentication | HMAC-SHA256 via `ElevenLabs-Signature` header |
## Error Handling
| Issue | Cause | Solution |
|-------|-------|----------|
| Signature mRelated in Image & Video
watch
IncludedWatch a video (URL or local path). Downloads with yt-dlp, extracts auto-scaled frames with ffmpeg, pulls the transcript from captions (or Whisper API fallback), and hands the result to Claude so it can answer questions about what's in the video.
physical-ai-defect-image-generation
IncludedUse when the user wants to orchestrate defect image generation, run associated setup, or handle outputs on OSMO. The Day 0 path handles cold-start with USD-to-ROI, image-edit augmentation, and AnomalyGen to create initial PCBA datasets. The Day 1 path performs inference and labeling on real images. This skill helps with first-time asset setup, creation of finetuning checkpoints, and configuring deployment. Trigger keywords: defect image generation, dig workflow, dig pipeline, defect image detection workflow, aoi pipeline, aoi anomalygen, usd2roi anomalygen, day 0 pcba, day 1 pcba, day 1 real-photo alignment, day 1 manual roi, metal surface anomaly, glass defect, anomalygen finetune, setup_pcb, setup_metal, setup_glass, setup_pretrained, dig setup, dig datasets, dig pretrained checkpoint, dig image-edit endpoint.
accelint-react-best-practices
IncludedReact performance optimization and best practices. ALWAYS use this skill when working with any React code - writing components, hooks, JSX; refactoring; optimizing re-renders, memoization, state management; reviewing for performance; fixing hydration mismatches; debugging infinite re-renders, stale closures, input focus loss, animations restarting; preventing remounting; implementing transitions, lazy initialization, effect dependencies. Even simple React tasks benefit from these patterns. Covers React 19+ (useEffectEvent, Activity, ref props). Triggers - useEffect, useState, useMemo, useCallback, memo, inline components, nested components, components inside components, re-render, performance, hydration, SSR, Next.js, useDeferredValue, combined hooks.
elevenlabs-agents
IncludedBuild conversational AI voice agents with ElevenLabs Platform using React, JavaScript, React Native, or Swift SDKs. Configure agents, tools (client/server/MCP), RAG knowledge bases, multi-voice, and Scribe real-time STT. Use when: building voice chat interfaces, implementing AI phone agents with Twilio, configuring agent workflows or tools, adding RAG knowledge bases, testing with CLI "agents as code", or troubleshooting deprecated @11labs packages, Android audio cutoff, CSP violations, dynamic variables, or WebRTC config. Keywords: ElevenLabs Agents, ElevenLabs voice agents, AI voice agents, conversational AI, @elevenlabs/react, @elevenlabs/client, @elevenlabs/react-native, @elevenlabs/elevenlabs-js, @elevenlabs/agents-cli, elevenlabs SDK, voice AI, TTS, text-to-speech, ASR, speech recognition, turn-taking model, WebRTC voice, WebSocket voice, ElevenLabs conversation, agent system prompt, agent tools, agent knowledge base, RAG voice agents, multi-voice agents, pronunciation dictionary, voice speed control, elevenlabs scribe, @11labs deprecated, Android audio cutoff, CSP violation elevenlabs, dynamic variables elevenlabs, case-sensitive tool names, webhook authentication
humanizer
IncludedHumanize AI-generated text by detecting and removing patterns typical of LLM output. Rewrites text to sound natural, specific, and human. Uses 28 pattern detectors, 560+ AI vocabulary terms across 3 tiers, and statistical analysis (burstiness, type-token ratio, readability) for comprehensive detection. Use when asked to humanize text, de-AI writing, make content sound more natural/human, review writing for AI patterns, score text for AI detection, or improve AI-generated drafts. Covers content, language, style, communication, and filler categories.
generating-mermaid-diagrams
IncludedSalesforce architecture diagrams using Mermaid with ASCII fallback. Use this skill when generating text-based diagrams for Salesforce architecture, OAuth flows, ERDs, integration sequences, or Agentforce structure. TRIGGER when: user says "diagram", "visualize", "ERD", or asks for sequence diagrams, flowcharts, class diagrams, or architecture visualizations in Mermaid. DO NOT TRIGGER when: user wants PNG/SVG image output (use generating-visual-diagrams), or asks about non-Salesforce systems.