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

# Webhooks efter samtal

## Översikt

[Webhooks](/docs/sv/eleven-api/resources/webhooks) efter samtal gör att du kan få detaljerad information om ett samtal när analysen är klar. När de är aktiverade skickar ElevenLabs en POST-begäran till din angivna slutpunkt med omfattande samtalsdata.

ElevenLabs stöder tre typer av webhooks efter samtal:

* **Webhooks för transkribering** (`post_call_transcription`): Innehåller fullständig konversationsdata, inklusive transkriberingar, analysresultat och metadata
* **Ljudwebhooks** (`post_call_audio`): Innehåller minimala data med base64-kodat ljud från hela konversationen
* **Webhooks för misslyckad samtalsinitiering** (`call_initiation_failure`): Innehåller information om misslyckade försök att initiera samtal, inklusive felorsaker och metadata

## Aktivera webhooks efter samtal

Webhooks efter samtal kan aktiveras för alla agenter i din arbetsyta via ElevenAgents [inställningssida](https://elevenlabs.io/app/agents/settings).

![Inställningar för webhook efter samtal](/docs/_fern-files/elevenlabs.docs.buildwithfern.com/eb5d768612d6461a21bc3127611f60724be3e1a55005af43faf48f2d7bf23807/assets/images/conversational-ai/postcallwebhooksettings.webp)

> **Warning**
>
> Webhooks efter samtal måste returnera en statuskod på 200 för att räknas som lyckade. Webhooks som
> misslyckas upprepade gånger inaktiveras automatiskt om det har skett 10 eller fler misslyckanden i följd och den senaste
> lyckade leveransen var för mer än 7 dagar sedan eller om ingen lyckad leverans någonsin har gjorts.

> **Note**
>
> Webhooks efter samtal kan automatiskt försökas igen om de misslyckas. Se [webhook- försök igen](/docs/sv/eleven-api/resources/webhooks#retries).

### Autentisering

Det är viktigt att mottagaren validerar alla inkommande webhooks. Webhooks har för närvarande stöd för autentisering via HMAC-signaturer. Konfigurera HMAC-autentisering genom att:

* Lagra den delade hemligheten som genereras när webhooken skapas på ett säkert sätt
* Verifiera headern ElevenLabs-Signature i din endpoint med hjälp av SDK:n

JavaScript-SDK:n exponerar `constructEvent`; Python-SDK:n exponerar `construct_event` med **`rawBody`**, **`sig_header`** och **`secret`** (dessa heter inte `payload` / `signature` i Python). Båda verifierar signaturen, validerar tidsstämpeln och tolkar JSON-payloaden.

#### Python

Exempel på webhook-hanterare med 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

Exempel på webhook-hanterare med 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

Exempel på webhook-hanterare med Next.js API-route:

**`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 });
}
```

### IP-tillåtelselista

För ytterligare säkerhet kan du lägga till ElevenLabs statiska utgående IP-adresser i din tillåtelselista. Se [IP-tillåtelselista](/docs/sv/eleven-api/resources/ip-allowlisting) för den fullständiga listan över IP-adresser.

> **Tip**
>
> Att använda IP-tillåtelselista tillsammans med validering av HMAC-signaturer ger flera säkerhetslager.

## Webhook-svarstruktur

ElevenLabs skickar tre olika typer av webhookar efter samtal, var och en med olika datastrukturer:

### Transkriptionswebhookar (`post_call_transcription`)

Innehåller omfattande konversationsdata, inklusive fullständiga transkript, analysresultat och metadata.

#### Fält på toppnivå

| Fält              | Typ    | Beskrivning                                                       |
| ----------------- | ------ | ----------------------------------------------------------------- |
| `type`            | string | Typ av händelse (alltid `post_call_transcription`)                |
| `data`            | object | Konversationsdata med strukturen `ConversationHistoryCommonModel` |
| `event_timestamp` | number | När händelsen inträffade, i Unix-tid UTC                          |

#### Datastruktur för objektet

Objektet `data` innehåller:

| Fält                                  | Typ     | Beskrivning                                                         |
| ------------------------------------- | ------- | ------------------------------------------------------------------- |
| `agent_id`                            | string  | ID för agenten som hanterade samtalet                               |
| `agent_name`                          | string  | Agentens namn vid tidpunkten för konversationen                     |
| `conversation_id`                     | string  | Unik identifierare för konversationen                               |
| `status`                              | string  | Konversationens status (t.ex. "done")                               |
| `user_id`                             | string  | Användaridentifierare, om tillgänglig                               |
| `branch_id`                           | string  | Agentgrenen som användes för konversationen, om tillämpligt         |
| `version_id`                          | string  | ID för agentversionen (ögonblicksbild) som var aktiv under samtalet |
| `environment`                         | string  | Miljön som användes för att lösa miljövariabler                     |
| `transcript`                          | array   | Fullständigt konversationstranskript med turer                      |
| `metadata`                            | object  | Tidpunkter för samtal, kostnader och telefonuppgifter               |
| `analysis`                            | object  | Utvärderingsresultat och konversationssammanfattning                |
| `conversation_initiation_client_data` | object  | Konfigurationsåsidosättningar och dynamiska variabler               |
| `has_audio`                           | boolean | Om konversationen har tillgängligt ljud                             |
| `has_user_audio`                      | boolean | Om användarljud är tillgängligt för konversationen                  |
| `has_response_audio`                  | boolean | Om ljud från agentsvaret är tillgängligt för konversationen         |

### Ljudwebhookar (`post_call_audio`)

Innehåller minimala data med hela konversationsljudet som base64-kodad MP3.

#### Fält på toppnivå

| Fält              | Typ    | Beskrivning                                |
| ----------------- | ------ | ------------------------------------------ |
| `type`            | string | Typ av händelse (alltid `post_call_audio`) |
| `data`            | object | Minimala ljuddata                          |
| `event_timestamp` | number | När händelsen inträffade, i Unix-tid UTC   |

#### Datastruktur för objektet

Objektet `data` innehåller endast:

| Fält              | Typ    | Beskrivning                                                              |
| ----------------- | ------ | ------------------------------------------------------------------------ |
| `agent_id`        | string | ID för agenten som hanterade samtalet                                    |
| `conversation_id` | string | Unik identifierare för konversationen                                    |
| `full_audio`      | string | Base64-kodad sträng som innehåller hela konversationsljudet i MP3-format |

> **Warning**
>
> Ljudwebhookar innehåller endast de tre fälten ovan. De innehåller INTE transkriptdata,
> metadata, analysresultat eller andra konversationsuppgifter.

### Webhookar för misslyckad samtalsinitiering (`call_initiation_failure`)

Innehåller information om försök att initiera telefonsamtal, inklusive orsaker till fel och metadata från telefonioperatören.

> **Note**
>
> Webhookhändelser för misslyckad samtalsinitiering skickas när ett samtal inte kan initieras på grund av anslutningsfel,
> att användaren avvisar samtalet eller att användaren inte svarar. Om ett samtal går till röstbrevlådan eller besvaras
> av en automatiserad tjänst skickas ingen webhook för misslyckad samtalsinitiering, eftersom samtalet
> initierades korrekt.

#### Fält på toppnivå

| Fält              | Typ    | Beskrivning                                        |
| ----------------- | ------ | -------------------------------------------------- |
| `type`            | string | Typ av händelse (alltid `call_initiation_failure`) |
| `data`            | object | Data om misslyckad samtalsinitiering               |
| `event_timestamp` | number | När händelsen inträffade, i Unix-tid UTC           |

#### Datastruktur för objektet

Objektet `data` innehåller:

| Fält              | Typ    | Beskrivning                                         |
| ----------------- | ------ | --------------------------------------------------- |
| `agent_id`        | string | ID för agenten som tilldelades att hantera samtalet |
| `conversation_id` | string | Unik identifierare för konversationen               |
| `failure_reason`  | string | Orsaken till felet ("busy", "no-answer", "unknown") |
| `metadata`        | object | Ytterligare data från telefonioperatören.           |

#### Datastruktur för metadataobjektet

Strukturen för objektet `metadata` varierar beroende på om det utgående samtalet gjordes via Twilio eller via SIP-trunking. Objektet innehåller fältet `type`, som skiljer de två åt, och fältet `body`, som innehåller operatörsspecifika uppgifter.

**SIP-metadata** (`type: "sip"`):

| Fält   | Typ    | Obligatoriskt | Beskrivning                            |
| ------ | ------ | ------------- | -------------------------------------- |
| `type` | string | Ja            | Operatörstyp (alltid `sip`)            |
| `body` | object | Ja            | SIP-specifik information om samtalsfel |

Objektet `body` för SIP-metadata innehåller:

| Fält              | Typ    | Obligatoriskt | Beskrivning                                                                                    |
| ----------------- | ------ | ------------- | ---------------------------------------------------------------------------------------------- |
| `from_number`     | number | Ja            | Telefonnummer för parten som initierade samtalet.                                              |
| `to_number`       | number | Ja            | Telefonnummer för den uppringda parten.                                                        |
| `sip_status_code` | number | Ja            | SIP-svarskod (t.ex. 486 för upptaget)                                                          |
| `error_reason`    | string | Ja            | Läsbar felbeskrivning                                                                          |
| `call_sid`        | string | Ja            | Sessionsidentifierare för SIP-samtalet                                                         |
| `twirp_code`      | string | Nej           | [Twirp-felkod](https://twitchtv.github.io/twirp/docs/spec_v7.html#error-codes), om tillämpligt |
| `sip_status`      | string | Nej           | SIP-statustext som motsvarar statuskoden                                                       |

**Twilio-metadata** (`type: "twilio"`):

| Fält   | Typ    | Obligatoriskt | Beskrivning                                                                                                                             |
| ------ | ------ | ------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| `type` | string | Ja            | Operatörstyp (alltid `twilio`)                                                                                                          |
| `body` | object | Ja            | Twilio StatusCallback body med samtalsuppgifter, dokumenterat [här](https://www.twilio.com/docs/voice/api/call-resource#statuscallback) |

## Exempel på webhook-payloads

### Exempel på transkriptionswebhook

```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
    }
  }
}
```

### Exempel på ljudwebhook

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

### Exempel på webhookar för misslyckad samtalsinitiering

#### Exempel på Twilio-metadata

```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"
      }
    }
  }
}
```

#### Exempel på SIP-metadata

```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"
      }
    }
  }
}
```

## Leverans av ljudwebhookar

Ljudwebhookar levereras separat från transkriptionswebhookar och innehåller endast de nödvändiga fälten för att identifiera konversationen samt base64-kodade ljuddata.

> **Note**
>
> Ljudwebhookar kan aktiveras eller inaktiveras med reglaget "Skicka ljuddata" i dina webhook-
> inställningar. Den här inställningen kan konfigureras både på arbetsytenivå (i ElevenAgents-inställningarna)
> och på agentnivå (i enskilda agenters webhook-åsidosättningar).

### Strömmande leverans

Ljudwebhookar levereras som strömmande HTTP-begäranden med headern `transfer-encoding: chunked` för att effektivt hantera stora ljudfiler. Varje begäran får timeout efter 5 minuter.

### Återförsök

När återförsök är aktiverade för webhooken görs återförsök för misslyckade ljudleveranser enligt samma schema som för transkriptionswebhookar. Vid ett återförsök skickas hela ljud-payloaden igen, så deduplicera efter `conversation_id`. Se [återförsök för webhookar](/docs/sv/eleven-api/resources/webhooks#retries) för schemat, fel som kan återförsökas och gränser för ljudstorlek.

### Bearbeta ljudwebhookar

Eftersom ljudwebhookar levereras via chunked transfer encoding behöver du hantera strömmande data korrekt:

```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**
>
> Ljudwebhookar kan vara stora filer, så se till att din webhook-slutpunkt kan hantera strömmande begäranden
> och har tillräcklig minnes- och lagringskapacitet. Ljudet levereras i MP3-format.

## Användningsområden

### Automatiserad uppföljning efter samtal

Webhookar efter samtal gör att du kan bygga automatiserade workflows som utlöses direkt när ett samtal avslutas. Här är några praktiska användningsområden:

#### CRM-integration

Uppdatera ditt CRM-system med konversationsdata så snart ett samtal avslutas:

```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");
});
```

### Tillståndsbaserade konversationer

Behåll konversationskontext mellan flera interaktioner genom att lagra och hämta tillstånd:

1. När ett samtal börjar skickar du med ditt användar-id som en dynamisk variabel.
2. När ett samtal avslutas konfigurerar du din webhook-slutpunkt för att lagra konversationsdata i din databas, baserat på användar-id:t som extraherats från dynamic\_variables.
3. När användaren ringer igen kan du hämta den här kontexten och skicka den till den nya konversationen i den dynamiska variabeln \{\{previous\_topics}}.
4. Det skapar en smidig upplevelse där agenten "minns" tidigare interaktioner

```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(", "),
    },
  });
}
```