---
title: "telephony.incoming / web.incoming"
description: "Blokujący webhook, który kształtuje konfigurację połączenia przychodzącego w czasie rzeczywistym."
---

Gdy przychodzące połączenie telefoniczne trafia na numer **bez przypisanego
agenta głosowego** albo sesja widżetu internetowego rozpoczyna się przy użyciu klucza publikowalnego w
`mode="webhook"`, ThunderPhone wysyła **blokujące**
żądanie `telephony.incoming` / `web.incoming` na Twój
[starszy adres URL webhooka](/api-reference/organizations#legacy-single-url-webhook)
i czeka na odpowiedź z konfiguracją: do **13 sekund** w przypadku
połączenia telefonicznego, z uwzględnieniem jednej ponownej próby, oraz do **10 sekund** w przypadku sesji widżetu
(zobacz [Limity czasu i ponowne próby](#timeouts-and-retries)). Użyj tej
wymiany, aby dynamicznie wybierać prompt, głos i narzędzia dla każdego połączenia —
pełny wzorzec znajdziesz w [przewodniku po dynamicznej konfiguracji połączeń](/pl/guides/dynamic-call-config).

<Note>
  Subskrybowane [punkty końcowe webhooków](/pl/webhooks/endpoints) również otrzymują
  `telephony.incoming` / `web.incoming` — dla **każdego** połączenia przychodzącego
  i sesji internetowej, niezależnie od konfiguracji agenta — ale te dostarczenia są
  powiadomieniami typu fire-and-forget z `event_id`, nigdy blokującymi.
  Tylko starszy webhook z jednym adresem URL obsługuje wymianę konfiguracji
  opisaną na tej stronie. Struktury powiadomień punktów końcowych znajdują się w
  [katalogu zdarzeń](/pl/webhooks/events).
</Note>

## Przekroczenia limitu czasu i ponowne próby

Odpowiadaj szybko: rozmówca czeka na linii, gdy podejmujesz decyzję.

W przypadku połączenia telefonicznego (`telephony.incoming`):

- Pierwsza próba może trwać do **10 sekund**.
- Jeśli przekroczy limit czasu, nie może się połączyć lub otrzyma odpowiedź
  `5xx`, `408` albo `429`, ThunderPhone podejmuje jeszcze jedną próbę. Ponowna próba otrzymuje pozostałą część
  **13-sekundowego** budżetu na całą wymianę: około 3 sekundy po
  pierwszej próbie, która przekroczyła limit czasu, i prawie całe 13 sekund po szybkim niepowodzeniu.
  Nie podejmuje się ponownej próby, gdy pozostałyby mniej niż 2 sekundy.
- Każda inna odpowiedź `4xx`, odpowiedź przekraczająca
  [limit rozmiaru](#response-size-limit) lub odpowiedź `2xx`, której treść nie jest
  prawidłową konfiguracją, jest ostateczna i nie jest ponawiana.
- Obie próby wysyłają tę samą treść, ten sam nagłówek `X-ThunderPhone-Signature`
  oraz ten sam nagłówek `X-ThunderPhone-Idempotency-Key`
  (`telephony-incoming-<call_id>`), dzięki czemu procedura obsługi powodująca skutki uboczne
  może rozpoznać ponowną próbę.

Nie ma agenta awaryjnego. Jeśli nie nadejdzie użyteczna konfiguracja,
połączenie jest natychmiast kończone, zamiast pozostawiać rozmówcę na cichej
linii. W Historii połączeń jest widoczne jako nieudane połączenie, za które nie naliczono opłaty, z
`end_reason` `inbound_config_webhook_failed`, a standardowe zdarzenie
[`telephony.complete`](/pl/webhooks/call-complete) jest wysyłane.

Sesja widżetu (`web.incoming`) otrzymuje jedną próbę trwającą do 10
sekund. Jeśli się nie powiedzie, żądanie sesji kończy się błędem `502` lub `422`.

<Warning>
  **Połączenia konfigurowane przez webhook nie zawierają komunikatu ThunderPhone
  dotyczącego zgody.** Połączenia skonfigurowane przez tę wymianę omijają
  komunikat na poziomie agenta odtwarzany na początku połączenia i są wyraźnie wyłączone z
  mechanizmu komunikatów ThunderPhone dotyczących zgody (Regulamin,
  sekcja „Nagrywanie i zgoda”). Twoja organizacja ponosi wyłączną
  odpowiedzialność za wszystkie wymagane w tych połączeniach informacje i zgody dotyczące
  nagrywania, monitorowania, udziału AI oraz identyfikacji rozmówcy —
  połączenia nadal mogą być nagrywane, transkrybowane, analizowane i obsługiwane
  przez AI. Przed włączeniem tej ścieżki uwzględnij wymagane informacje
  w swoim własnym przepływie połączenia.
</Warning>

## Ładunek żądania

Dla połączeń telefonicznych (`telephony.incoming`):

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

| Pole | Typ | Opis |
|-------|------|-------------|
| `call_id` | integer | Identyfikator połączenia — stały we wszystkich zdarzeniach dla tego połączenia |
| `from_number` | string | Numer rozmówcy w formacie E.164 |
| `to_number` | string | Miejsce docelowe E.164 (jeden z Twoich numerów ThunderPhone) |

W przypadku sesji widżetu internetowego (`web.incoming`) pole `data` identyfikuje
stronę osadzającą zamiast numerów telefonów:

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

| Pole | Typ | Opis |
|-------|------|-------------|
| `call_id` | integer | Identyfikator połączenia |
| `origin_domain` | string | Źródło strony hostującej widżet, przesłane przez przeglądarkę. Klient inny niż przeglądarka może wysłać dowolną wartość, dlatego nie traktuj go jako dowodu miejsca, z którego pochodzi żądanie |
| `publishable_key_prefix` | string | Pierwsze znaki klucza publicznego, który otworzył sesję |
| `language`, `primary_language` | string | Obecne, gdy sesja widżetu zażądała zastąpienia języka |
| `voice` | string | Obecne, gdy sesja widżetu zażądała zastąpienia głosu |
| `website_context` | string | Obecne, gdy widżet przekazał kontekst strony dla sesji |

Pola zastępujące (`language`, `voice`, `website_context` oraz wszelkie
przeglądarkowe `variables`) są przekazywane tylko wtedy, gdy klucz publiczny zezwala na nie w swoich [zastąpieniach przeglądarkowych](/pl/guides/embed-web-widget). Żądanie sesji zawierające pole, na które klucz nie zezwala, jest odrzucane przed wywołaniem tego webhooka.

<Note>
  Widżety w trybie webhooka dostarczają to żądanie do własnego adresu
  `webhook_url` klucza publikowalnego, jeśli jest ustawiony, a w przeciwnym razie do
  adresu URL webhooka na poziomie organizacji. W obu przypadkach jest ono podpisywane
  za pomocą organizacyjnego `secret` webhooka.
</Note>

---

## Schemat odpowiedzi

Zwróć obiekt JSON opisujący konfigurację agenta dla tego połączenia.
`prompt` i `voice` są wymagane; wszystkie pozostałe pola są opcjonalne.

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

| Pole | Typ | Wymagane | Opis |
|-------|------|----------|-------------|
| `prompt` | ciąg znaków | tak | Prompt systemowy sterujący agentem |
| `voice` | ciąg znaków | tak | Identyfikator głosu z [`GET /v1/voices`](/api-reference/agents#voices), np. `john`. `voice_name` jest akceptowane jako alias. Nieznane głosy nie przechodzą walidacji i powodują odrzucenie połączenia |
| `product` | ciąg znaków | nie | Domyślnie `spark`. Dozwolone: `spark`, `bolt`, `storm-base`, `storm-base-with-ack`, `storm-extra`, `storm-extra-with-ack` |
| `thinking_level` | ciąg znaków | nie | `minimal`, `base` (domyślnie) lub `extra`. Zastępowane dla produktów Storm: `storm-extra*` wymusza `extra`, pozostałe `storm-*` wymuszają `base` |
| `audio_context_mode` | ciąg znaków | nie | `full` (domyślnie) lub `reduced` |
| `watchdog_enabled` | wartość logiczna | nie | Włącz nadzór dla tego połączenia. Domyślnie `false` |
| `additional_audio_context` | wartość logiczna \| null | nie | Uwzględnia kilka ostatnich tur dźwięku rozmówcy zamiast tylko najnowszej tury, co poprawia korekty oraz zbieranie danych zawierających pisownię i liczby, kosztem niewielkiego wzrostu opóźnienia i kosztu. Domyślnie włączone dla sesji przychodzących i wyłączone dla wychodzących połączeń telefonicznych; `null` zachowuje ustawienie domyślne |
| `storm_feedback_mode` | ciąg znaków | nie | `none`, `acknowledgement` (domyślnie) lub `tick` |
| `language` | ciąg znaków | nie | Skrót dla `primary_language` |
| `primary_language` | ciąg znaków | nie | Kod języka, znormalizowany (domyślnie `en`). Kody, których nie można rozpoznać, powodują odrzucenie połączenia |
| `has_additional_languages` | wartość logiczna | nie | Domyślnie `false` |
| `additional_languages` | tablica ciągów znaków | nie | Dodatkowe języki, na które agent może się przełączyć |
| `native_voice_switching` | wartość logiczna | nie | Domyślnie `false`. Gdy połączenie przełącza się na inny język, zamień głos na rodzimy dla tego języka (dopasowany według płci), zamiast zachowywać skonfigurowany głos |
| `language_switching` | ciąg znaków \| null | nie | `ask_first` (domyślnie) lub `follow_caller`; `null` działa tak samo jak `ask_first`. Określa, jak wielojęzyczny agent obsługuje sytuację, gdy rozmówca mówi w innym z jego języków. `ask_first` przełącza się od razu, gdy rozmówca poprosi o język lub wypowie w nim pełne zdanie, a po czymś krótkim, takim jak „tak”, pyta, czy kontynuować w tym języku. `follow_caller` przełącza się na dowolny skonfigurowany język, którym mówi rozmówca, nawet po jednym słowie. Dotyczy tylko sytuacji, gdy ustawiono `additional_languages` |
| `background_track` | ciąg znaków \| null | nie | Identyfikator dźwięku tła lub `null` |
| `acknowledgement_prompt_mode` | ciąg znaków | nie | `auto` (domyślnie) lub `manual` (produkty Storm-with-ack) |
| `acknowledgement_prompt` | ciąg znaków | nie | Używane, gdy `acknowledgement_prompt_mode="manual"` |
| `silence_interval_seconds` | liczba całkowita \| null | nie | 5–120. Liczba sekund ciszy rozmówcy przed sprawdzeniem |
| `silence_max_checkins` | liczba całkowita \| null | nie | 1–10 |
| `silence_checkins_enabled` | wartość logiczna | nie | Domyślnie `true` |
| `connect_tone_enabled` | wartość logiczna | nie | Domyślnie `false` |
| `pronunciation_dicts` | wartość logiczna \| null | nie | Domyślnie `false`; `null` działa tak samo jak `false`. Gdy ustawiono `true`, mowa Cartesia w połączeniu używa słownika wymowy ThunderPhone dla języka połączenia, jeśli taki istnieje. Działa w połączeniach telefonicznych i widżetach. Dotyczy tylko własnych głosów Cartesia ThunderPhone, a nie głosów używanych z własnym kluczem API Cartesia. Nie jest używane z `agent_id`, gdzie obowiązują ustawienia zapisanego agenta |
| `voicemail_action` | ciąg znaków | nie | `prompt` (domyślnie), `hangup` lub `message` |
| `voicemail_message` | ciąg znaków | nie | Używane, gdy `voicemail_action="message"` |
| `agent_name` | ciąg znaków | nie | Nazwa wyświetlana raportowana w panelach i widżecie |
| `org_name` | ciąg znaków | nie | Nazwa wyświetlana organizacji dla persony agenta |
| `tools` | tablica | nie | Wbudowane schematy narzędzi funkcji (zobacz [Narzędzia funkcji](/pl/tools/overview)) |
| `transfer_destinations` | tablica obiektów \| null | nie | Do 50 wpisów `{name, number}`, do których to połączenie może zostać przekazane, walidowanych tak jak [numery przekierowań](/api-reference/agents#transfer-numbers) agenta. Agent przekazuje połączenia według nazwy i tylko do tych wpisów; `[]` oznacza brak przekierowań. Pomiń to pole lub wyślij `null`, aby pozwolić agentowi przekazać połączenie na wybrany przez niego numer |
| `timezone` | ciąg znaków | nie | Strefa czasowa IANA dla tego połączenia, np. `America/Chicago`. Agent podaje w niej datę i godzinę, z pierwszeństwem przed własną strefą czasową. Dozwolone także obok `agent_id`. Wartości, które nie są strefami IANA, są ignorowane. Zobacz [Ustaw strefę czasową dla każdego połączenia](/pl/guides/dynamic-call-config) |
| `call_id` | liczba całkowita | nie | Opcjonalne powtórzenie identyfikatora połączenia z żądania; ignorowane |

<Note>
  Nieznane klucze najwyższego poziomu są po cichu **ignorowane** — pole z literówką
  w nazwie nie powoduje odrzucenia konfiguracji, ale nie jest stosowane. Kolejność
  mówienia i `max_hold_seconds` nie są tutaj akceptowane; można je
  skonfigurować wyłącznie dla samego [Agenta](/api-reference/agents).
</Note>

Ponieważ `prompt` i `voice` są wymagane, zwrócenie `{}` lub dowolnej
odpowiedzi, która nie przejdzie walidacji, odrzuca połączenie: połączenie telefoniczne jest kończone
zgodnie z opisem w [Limitach czasu i ponowieniach](#timeouts-and-retries), a żądanie sesji
widżetu kończy się błędem `422`. Na tej ścieżce nie ma awaryjnego użycia statycznego agenta
(numer lub klucz w trybie webhook nie ma przypisanego agenta).

---

## Limit rozmiaru odpowiedzi

<Warning>
  Odpowiedzi konfiguracji są ograniczone do **5 MiB**. Jeśli moduł obsługi
  zwróci większą odpowiedź, również ze statusem `2xx`,
  ThunderPhone zgłosi, że odpowiedź przekroczyła limit, i
  odrzuci połączenie lub sesję widżetu. Ogranicz odpowiedź do pól
  potrzebnych do konfiguracji połączenia; duże dane hostuj za narzędziami funkcyjnymi
  lub inną usługą zamiast osadzać je w konfiguracji.
</Warning>

---

## Przykładowy moduł obsługi

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

---

## Odpowiedź z narzędziami funkcyjnymi

Dołącz narzędzia, aby AI mogła wywoływać Twoje API w trakcie rozmowy:

```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>
  Żądania do punktów końcowych narzędzi są podpisywane tym **samym tajnym kluczem webhooka
  organizacji**, który podpisał tę wymianę. Dokładny schemat i format podpisanego
  żądania znajdziesz w [Narzędziach funkcyjnych](/pl/tools/overview).
</Tip>

---

## Ściągawka dotycząca poziomów produktów

| Produkt | Opóźnienie | Rozumowanie | Potwierdzenie |
|---------|------------|-------------|---------------|
| `spark` | Najniższe | Podstawowe | — |
| `bolt` | Niskie | Ulepszone | — |
| `storm-base` | Średnie | Zaawansowane | — |
| `storm-base-with-ack` | Średnie | Zaawansowane | Automatyczna fraza wypełniająca podczas przetwarzania |
| `storm-extra` | Wyższe | Głębokie | — |
| `storm-extra-with-ack` | Wyższe | Głębokie | Automatyczna fraza wypełniająca podczas przetwarzania |

---

## Powiązane

<CardGroup cols={2}>
  <Card title="telephony.complete / web.complete" icon="phone-slash" href="/pl/webhooks/call-complete">
    Nieblokujące zdarzenie zakończenia połączenia.
  </Card>
  <Card title="Narzędzia funkcji" icon="screwdriver-wrench" href="/pl/tools/overview">
    Pełny schemat JSON dla `tools[]` i podpisany kontrakt endpointu.
  </Card>
  <Card title="Endpointy webhooków" icon="bolt" href="/pl/webhooks/endpoints">
    Subskrybuj wiele adresów URL dla `telephony.incoming` / `web.incoming`.
  </Card>
  <Card title="Dynamiczna konfiguracja połączeń" icon="wand-magic-sparkles" href="/pl/guides/dynamic-call-config">
    Wzorce promptów, narzędzi i testów A/B dla poszczególnych rozmówców.
  </Card>
</CardGroup>
