The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"name": "elevenlabs_post_call_webhook",
"description": null,
"active": true,
"nodes": [
{
"name": "Webhook",
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [
0,
0
],
"parameters": {
"httpMethod": "POST",
"path": "elevenlabs/post-call",
"responseMode": "responseNode",
"options": {
"rawBody": true
}
},
"id": "ce3232fa-39dc-4f31-9832-e24e467c2661"
},
{
"name": "Parse Signature",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
220,
0
],
"parameters": {
"language": "javaScript",
"jsCode": "// Extract t and v0 from the ElevenLabs-Signature header.\n// Build signing_string = \"${t}.${rawBody}\" \u2014 the input the Crypto node hashes.\n// No 'crypto' dependency here; n8n's sandbox blocks it.\nconst item = $input.first();\nconst headers = item.json.headers || {};\nconst sigHeader = headers['elevenlabs-signature'] || headers['ElevenLabs-Signature'] || '';\nconst rawBody = item.json.rawBody || (typeof item.json.body === 'string' ? item.json.body : JSON.stringify(item.json.body || item.json));\nconst parts = sigHeader.split(',').reduce((acc, p) => { const [k, v] = p.split('='); acc[k] = v; return acc; }, {});\nconst t = parts.t || '';\nconst v0 = parts.v0 || '';\nconst ageSeconds = t ? Math.abs(Math.floor(Date.now()/1000) - parseInt(t, 10)) : 999999;\nconst payload = typeof item.json.body === 'string' ? JSON.parse(item.json.body) : (item.json.body || item.json);\nreturn [{json: {\n signing_string: t + '.' + rawBody,\n t,\n v0_received: v0,\n age_seconds: ageSeconds,\n signature_present: Boolean(t && v0),\n payload,\n event_type: payload.type || 'unknown',\n received_at: new Date().toISOString()\n}}];"
},
"id": "d41b05ca-b3fb-4fd8-9903-c82cd3643b52"
},
{
"name": "HMAC Compute",
"type": "n8n-nodes-base.crypto",
"typeVersion": 1,
"position": [
440,
0
],
"parameters": {
"action": "hmac",
"type": "SHA256",
"value": "={{$json.signing_string}}",
"secret": "<REDACTED:elevenlabs-webhook-secret>",
"encoding": "hex",
"dataPropertyName": "v0_computed"
},
"id": "1ec47101-9aba-4cbf-ac1e-36eb029aed7e"
},
{
"name": "Verify Signature",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
660,
0
],
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict"
},
"conditions": [
{
"id": "sig-eq",
"leftValue": "={{$json.v0_computed}}",
"rightValue": "={{$json.v0_received}}",
"operator": {
"type": "string",
"operation": "equals"
}
},
{
"id": "sig-fresh",
"leftValue": "={{$json.age_seconds}}",
"rightValue": 1800,
"operator": {
"type": "number",
"operation": "lt"
}
}
],
"combinator": "and"
}
},
"id": "5c471f00-1071-416e-986e-5a468875e4c7"
},
{
"name": "Route by Event Type",
"type": "n8n-nodes-base.switch",
"typeVersion": 3,
"position": [
880,
-160
],
"parameters": {
"rules": {
"values": [
{
"conditions": {
"options": {},
"conditions": [
{
"id": "r-tr",
"leftValue": "={{$json.event_type}}",
"rightValue": "post_call_transcription",
"operator": {
"type": "string",
"operation": "equals"
}
}
],
"combinator": "and"
},
"outputKey": "transcription",
"renameOutput": true
},
{
"conditions": {
"options": {},
"conditions": [
{
"id": "r-au",
"leftValue": "={{$json.event_type}}",
"rightValue": "post_call_audio",
"operator": {
"type": "string",
"operation": "equals"
}
}
],
"combinator": "and"
},
"outputKey": "audio",
"renameOutput": true
},
{
"conditions": {
"options": {},
"conditions": [
{
"id": "r-fa",
"leftValue": "={{$json.event_type}}",
"rightValue": "call_initiation_failure",
"operator": {
"type": "string",
"operation": "equals"
}
}
],
"combinator": "and"
},
"outputKey": "failure",
"renameOutput": true
}
]
},
"options": {
"fallbackOutput": "extra"
}
},
"id": "9e4cf3db-1005-42ca-8c29-8c476380b1a2"
},
{
"name": "Format Transcription",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1120,
-320
],
"parameters": {
"language": "javaScript",
"jsCode": "// Extract the structured fields from a post_call_transcription payload.\nconst item = $input.first().json;\nconst data = item.payload?.data || {};\nreturn [{json: {\n event: 'post_call_transcription',\n conversation_id: data.conversation_id,\n agent_id: data.agent_id,\n agent_name: data.agent_name,\n status: data.status,\n call_duration_secs: data.metadata?.call_duration_secs,\n caller_id: data.metadata?.caller_id,\n called_number: data.metadata?.called_number,\n phone_provider: data.metadata?.phone_provider,\n transcript_turns: (data.transcript || []).length,\n summary: data.analysis?.summary,\n evaluation_results: data.analysis?.evaluation_results || {},\n data_collection: data.analysis?.data_collection || {},\n has_audio: data.has_audio,\n raw: item.payload\n}}];"
},
"id": "f412cba8-40a1-4bca-bf70-6382ae7eadaf"
},
{
"name": "Format Audio",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1120,
-160
],
"parameters": {
"language": "javaScript",
"jsCode": "// Extract metadata from a post_call_audio payload. The base64-encoded audio\n// lives in payload.data.full_audio \u2014 large, may exceed n8n's default item\n// size limits. Keep only the metadata in the structured output; pass the raw\n// payload through to downstream sinks that handle binary.\nconst item = $input.first().json;\nconst data = item.payload?.data || {};\nreturn [{json: {\n event: 'post_call_audio',\n conversation_id: data.conversation_id,\n agent_id: data.agent_id,\n audio_bytes: data.full_audio ? Buffer.from(data.full_audio, 'base64').length : 0,\n audio_base64_length: data.full_audio?.length || 0,\n raw: item.payload\n}}];"
},
"id": "e4b1c7ee-cf80-4eb0-b298-4fcfbaa8521d"
},
{
"name": "Format Failure",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1120,
0
],
"parameters": {
"language": "javaScript",
"jsCode": "// Extract failure detail from a call_initiation_failure payload \u2014 alert-worthy.\nconst item = $input.first().json;\nconst data = item.payload?.data || {};\nconst body = data.metadata?.body || {};\nreturn [{json: {\n event: 'call_initiation_failure',\n alert_severity: 'high',\n conversation_id: data.conversation_id,\n agent_id: data.agent_id,\n failure_reason: data.failure_reason,\n provider: data.metadata?.type,\n from_number: body.from_number,\n to_number: body.to_number,\n sip_status_code: body.sip_status_code,\n error_reason: body.error_reason,\n call_sid: body.call_sid,\n raw: item.payload\n}}];"
},
"id": "575ab6e7-694b-487a-93b8-640554a64f71"
},
{
"name": "Format Unknown",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1120,
160
],
"parameters": {
"language": "javaScript",
"jsCode": "// Catch-all for unknown event types \u2014 log but do not alarm.\nconst item = $input.first().json;\nreturn [{json: {\n event: 'unknown_event_type',\n declared_type: item.event_type,\n payload_keys: Object.keys(item.payload || {}),\n raw: item.payload\n}}];"
},
"id": "86920ab7-eccc-4073-bb44-361ce899a846"
},
{
"name": "Has Ingest URL?",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
1340,
-320
],
"parameters": {
"conditions": {
"options": {
"caseSensitive": true
},
"conditions": [
{
"id": "has-ing",
"leftValue": "",
"rightValue": "",
"operator": {
"type": "string",
"operation": "notEmpty"
}
}
],
"combinator": "and"
}
},
"id": "bac4d4d7-ba9a-4486-912e-d761807cb287"
},
{
"name": "POST Ingest",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4,
"position": [
1560,
-360
],
"parameters": {
"method": "POST",
"url": "http://localhost/elevenlabs-ingest",
"sendBody": true,
"contentType": "json",
"jsonBody": "={{ JSON.stringify($json) }}",
"options": {
"timeout": 5000,
"response": {
"response": {
"neverError": true
}
}
}
},
"id": "d05e5f59-e618-4d7c-b230-f0478082214d"
},
{
"name": "Has Audio Sink URL?",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
1340,
-160
],
"parameters": {
"conditions": {
"options": {
"caseSensitive": true
},
"conditions": [
{
"id": "has-asu",
"leftValue": "",
"rightValue": "",
"operator": {
"type": "string",
"operation": "notEmpty"
}
}
],
"combinator": "and"
}
},
"id": "7cf25365-a235-41f3-992b-ecf2ed910c2a"
},
{
"name": "POST Audio Sink",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4,
"position": [
1560,
-200
],
"parameters": {
"method": "POST",
"url": "http://localhost/elevenlabs-audio",
"sendBody": true,
"contentType": "json",
"jsonBody": "={{ JSON.stringify($json) }}",
"options": {
"timeout": 10000,
"response": {
"response": {
"neverError": true
}
}
}
},
"id": "1d39d9d6-131c-4d70-bd7a-4d164a6cc7b8"
},
{
"name": "Has Alert URL?",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
1340,
0
],
"parameters": {
"conditions": {
"options": {
"caseSensitive": true
},
"conditions": [
{
"id": "has-alert",
"leftValue": "",
"rightValue": "",
"operator": {
"type": "string",
"operation": "notEmpty"
}
}
],
"combinator": "and"
}
},
"id": "1b3babb7-9610-425f-8ba2-05b2cd7c88c9"
},
{
"name": "POST Alert",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4,
"position": [
1560,
-40
],
"parameters": {
"method": "POST",
"url": "http://localhost/elevenlabs-alert",
"sendBody": true,
"contentType": "json",
"jsonBody": "={{ JSON.stringify($json) }}",
"options": {
"timeout": 5000,
"response": {
"response": {
"neverError": true
}
}
}
},
"id": "8bf57dde-d036-4950-9da9-91b9ec1acf03"
},
{
"name": "Respond 200",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1,
"position": [
1800,
-120
],
"parameters": {
"respondWith": "json",
"responseBody": "={{ JSON.stringify({status: 'ok', event: $json.event || $json.event_type || 'unknown', conversation_id: $json.conversation_id}) }}",
"options": {
"responseCode": 200
}
},
"id": "313e67b2-a039-448f-8ade-cb1c0afa0d7c"
},
{
"name": "Respond 401",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1,
"position": [
880,
160
],
"parameters": {
"respondWith": "json",
"responseBody": "={{ JSON.stringify({status: 'rejected', reason: 'signature_invalid_or_stale'}) }}",
"options": {
"responseCode": 401
}
},
"id": "48c58c55-3c57-4d44-a698-7a8d8d65d72f"
}
],
"connections": {
"Webhook": {
"main": [
[
{
"node": "Parse Signature",
"type": "main",
"index": 0
}
]
]
},
"Parse Signature": {
"main": [
[
{
"node": "HMAC Compute",
"type": "main",
"index": 0
}
]
]
},
"HMAC Compute": {
"main": [
[
{
"node": "Verify Signature",
"type": "main",
"index": 0
}
]
]
},
"Verify Signature": {
"main": [
[
{
"node": "Route by Event Type",
"type": "main",
"index": 0
}
],
[
{
"node": "Respond 401",
"type": "main",
"index": 0
}
]
]
},
"Route by Event Type": {
"main": [
[
{
"node": "Format Transcription",
"type": "main",
"index": 0
}
],
[
{
"node": "Format Audio",
"type": "main",
"index": 0
}
],
[
{
"node": "Format Failure",
"type": "main",
"index": 0
}
],
[
{
"node": "Format Unknown",
"type": "main",
"index": 0
}
]
]
},
"Format Transcription": {
"main": [
[
{
"node": "Has Ingest URL?",
"type": "main",
"index": 0
}
]
]
},
"Has Ingest URL?": {
"main": [
[
{
"node": "POST Ingest",
"type": "main",
"index": 0
}
],
[
{
"node": "Respond 200",
"type": "main",
"index": 0
}
]
]
},
"POST Ingest": {
"main": [
[
{
"node": "Respond 200",
"type": "main",
"index": 0
}
]
]
},
"Format Audio": {
"main": [
[
{
"node": "Has Audio Sink URL?",
"type": "main",
"index": 0
}
]
]
},
"Has Audio Sink URL?": {
"main": [
[
{
"node": "POST Audio Sink",
"type": "main",
"index": 0
}
],
[
{
"node": "Respond 200",
"type": "main",
"index": 0
}
]
]
},
"POST Audio Sink": {
"main": [
[
{
"node": "Respond 200",
"type": "main",
"index": 0
}
]
]
},
"Format Failure": {
"main": [
[
{
"node": "Has Alert URL?",
"type": "main",
"index": 0
}
]
]
},
"Has Alert URL?": {
"main": [
[
{
"node": "POST Alert",
"type": "main",
"index": 0
}
],
[
{
"node": "Respond 200",
"type": "main",
"index": 0
}
]
]
},
"POST Alert": {
"main": [
[
{
"node": "Respond 200",
"type": "main",
"index": 0
}
]
]
},
"Format Unknown": {
"main": [
[
{
"node": "Respond 200",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1",
"callerPolicy": "workflowsFromSameOwner",
"availableInMCP": false
},
"tags": []
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
elevenlabs_post_call_webhook. Uses crypto, httpRequest. Webhook trigger; 17 nodes.
Source: https://github.com/wranngle/n8n_showcase/blob/main/workflows/elevenlabs/post-call-webhook.json — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
elevenlabs_webhook_listener. Uses httpRequest, pipedrive, crypto. Webhook trigger; 35 nodes.
Community Node Disclaimer: This workflow uses KlickTipp community nodes.
Community Node Disclaimer: This workflow uses KlickTipp community nodes.
Struggling with inaccurate Meta Ads tracking due to iOS 14+ and ad blockers? 📉 This workflow is your solution. It provides a robust, server-side endpoint to reliably send conversion events directly to
VenueDesk - User Manager (Final Database Fix). Uses httpRequest, crypto. Webhook trigger; 13 nodes.