---
title: "telephony.incoming / web.incoming"
description: "Blokkerende webhook som former konfigurasjonen for et innkommende anrop i sanntid."
---

Når et innkommende telefonanrop når et nummer **uten en tilordnet
stemmeagent**, eller en nettwidgetøkt starter med en publiserbar nøkkel i
`mode="webhook"`, sender ThunderPhone en **blokkerende**
`telephony.incoming` / `web.incoming`-forespørsel til din
[eldre webhook-URL](/api-reference/organizations#legacy-single-url-webhook)
og venter på et konfigurasjonssvar: opptil **13 sekunder** for et
telefonanrop, inkludert ett nytt forsøk, og opptil **10 sekunder** for en
widgetøkt (se [Tidsavbrudd og nye forsøk](#timeouts-and-retries)). Bruk denne
utvekslingen til å velge prompt, stemme og verktøy dynamisk for hvert anrop —
se [veiledningen for dynamisk samtalekonfigurasjon](/nb/guides/dynamic-call-config)
for det fullstendige mønsteret.

<Note>
  Abonnerte [webhook-endepunkter](/nb/webhooks/endpoints) mottar også
  `telephony.incoming` / `web.incoming` — for **hvert** innkommende anrop
  og hver webøkt, enten en stemmeagent er konfigurert eller ikke — men disse
  leveransene er send-og-glem-varsler med en `event_id` og er aldri blokkerende.
  Bare den eldre webhooken med én URL har konfigurasjonsutvekslingen på denne
  siden. Formatene for endepunktvarsler finner du i
  [hendelseskatalogen](/nb/webhooks/events).
</Note>

## Tidsavbrudd og nye forsøk

Svar raskt: innringeren venter på linjen mens du avgjør.

For en telefonsamtale (`telephony.incoming`):

- Det første forsøket kan ta opptil **10 sekunder**.
- Hvis det får tidsavbrudd, ikke kan koble til eller får et
  `5xx`-, `408`- eller `429`-svar, prøver ThunderPhone én gang til. Det nye forsøket får det som gjenstår
  av et **13-sekunders** budsjett for hele utvekslingen: omtrent 3 sekunder etter
  et første forsøk som fikk tidsavbrudd, og nesten alle 13 etter en rask feil.
  Det gjøres ikke et nytt forsøk hvis det ville gjenstått mindre enn 2 sekunder.
- Ethvert annet `4xx`-svar, et svar over
  [størrelsesgrensen](#response-size-limit), eller et `2xx`-svar med en brødtekst som ikke er en
  gyldig konfigurasjon, er endelig og prøves ikke på nytt.
- Begge forsøkene sender den samme brødteksten, den samme `X-ThunderPhone-Signature`-headeren
  og den samme `X-ThunderPhone-Idempotency-Key`-headeren
  (`telephony-incoming-<call_id>`), slik at en håndterer med sideeffekter
  kan gjenkjenne det nye forsøket.

Det finnes ingen reserve-stemmeagent. Hvis ingen brukbar konfigurasjon kommer frem, blir
samtalen avsluttet med én gang i stedet for å la innringeren vente på en stille
linje. Den vises i samtalehistorikken som en mislykket, ikke-fakturert samtale med
`end_reason` `inbound_config_webhook_failed`, og den vanlige
[`telephony.complete`](/nb/webhooks/call-complete)-hendelsen sendes.

En widgetøkt (`web.incoming`) får ett enkelt forsøk på opptil 10
sekunder. Hvis det mislykkes, mislykkes øktforespørselen med `502` eller `422`.

<Warning>
  **Webhook-konfigurerte anrop har ingen samtykkekunngjøring fra
  ThunderPhone.** Anrop som konfigureres gjennom denne utvekslingen, omgår
  kunngjøringen ved anropsstart på stemmeagentnivå og er uttrykkelig unntatt fra
  ThunderPhones rammeverk for samtykkekunngjøringer (tjenestevilkår, delen
  «Opptak og samtykke»). Organisasjonen din er eneansvarlig for alle varsler og
  alt samtykke knyttet til opptak, overvåking, AI-deltakelse og identifisering av
  innringere som kreves for disse anropene — anropene kan fortsatt tas opp,
  transkriberes, analyseres og betjenes av AI. Bygg inn de nødvendige
  opplysningene i din egen anropsflyt før du aktiverer denne løsningen.
</Warning>

## Forespørselsnyttelast

For telefonanrop (`telephony.incoming`):

```json
{
  "type": "telephony.incoming",
  "data": {
    "call_id":     987654321,
    "from_number": "+14155550199",
    "to_number":   "+15551234567"
  }
}
```

| Felt | Type | Beskrivelse |
|-------|------|-------------|
| `call_id` | integer | Anrops-ID — stabil på tvers av alle hendelser for dette anropet |
| `from_number` | string | E.164-nummeret til innringeren |
| `to_number` | string | E.164-destinasjon (ett av ThunderPhone-numrene dine) |

For webwidget-økter (`web.incoming`) identifiserer `data`
innbyggingssiden i stedet for telefonnumre:

```json
{
  "type": "web.incoming",
  "data": {
    "call_id": 987654322,
    "origin_domain": "https://example.com",
    "publishable_key_prefix": "pk_live_a1b2"
  }
}
```

| Felt | Type | Beskrivelse |
|-------|------|-------------|
| `call_id` | heltall | Anrops-ID |
| `origin_domain` | streng | Sidens opphav som er vert for widgeten, slik nettleseren sendte det. En klient som ikke er en nettleser, kan sende en hvilken som helst verdi, så ikke behandle den som bevis på hvor forespørselen kom fra |
| `publishable_key_prefix` | streng | De første tegnene i den publiserbare nøkkelen som åpnet økten |
| `language`, `primary_language` | streng | Finnes når widgetøkten ba om en språkoverstyring |
| `voice` | streng | Finnes når widgetøkten ba om en stemmeoverstyring |
| `website_context` | streng | Finnes når widgeten sendte sidekontekst per økt |

Overstyringsfeltene (`language`, `voice`, `website_context` og eventuelle
nettleser-`variables`) kommer bare når den publiserbare nøkkelen tillater dem i
sine [nettleseroverstyringer](/nb/guides/embed-web-widget). En
øktforespørsel som inneholder en overstyring nøkkelen ikke tillater, avvises før
denne webhooken kalles.

<Note>
  Widgeter i webhookmodus leverer denne forespørselen til den publiserbare
  nøkkelens egen `webhook_url` når en er angitt, og faller ellers tilbake til
  webhook-URL-en på organisasjonsnivå. Uansett signeres den med organisasjonens
  webhook-`secret`.
</Note>

---

## Svarskjema

Returner et JSON-objekt som beskriver stemmeagentkonfigurasjonen for dette anropet.
`prompt` og `voice` er påkrevd; alt annet er valgfritt.

```json
{
  "prompt":  "You are a helpful booking assistant for Acme Restaurant.",
  "voice":   "john",
  "product": "spark",
  "background_track": null,
  "tools":   []
}
```

| Felt | Type | Påkrevd | Beskrivelse |
|-------|------|----------|-------------|
| `prompt` | streng | ja | Systeminstruks som styrer agenten |
| `voice` | streng | ja | Stemme-ID fra [`GET /v1/voices`](/api-reference/agents#voices), for eksempel `john`. `voice_name` godtas som et alias. Ukjente stemmer feiler valideringen og avviser samtalen |
| `product` | streng | nei | Standard er `spark`. Tillatt: `spark`, `bolt`, `storm-base`, `storm-base-with-ack`, `storm-extra`, `storm-extra-with-ack` |
| `thinking_level` | streng | nei | `minimal`, `base` (standard) eller `extra`. Overstyres for Storm-produkter: `storm-extra*` tvinger `extra`, andre `storm-*` tvinger `base` |
| `audio_context_mode` | streng | nei | `full` (standard) eller `reduced` |
| `watchdog_enabled` | boolsk | nei | Aktiver overvåking for denne samtalen. Standard er `false` |
| `additional_audio_context` | boolsk \| null | nei | Ta med de siste rundene med innringerlyd i stedet for bare den nyeste runden, noe som forbedrer rettelser og datainnsamling med mye staving/tall, med en liten økning i forsinkelse/kostnad. Er på som standard for innkommende økter og av for utgående telefonsamtaler; `null` beholder standardverdien |
| `storm_feedback_mode` | streng | nei | `none`, `acknowledgement` (standard) eller `tick` |
| `language` | streng | nei | Kortform for `primary_language` |
| `primary_language` | streng | nei | Språkkode, normalisert (standard `en`). Koder som ikke kan løses, avviser samtalen |
| `has_additional_languages` | boolsk | nei | Standard er `false` |
| `additional_languages` | matrise av strenger | nei | Ekstra språk agenten kan bytte til |
| `native_voice_switching` | boolsk | nei | Standard er `false`. Når samtalen bytter til et annet språk, bytt til en stemme som har språket som morsmål (matchet etter kjønn) i stedet for å beholde den konfigurerte stemmen |
| `language_switching` | streng \| null | nei | `ask_first` (standard) eller `follow_caller`; `null` er det samme som `ask_first`. Angir hvordan en flerspråklig agent håndterer at den andre personen snakker et annet av språkene dens. `ask_first` bytter straks når personen ber om språket eller sier en hel setning på det, og spør om dere skal fortsette på det etter noe kort, som et «ja». `follow_caller` bytter til ethvert konfigurert språk personen snakker, selv bare ett ord. Gjelder bare når `additional_languages` er angitt |
| `background_track` | streng \| null | nei | ID for bakgrunnslyd eller `null` |
| `acknowledgement_prompt_mode` | streng | nei | `auto` (standard) eller `manual` (Storm-with-ack-produkter) |
| `acknowledgement_prompt` | streng | nei | Brukes når `acknowledgement_prompt_mode="manual"` |
| `silence_interval_seconds` | heltall \| null | nei | 5–120. Sekunder med stillhet fra innringeren før en oppfølging |
| `silence_max_checkins` | heltall \| null | nei | 1–10 |
| `silence_checkins_enabled` | boolsk | nei | Standard er `true` |
| `connect_tone_enabled` | boolsk | nei | Standard er `false` |
| `pronunciation_dicts` | boolsk \| null | nei | Standard er `false`; `null` er det samme som `false`. Når verdien er `true`, bruker samtalens Cartesia-tale ThunderPhones uttaleordbok for samtalens språk, der en finnes. Fungerer for telefon- og widget-samtaler. Gjelder bare ThunderPhones egne Cartesia-stemmer, ikke stemmer på din egen Cartesia API-nøkkel. Brukes ikke med `agent_id`, der den lagrede agentens innstillinger gjelder |
| `voicemail_action` | streng | nei | `prompt` (standard), `hangup` eller `message` |
| `voicemail_message` | streng | nei | Brukes når `voicemail_action="message"` |
| `agent_name` | streng | nei | Visningsnavn som rapporteres til instrumentpaneler og widgeten |
| `org_name` | streng | nei | Organisasjonens visningsnavn for agentens persona |
| `tools` | matrise | nei | Innebygde skjemaer for funksjonsverktøy (se [Funksjonsverktøy](/nb/tools/overview)) |
| `transfer_destinations` | matrise av objekter \| null | nei | Opptil 50 `{name, number}`-oppføringer som denne samtalen kan overføres til, validert på samme måte som en agents [overføringsnumre](/api-reference/agents#transfer-numbers). Agenten overfører etter navn og bare til disse; `[]` betyr ingen overføringer. Utelat feltet eller send `null` for å la agenten overføre til et nummer den velger |
| `timezone` | streng | nei | IANA-tidssone for denne samtalen, for eksempel `America/Chicago`. Agenten oppgir datoen og klokkeslettet i denne, foran agentens egen tidssone. Tillatt også sammen med `agent_id`. Verdier som ikke er IANA-tidssoner, ignoreres. Se [Angi tidssone per samtale](/nb/guides/dynamic-call-config) |
| `call_id` | heltall | nei | Valgfritt ekko av forespørselens samtale-ID; ignoreres |

<Note>
  Ukjente nøkler på toppnivå blir stille **ignorert** — et feilstavet
  feltnavn avviser ikke konfigurasjonen, det blir bare ikke brukt. Talerrekkefølge
  og `max_hold_seconds` godtas ikke her; de kan bare
  konfigureres på selve [stemmeagenten](/api-reference/agents).
</Note>

Fordi `prompt` og `voice` er obligatoriske, avviser retur av `{}` eller et
svar som ikke består valideringen anropet: en telefonsamtale avsluttes
som beskrevet i [Tidsavbrudd og nye forsøk](#timeouts-and-retries), og en
forespørsel om widgetøkt mislykkes med `422`. Det finnes ingen reserve for
statisk agent på denne banen (et nummer eller en nøkkel i webhook-modus har ingen tildelt
agent).

---

## Grense for responsstørrelse

<Warning>
  Konfigurasjonssvar er begrenset til **5 MiB**. Hvis en behandler
  returnerer et større svar, også med en `2xx`-status,
  rapporterer ThunderPhone at svaret overskred grensen og
  avviser anropet eller widgetøkten. Begrens svaret til feltene
  som trengs for anropsoppsett; legg store datamengder bak funksjonsverktøy eller
  en annen tjeneste i stedet for å bygge dem inn i konfigurasjonen.
</Warning>

---

## Eksempelbehandler

<CodeGroup>
```python Python (FastAPI)
import hashlib
import hmac
import json
import os

from fastapi import FastAPI, HTTPException, Request

app = FastAPI()
WEBHOOK_SECRET = os.environ["THUNDERPHONE_WEBHOOK_SECRET"]

def verify(body: bytes, signature: str) -> bool:
    expected = hmac.new(WEBHOOK_SECRET.encode(), body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, signature or "")

@app.post("/thunderphone-webhook")
async def webhook(request: Request):
    body = await request.body()
    if not verify(body, request.headers.get("X-ThunderPhone-Signature", "")):
        raise HTTPException(status_code=401)

    event = json.loads(body)
    if event["type"] == "telephony.incoming":
        caller = event["data"]["from_number"]
        prompt = (
            "Greet the caller as a San Francisco local…"
            if caller.startswith("+1415")
            else "You are a friendly customer support agent…"
        )
        return {
            "prompt": prompt,
            "voice": "john",
            "product": "spark",
        }
    if event["type"] == "web.incoming":
        return {
            "prompt": "You are the website's helpful voice assistant…",
            "voice": "john",
            "product": "spark",
        }
    return {}
```

```javascript Node.js (Express)
import crypto from "node:crypto";
import express from "express";

const app = express();
const SECRET = process.env.THUNDERPHONE_WEBHOOK_SECRET;

function verify(body, signature) {
  const expected = crypto
    .createHmac("sha256", SECRET)
    .update(body)
    .digest("hex");
  return signature &&
    crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
}

app.post(
  "/thunderphone-webhook",
  express.raw({ type: "application/json" }),
  (req, res) => {
    if (!verify(req.body, req.header("X-ThunderPhone-Signature"))) {
      return res.sendStatus(401);
    }
    const event = JSON.parse(req.body.toString("utf8"));

    if (event.type === "telephony.incoming" || event.type === "web.incoming") {
      const caller = event.data.from_number || "web";
      const prompt = caller.startsWith("+1415")
        ? "Greet the caller as a San Francisco local…"
        : "You are a friendly customer support agent…";
      return res.json({
        prompt,
        voice: "john",
        product: "spark",
      });
    }
    res.json({});
  },
);
```
</CodeGroup>

---

## Svar med funksjonsverktøy

Legg ved verktøy slik at AI-en kan kalle API-ene dine midt i samtalen:

```json
{
  "prompt":  "You are a booking assistant. Use the available tools to help customers schedule appointments.",
  "voice":   "john",
  "product": "spark",
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "search_appointments",
        "description": "Find available appointment slots",
        "parameters": {
          "type": "object",
          "properties": {
            "date": { "type": "string", "description": "YYYY-MM-DD" },
            "service": { "type": "string" }
          },
          "required": ["date"]
        }
      },
      "endpoint": {
        "url": "https://api.example.com/appointments/search",
        "method": "POST",
        "headers": {
          "X-Api-Key": "your-key"
        }
      }
    }
  ]
}
```

<Tip>
  Forespørsler til verktøyendepunkter signeres med den **samme
  organisasjonens webhook-hemmelighet** som signerte denne utvekslingen. Se
  [Funksjonsverktøy](/nb/tools/overview) for nøyaktig format og det signerte
  forespørselsformatet.
</Tip>

---

## Hurtigoversikt over produktnivåer

| Produkt | Forsinkelse | Resonnering | Bekreftelse |
|---------|---------|-----------|-----------------|
| `spark` | Lavest | Grunnleggende | — |
| `bolt` | Lav | Forbedret | — |
| `storm-base` | Middels | Sterk | — |
| `storm-base-with-ack` | Middels | Sterk | Automatisk utfylling mens den tenker |
| `storm-extra` | Høyere | Dyp | — |
| `storm-extra-with-ack` | Høyere | Dyp | Automatisk utfylling mens den tenker |

---

## Relatert

<CardGroup cols={2}>
  <Card title="telephony.complete / web.complete" icon="phone-slash" href="/nb/webhooks/call-complete">
    Den ikke-blokkerende hendelsen ved avslutning av samtale.
  </Card>
  <Card title="Funksjonsverktøy" icon="screwdriver-wrench" href="/nb/tools/overview">
    Fullstendig JSON-skjema for `tools[]` og den signerte endepunktkontrakten.
  </Card>
  <Card title="Webhook-endepunkter" icon="bolt" href="/nb/webhooks/endpoints">
    Abonner flere URL-er på `telephony.incoming` / `web.incoming`.
  </Card>
  <Card title="Dynamisk samtalekonfigurasjon" icon="wand-magic-sparkles" href="/nb/guides/dynamic-call-config">
    Mønstre for ledetekster, verktøy og A/B-tester per innringer.
  </Card>
</CardGroup>
