> This is a page from the ElevenLabs documentation. For a complete page index, fetch https://elevenlabs.io/docs/llms.txt. For the full documentation in a single file, fetch https://elevenlabs.io/docs/llms-full.txt.

# Webhook post-chiamata

## Panoramica

I [webhook](/docs/it/eleven-api/resources/webhooks) post-chiamata ti consentono di ricevere informazioni dettagliate su una chiamata dopo il completamento dell'analisi. Quando sono abilitati, ElevenLabs invierà una richiesta POST all'endpoint specificato con dati completi sulla chiamata.

ElevenLabs supporta tre tipi di webhook post-chiamata:

* **Webhook di trascrizione** (`post_call_transcription`): contengono dati completi della conversazione, incluse trascrizioni, risultati dell'analisi e metadati
* **Webhook audio** (`post_call_audio`): contengono dati minimi con l'audio dell'intera conversazione codificato in base64
* **Webhook per l'errore di avvio della chiamata** (`call_initiation_failure`): contengono informazioni sui tentativi di avvio della chiamata non riusciti, inclusi motivi dell'errore e metadati

## Abilitare i webhook post-chiamata

Puoi abilitare i webhook post-chiamata per tutti gli agenti del tuo workspace dalla [pagina delle impostazioni](https://elevenlabs.io/app/agents/settings) di ElevenAgents.

![Impostazioni dei webhook post-chiamata](/docs/_fern-files/elevenlabs.docs.buildwithfern.com/eb5d768612d6461a21bc3127611f60724be3e1a55005af43faf48f2d7bf23807/assets/images/conversational-ai/postcallwebhooksettings.webp)

> **Warning**
>
> Per essere considerati riusciti, i webhook post-chiamata devono restituire un codice di stato 200. I webhook che
> non riescono ripetutamente vengono disabilitati automaticamente se si verificano 10 o più errori consecutivi e l'ultima
> consegna riuscita risale a più di 7 giorni fa o non è mai stata completata con successo.

> **Note**
>
> I webhook post-chiamata possono essere ritentati automaticamente in caso di errore. Consulta i [nuovi tentativi dei webhook ](/docs/it/eleven-api/resources/webhooks#retries).

### Autenticazione

È importante che il listener convalidi tutti i webhook in arrivo. I webhook supportano attualmente l'autenticazione tramite firme HMAC. Configura l'autenticazione HMAC:

* Archiviando in modo sicuro il segreto condiviso generato alla creazione del webhook
* Verificando l'header ElevenLabs-Signature nel tuo endpoint tramite l'SDK

L'SDK JavaScript espone `constructEvent`; l'SDK Python espone `construct_event` con **`rawBody`**, **`sig_header`** e **`secret`** (in Python non si chiamano `payload` / `signature`). Entrambi verificano la firma, convalidano il timestamp e analizzano il payload JSON.

#### Python

Esempio di gestore webhook con FastAPI:

```python
from dotenv import load_dotenv
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
from elevenlabs.client import ElevenLabs
from elevenlabs.errors import BadRequestError
import os

load_dotenv()

app = FastAPI()
elevenlabs = ElevenLabs(
    api_key=os.getenv("ELEVENLABS_API_KEY"),
)

WEBHOOK_SECRET = os.getenv("WEBHOOK_SECRET")

@app.post("/webhook")
async def receive_message(request: Request):
    payload = await request.body()
    signature = request.headers.get("elevenlabs-signature")

    try:
        event = elevenlabs.webhooks.construct_event(
            rawBody=payload.decode("utf-8"),
            sig_header=signature,
            secret=WEBHOOK_SECRET,
        )
    except BadRequestError as e:
        return JSONResponse(content={"error": "Invalid signature"}, status_code=401)

    # construct_event returns a dict (parsed JSON), not an object with attributes
    if event.get("type") == "post_call_transcription":
        print(f"Received transcription: {event.get('data')}")

    return {"status": "received"}
```

#### JavaScript

#### Express

Esempio di gestore webhook con Express:

```javascript
import { ElevenLabsClient } from '@elevenlabs/elevenlabs-js';
import express from 'express';

const app = express();

const elevenlabs = new ElevenLabsClient();
const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET;

// Use express.text() to preserve raw body for signature verification
app.post('/webhook', express.text({ type: 'application/json' }), async (req, res) => {
  const signature = req.headers['elevenlabs-signature'];
  const payload = req.body; // Raw string body

  let event;
  try {
    event = await elevenlabs.webhooks.constructEvent(payload, signature, WEBHOOK_SECRET);
  } catch (error) {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  // Process the webhook event
  if (event.type === 'post_call_transcription') {
    console.log('Received transcription:', event.data);
  }

  res.status(200).json({ received: true });
});
```

#### Next.js

Esempio di gestore webhook con una route API di Next.js:

**`app/api/webhook/route.ts`**

```typescript app/api/webhook/route.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import { ElevenLabsClient } from '@elevenlabs/elevenlabs-js';

const elevenlabs = new ElevenLabsClient();
const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET;

export async function POST(req: NextRequest) {
  const body = await req.text();
  const signature = req.headers.get('elevenlabs-signature');

  let event;
  try {
    event = await elevenlabs.webhooks.constructEvent(body, signature, WEBHOOK_SECRET);
  } catch (error) {
    return NextResponse.json({ error: 'Invalid signature' }, { status: 401 });
  }

  // Process the webhook event
  if (event.type === 'post_call_transcription') {
    console.log('Received transcription:', event.data);
  }

  return NextResponse.json({ received: true }, { status: 200 });
}
```

### Allowlist di IP

Per una maggiore sicurezza, puoi aggiungere gli IP di uscita statici di ElevenLabs alla tua allowlist. Consulta l'[allowlist di IP](/docs/it/eleven-api/resources/ip-allowlisting) per l'elenco completo degli indirizzi IP.

> **Tip**
>
> L'uso di un'allowlist di IP insieme alla convalida della firma HMAC offre più livelli di
> sicurezza.

## Struttura della risposta webhook

ElevenLabs invia tre tipi distinti di webhook post-chiamata, ciascuno con strutture dati diverse:

### Webhook di trascrizione (`post_call_transcription`)

Contiene dati completi sulla conversazione, inclusi trascrizioni complete, risultati dell'analisi e metadati.

#### Campi di primo livello

| Campo             | Tipo   | Descrizione                                                                      |
| ----------------- | ------ | -------------------------------------------------------------------------------- |
| `type`            | string | Tipo di evento (sempre `post_call_transcription`)                                |
| `data`            | object | Dati della conversazione che usano la struttura `ConversationHistoryCommonModel` |
| `event_timestamp` | number | Momento in cui si è verificato l'evento in formato Unix UTC                      |

#### Struttura dell'oggetto data

L'oggetto `data` contiene:

| Campo                                 | Tipo    | Descrizione                                                                   |
| ------------------------------------- | ------- | ----------------------------------------------------------------------------- |
| `agent_id`                            | string  | ID dell'agente che ha gestito la chiamata                                     |
| `agent_name`                          | string  | Nome dell'agente al momento della conversazione                               |
| `conversation_id`                     | string  | Identificatore univoco della conversazione                                    |
| `status`                              | string  | Stato della conversazione (ad esempio, "done")                                |
| `user_id`                             | string  | Identificatore dell'utente, se disponibile                                    |
| `branch_id`                           | string  | Branch dell'agente usato per la conversazione, se applicabile                 |
| `version_id`                          | string  | ID della versione dell'agente (snapshot) attiva durante la chiamata           |
| `environment`                         | string  | Ambiente usato per risolvere le variabili d'ambiente                          |
| `transcript`                          | array   | Trascrizione completa della conversazione con i turni                         |
| `metadata`                            | object  | Tempistiche, costi e dettagli telefonici della chiamata                       |
| `analysis`                            | object  | Risultati della valutazione e riepilogo della conversazione                   |
| `conversation_initiation_client_data` | object  | Override di configurazione e variabili dinamiche                              |
| `has_audio`                           | boolean | Indica se è disponibile audio per la conversazione                            |
| `has_user_audio`                      | boolean | Indica se è disponibile audio dell'utente per la conversazione                |
| `has_response_audio`                  | boolean | Indica se è disponibile audio delle risposte dell'agente per la conversazione |

### Webhook audio (`post_call_audio`)

Contiene dati minimi e l'audio completo della conversazione come MP3 codificato in base64.

#### Campi di primo livello

| Campo             | Tipo   | Descrizione                                                 |
| ----------------- | ------ | ----------------------------------------------------------- |
| `type`            | string | Tipo di evento (sempre `post_call_audio`)                   |
| `data`            | object | Dati audio minimi                                           |
| `event_timestamp` | number | Momento in cui si è verificato l'evento in formato Unix UTC |

#### Struttura dell'oggetto data

L'oggetto `data` contiene solo:

| Campo             | Tipo   | Descrizione                                                                                 |
| ----------------- | ------ | ------------------------------------------------------------------------------------------- |
| `agent_id`        | string | ID dell'agente che ha gestito la chiamata                                                   |
| `conversation_id` | string | Identificatore univoco della conversazione                                                  |
| `full_audio`      | string | Stringa codificata in base64 contenente l'audio completo della conversazione in formato MP3 |

> **Warning**
>
> I webhook audio contengono solo i tre campi elencati sopra. NON includono dati di trascrizione,
> metadati, risultati dell'analisi o altri dettagli della conversazione.

### Webhook per errori di avvio della chiamata (`call_initiation_failure`)

Contiene informazioni sui tentativi di avvio delle chiamate telefoniche, incluse le ragioni dell'errore e i metadati del provider di telefonia.

> **Note**
>
> Gli eventi webhook per errori di avvio della chiamata vengono inviati quando una chiamata non riesce ad avviarsi a causa di errori di connessione,
> del rifiuto della chiamata da parte dell'utente o perché l'utente non risponde. Se una chiamata viene inoltrata alla segreteria telefonica o viene
> risposta da un servizio automatizzato, non viene inviato alcun webhook per errore di avvio, poiché la chiamata è stata
> avviata correttamente.

#### Campi di primo livello

| Campo             | Tipo   | Descrizione                                                 |
| ----------------- | ------ | ----------------------------------------------------------- |
| `type`            | string | Tipo di evento (sempre `call_initiation_failure`)           |
| `data`            | object | Dati dell'errore di avvio della chiamata                    |
| `event_timestamp` | number | Momento in cui si è verificato l'evento in formato Unix UTC |

#### Struttura dell'oggetto data

L'oggetto `data` contiene:

| Campo             | Tipo   | Descrizione                                           |
| ----------------- | ------ | ----------------------------------------------------- |
| `agent_id`        | string | ID dell'agente assegnato alla gestione della chiamata |
| `conversation_id` | string | Identificatore univoco della conversazione            |
| `failure_reason`  | string | Motivo dell'errore ("busy", "no-answer", "unknown")   |
| `metadata`        | object | Dati aggiuntivi forniti dal provider di telefonia.    |

#### Struttura dell'oggetto metadata

La struttura dell'oggetto `metadata` varia a seconda che la chiamata in uscita sia stata effettuata tramite Twilio o trunking SIP. L'oggetto include un campo `type` che distingue tra i due e un campo `body` contenente dettagli specifici del provider.

**Metadati SIP** (`type: "sip"`):

| Campo  | Tipo   | Obbligatorio | Descrizione                                            |
| ------ | ------ | ------------ | ------------------------------------------------------ |
| `type` | string | Sì           | Tipo di provider (sempre `sip`)                        |
| `body` | object | Sì           | Informazioni SIP specifiche sull'errore della chiamata |

L'oggetto `body` per i metadati SIP contiene:

| Campo             | Tipo   | Obbligatorio | Descrizione                                                                                              |
| ----------------- | ------ | ------------ | -------------------------------------------------------------------------------------------------------- |
| `from_number`     | number | Sì           | Numero di telefono del soggetto che ha avviato la chiamata.                                              |
| `to_number`       | number | Sì           | Numero di telefono del soggetto chiamato.                                                                |
| `sip_status_code` | number | Sì           | Codice di stato della risposta SIP (ad esempio, 486 per occupato)                                        |
| `error_reason`    | string | Sì           | Descrizione dell'errore leggibile dall'utente                                                            |
| `call_sid`        | string | Sì           | Identificatore della sessione di chiamata SIP                                                            |
| `twirp_code`      | string | No           | [Codice di errore Twirp](https://twitchtv.github.io/twirp/docs/spec_v7.html#error-codes), se applicabile |
| `sip_status`      | string | No           | Testo dello stato SIP corrispondente al codice di stato                                                  |

**Metadati Twilio** (`type: "twilio"`):

| Campo  | Tipo   | Obbligatorio | Descrizione                                                                                                                                               |
| ------ | ------ | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `type` | string | Sì           | Tipo di provider (sempre `twilio`)                                                                                                                        |
| `body` | object | Sì           | Body di Twilio StatusCallback contenente i dettagli della chiamata, documentato [qui](https://www.twilio.com/docs/voice/api/call-resource#statuscallback) |

## Esempi di payload webhook

### Esempio di webhook di trascrizione

```json
{
  "type": "post_call_transcription",
  "event_timestamp": 1739537297,
  "data": {
    "agent_id": "xyz",
    "conversation_id": "abc",
    "status": "done",
    "user_id": "user123",
    "transcript": [
      {
        "role": "agent",
        "message": "Hey there angelo. How are you?",
        "tool_calls": null,
        "tool_results": null,
        "feedback": null,
        "time_in_call_secs": 0,
        "conversation_turn_metrics": null
      },
      {
        "role": "user",
        "message": "Hey, can you tell me, like, a fun fact about 11 Labs?",
        "tool_calls": null,
        "tool_results": null,
        "feedback": null,
        "time_in_call_secs": 2,
        "conversation_turn_metrics": null
      },
      {
        "role": "agent",
        "message": "I do not have access to fun facts about Eleven Labs. However, I can share some general information about the company. Eleven Labs is an AI voice technology platform that specializes in voice cloning and text-to-speech...",
        "tool_calls": null,
        "tool_results": null,
        "feedback": null,
        "time_in_call_secs": 9,
        "conversation_turn_metrics": {
          "convai_llm_service_ttfb": {
            "elapsed_time": 0.3704247010173276
          },
          "convai_llm_service_ttf_sentence": {
            "elapsed_time": 0.5551181449554861
          }
        }
      }
    ],
    "metadata": {
      "start_time_unix_secs": 1739537297,
      "call_duration_secs": 22,
      "cost": 296,
      "deletion_settings": {
        "deletion_time_unix_secs": 1802609320,
        "deleted_logs_at_time_unix_secs": null,
        "deleted_audio_at_time_unix_secs": null,
        "deleted_transcript_at_time_unix_secs": null,
        "delete_transcript_and_pii": true,
        "delete_audio": true
      },
      "feedback": {
        "overall_score": null,
        "likes": 0,
        "dislikes": 0
      },
      "authorization_method": "authorization_header",
      "charging": {
        "dev_discount": true
      },
      "termination_reason": ""
    },
    "analysis": {
      "evaluation_criteria_results": {},
      "data_collection_results": {},
      "call_successful": "success",
      "transcript_summary": "The conversation begins with the agent asking how Angelo is, but Angelo redirects the conversation by requesting a fun fact about 11 Labs. The agent acknowledges they don't have specific fun facts about Eleven Labs but offers to provide general information about the company. They briefly describe Eleven Labs as an AI voice technology platform specializing in voice cloning and text-to-speech technology. The conversation is brief and informational, with the agent adapting to the user's request despite not having the exact information asked for."
    },
    "conversation_initiation_client_data": {
      "conversation_config_override": {
        "agent": {
          "prompt": null,
          "first_message": null,
          "language": "en"
        },
        "tts": {
          "voice_id": null
        }
      },
      "custom_llm_extra_body": {},
      "dynamic_variables": {
        "user_name": "angelo"
      },
      "branch_id": null,
      "environment": null
    }
  }
}
```

### Esempio di webhook audio

```json
{
  "type": "post_call_audio",
  "event_timestamp": 1739537319,
  "data": {
    "agent_id": "xyz",
    "conversation_id": "abc",
    "full_audio": "SUQzBAAAAAAA...base64_encoded_mp3_data...AAAAAAAAAA=="
  }
}
```

### Esempi di webhook per errori di avvio della chiamata

#### Esempio di metadati Twilio

```json
{
  "type": "call_initiation_failure",
  "event_timestamp": 1759931652,
  "data": {
    "agent_id": "xyz",
    "conversation_id": "abc",
    "failure_reason": "busy",
    "metadata": {
      "type": "twilio",
      "body": {
        "Called": "+441111111111",
        "ToState": "",
        "CallerCountry": "US",
        "Direction": "outbound-api",
        "Timestamp": "Wed, 08 Oct 2025 13:54:12 +0000",
        "CallbackSource": "call-progress-events",
        "SipResponseCode": "487",
        "CallerState": "WA",
        "ToZip": "",
        "SequenceNumber": "2",
        "CallSid": "CA8367245817625617832576245724",
        "To": "+441111111111",
        "CallerZip": "98631",
        "ToCountry": "GB",
        "CalledZip": "",
        "ApiVersion": "2010-04-01",
        "CalledCity": "",
        "CallStatus": "busy",
        "Duration": "0",
        "From": "+11111111111",
        "CallDuration": "0",
        "AccountSid": "AC37682153267845716245762454a",
        "CalledCountry": "GB",
        "CallerCity": "RAYMOND",
        "ToCity": "",
        "FromCountry": "US",
        "Caller": "+11111111111",
        "FromCity": "RAYMOND",
        "CalledState": "",
        "FromZip": "12345",
        "FromState": "WA"
      }
    }
  }
}
```

#### Esempio di metadati SIP

```json
{
  "type": "call_initiation_failure",
  "event_timestamp": 1759931652,
  "data": {
    "agent_id": "xyz",
    "conversation_id": "abc",
    "failure_reason": "busy",
    "metadata": {
      "type": "sip",
      "body": {
        "from_number": "+441111111111",
        "to_number": "+11111111111",
        "sip_status_code": 486,
        "error_reason": "INVITE failed: sip status: 486: Busy here (SIP 486)",
        "call_sid": "d8e7f6a5-b4c3-4d5e-8f9a-0b1c2d3e4f5a",
        "sip_status": "Busy here",
        "twirp_code": "unavailable"
      }
    }
  }
}
```

## Consegna dei webhook audio

I webhook audio vengono inviati separatamente dai webhook di trascrizione e contengono solo i campi essenziali per identificare la conversazione, insieme ai dati audio codificati in base64.

> **Note**
>
> Puoi abilitare o disabilitare i webhook audio usando l'interruttore "Invia dati audio" nelle impostazioni
> del webhook. Puoi configurare questa impostazione sia a livello di workspace (nelle impostazioni di ElevenAgents)
> sia a livello di agente (negli override webhook dei singoli agenti).

### Consegna in streaming

I webhook audio vengono inviati come richieste HTTP in streaming con l'header `transfer-encoding: chunked` per gestire in modo efficiente file audio di grandi dimensioni. Ogni richiesta scade dopo 5 minuti.

### Nuovi tentativi

Quando i nuovi tentativi sono abilitati sul webhook, le consegne audio non riuscite vengono ritentate con la stessa pianificazione dei webhook di trascrizione. Un nuovo tentativo invia di nuovo l'intero payload audio, quindi rimuovi i duplicati in base a `conversation_id`. Consulta [nuovi tentativi dei webhook](/docs/it/eleven-api/resources/webhooks#retries) per la pianificazione, gli errori per cui è possibile eseguire un nuovo tentativo e i limiti delle dimensioni audio.

### Elaborare i webhook audio

Poiché i webhook audio vengono inviati tramite codifica di trasferimento chunked, devi gestire correttamente i dati in streaming:

```python

import base64
import json
from aiohttp import web

async def handle_webhook(request):

    # Check if this is a chunked/streaming request
    if request.headers.get("transfer-encoding", "").lower() == "chunked":
        # Read streaming data in chunks
        chunked_body = bytearray()
        while True:
            chunk = await request.content.read(8192)  # 8KB chunks
            if not chunk:
                break
            chunked_body.extend(chunk)

        # Parse the complete payload
        request_body = json.loads(chunked_body.decode("utf-8"))
    else:
        # Handle regular requests
        body_bytes = await request.read()
        request_body = json.loads(body_bytes.decode('utf-8'))

    # Process different webhook types
    if request_body["type"] == "post_call_transcription":
        # Handle transcription webhook with full conversation data
        handle_transcription_webhook(request_body["data"])
    elif request_body["type"] == "post_call_audio":
        # Handle audio webhook with minimal data
        handle_audio_webhook(request_body["data"])
    elif request_body["type"] == "call_initiation_failure":
        # Handle call initiation failure webhook
        handle_call_initiation_failure_webhook(request_body["data"])

    return web.json_response({"status": "ok"})

def handle_audio_webhook(data):
    # Decode base64 audio data
    audio_bytes = base64.b64decode(data["full_audio"])

    # Save or process the audio file
    conversation_id = data["conversation_id"]
    with open(f"conversation_{conversation_id}.mp3", "wb") as f:
        f.write(audio_bytes)

def handle_call_initiation_failure_webhook(data):
    # Handle call initiation failure events
    agent_id = data["agent_id"]
    conversation_id = data["conversation_id"]
    failure_reason = data.get("failure_reason")
    metadata = data.get("metadata", {})

    # Log the failure for monitoring
    print(f"Call failed for agent {agent_id}, conversation {conversation_id}")
    print(f"Failure reason: {failure_reason}")

    # Access provider-specific metadata
    provider_type = metadata.get("type")
    body = metadata.get("body", {})
    if provider_type == "sip":
        print(f"SIP status code: {body.get('sip_status_code')}")
        print(f"Error reason: {body.get('error_reason')}")
    elif provider_type == "twilio":
        print(f"Twilio CallSid: {body.get('CallSid')}")
        print(f"Call status: {body.get('CallStatus')}")

    # Update your system with the failure information
    # e.g., mark lead as "call_failed" in CRM

```

```javascript
import fs from "fs";

app.post("/webhook/elevenlabs", (req, res) => {
  let body = "";

  // Handle chunked/streaming requests
  req.on("data", (chunk) => {
    body += chunk;
  });

  req.on("end", () => {
    try {
      const requestBody = JSON.parse(body);

      // Process different webhook types
      if (requestBody.type === "post_call_transcription") {
        // Handle transcription webhook with full conversation data
        handleTranscriptionWebhook(requestBody.data);
      } else if (requestBody.type === "post_call_audio") {
        // Handle audio webhook with minimal data
        handleAudioWebhook(requestBody.data);
      } else if (requestBody.type === "call_initiation_failure") {
        // Handle call initiation failure webhook
        handleCallFailureWebhook(requestBody.data);
      }

      res.status(200).json({ status: "ok" });
    } catch (error) {
      console.error("Error processing webhook:", error);
      res.status(400).json({ error: "Invalid JSON" });
    }
  });
});

function handleAudioWebhook(data) {
  // Decode base64 audio data
  const audioBytes = Buffer.from(data.full_audio, "base64");

  // Save or process the audio file
  const conversationId = data.conversation_id;
  fs.writeFileSync(`conversation_${conversationId}.mp3`, audioBytes);
}

function handleCallFailureWebhook(data) {
  // Handle call initiation failure events
  const { agent_id, conversation_id, failure_reason, metadata } = data;

  // Log the failure for monitoring
  console.log(`Call failed for agent ${agent_id}, conversation ${conversation_id}`);
  console.log(`Failure reason: ${failure_reason}`);

  // Access provider-specific metadata
  const body = metadata.body || {};
  if (metadata?.type === "sip") {
    console.log(`SIP status code: ${body.sip_status_code}`);
    console.log(`Error reason: ${body.error_reason}`);
  } else if (metadata?.type === "twilio") {
    console.log(`Twilio CallSid: ${body.CallSid}`);
    console.log(`Call status: ${body.CallStatus}`);
  }

  // Update your system with the failure information
  // e.g., mark lead as "call_failed" in CRM
}
```

> **Note**
>
> I webhook audio possono essere file di grandi dimensioni, quindi assicurati che il tuo endpoint webhook possa gestire richieste in streaming
> e disponga di capacità di memoria/archiviazione sufficienti. L'audio viene inviato in formato MP3.

## Casi d'uso

### Follow-up automatici delle chiamate

I webhook post-chiamata ti consentono di creare workflow automatizzati che si attivano subito dopo la fine di una chiamata. Ecco alcune applicazioni pratiche:

#### Integrazione CRM

Aggiorna il tuo sistema di gestione delle relazioni con i clienti con i dati della conversazione non appena una chiamata si conclude:

```javascript
// Example webhook handler
app.post("/webhook/elevenlabs", async (req, res) => {
  // HMAC validation code

  const { data } = req.body;

  // Extract key information
  const userId = data.metadata.user_id;
  const transcriptSummary = data.analysis.transcript_summary;
  const callSuccessful = data.analysis.call_successful;

  // Update CRM record
  await updateCustomerRecord(userId, {
    lastInteraction: new Date(),
    conversationSummary: transcriptSummary,
    callOutcome: callSuccessful,
    fullTranscript: data.transcript,
  });

  res.status(200).send("Webhook received");
});
```

### Conversazioni con stato

Mantieni il contesto della conversazione tra più interazioni archiviando e recuperando lo stato:

1. All'avvio di una chiamata, passa il tuo ID utente come variabile dinamica.
2. Al termine di una chiamata, configura l'endpoint webhook in modo che archivi i dati della conversazione nel database in base all'ID utente estratto da dynamic\_variables.
3. Quando l'utente richiama, puoi recuperare questo contesto e passarlo alla nuova conversazione in una variabile dinamica \{\{previous\_topics}}.
4. In questo modo crei un'esperienza fluida in cui l'agente "ricorda" le interazioni precedenti.

```javascript
// Store conversation state when call ends
app.post("/webhook/elevenlabs", async (req, res) => {
  // HMAC validation code

  const { data } = req.body;
  const userId = data.metadata.user_id;

  // Store conversation state
  await db.userStates.upsert({
    userId,
    lastConversationId: data.conversation_id,
    lastInteractionTimestamp: data.metadata.start_time_unix_secs,
    conversationHistory: data.transcript,
    previousTopics: extractTopics(data.analysis.transcript_summary),
  });

  res.status(200).send("Webhook received");
});

// When initiating a new call, retrieve and use the state
async function initiateCall(userId) {
  // Get user's conversation state
  const userState = await db.userStates.findOne({ userId });

  // Start new conversation with context from previous calls
  return await elevenlabs.startConversation({
    agent_id: "xyz",
    conversation_id: generateNewId(),
    dynamic_variables: {
      user_name: userState.name,
      previous_conversation_id: userState.lastConversationId,
      previous_topics: userState.previousTopics.join(", "),
    },
  });
}
```