---
title: "Dünaamiline kõnepõhine seadistamine"
description: "Vali vastav häälagent või kirjuta selle juhis ja seaded ümber iga sissetuleva kõne jaoks eraldi, lähtudes sinu hallatavas veebikonksus olevast kohandatud loogikast."
---

Vaikimisi on igale telefoninumbrile ja avaldatavale võtmele määratud staatiline agent. Kui vajad **helistajapõhist** või **külastajapõhist** kohandamist — VIP-marsruutimist, sisselogitud kasutaja konteksti, A/B-viibetestimist — lülitu webhooki režiimi ja lase oma serveril otsustada.

## Kuidas see toimib

1. Telli sündmus [`telephony.incoming`](/et/webhooks/events)
   (telefon) või [`web.incoming`](/et/webhooks/events) (vidin).
   Mõlemad on **blokeerivad** webhookid: ThunderPhone ootab enne kõnega
   jätkamist sinu vastust kuni 10 sekundit.
2. ThunderPhone saadab sulle `{call_id, from_number, to_number}` (vidina
   seansid sisaldavad numbrite asemel vidinapõhiseid välju — vaata
   [päringuskeemi](/et/webhooks/call-incoming)).
3. Sinu server vastab agendi konfiguratsiooniga (viip, hääl, toode,
   tööriistad). ThunderPhone kasutab seda konfiguratsiooni kõne jaoks.
4. Kui tagastad `{}`, vastamine aegub või tekib viga, kasutatakse varuna
   staatiliselt määratud agenti. Turvaline vaikeseade.

<Note>
  Toimib telefonikõnede (`telephony.incoming`) ja vidinaseansside
  (`web.incoming`) puhul identselt, olenemata sellest, kas sündmus
  edastatakse webhooki lõpp-punkti või pärandühte-URL-i webhooki kaudu.
</Note>

<Warning>
  **Veebikonksuga seadistatud reasisestel kõnedel ei esitata ThunderPhone'i
  nõusolekuteadet.** See tee jätab agenditaseme kõnealustuse teate vahele
  ning on ThunderPhone'i nõusolekuteadete raamistikust (teenusetingimuste
  jaotis „Salvestamine ja nõusolek”) selgesõnaliselt välja jäetud. Sinu
  organisatsioon vastutab nende kõnede kõigi salvestamise, jälgimise,
  tehisintellekti osalemise ja helistaja tuvastamise teadete ning
  nõusolekute eest. Esita need oma voos — näiteks viiba avaskriptis —
  enne reasisese webhooki režiimi lubamist. Webhooki vastus, mis viitab
  salvestatud `agent_id`-le, kasutab selle agendi tavapärast salvestamise
  ja avalikustamise poliitikat.
</Warning>

## 1. Seadista webhooki sihtkoht

<Tabs>
<Tab title="Telefonikõned">
Telefoninumbrite jaoks telli oma lõpp-punkt sündmusele `telephony.incoming`:

```bash
curl -X POST https://api.thunderphone.com/v1/developer/webhook-endpoints \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "label":  "Prod call-incoming",
    "url":    "https://example.com/thunderphone/incoming",
    "events": ["telephony.incoming"]
  }'
```

Vastus sisaldab ühekordset `secret`-it — salvesta see; kasutad seda
allkirja kontrollimiseks.
</Tab>
<Tab title="Veebividin">
Vidinaseansside jaoks loo avaldatav võti režiimis `mode="webhook"`,
mille sisse on lisatud sinu lõpp-punkti URL:

```bash
curl -X POST https://api.thunderphone.com/v1/publishable-key \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name":            "Dynamic widget",
    "mode":            "webhook",
    "webhook_url":     "https://example.com/thunderphone/widget-incoming",
    "allowed_domains": ["example.com"]
  }'
```

Vidin saadab iga seansi alustamisel sellele URL-ile POST-päringu.
</Tab>
</Tabs>

## 2. Rakenda töötleja

Kolm rusikareeglit:

- **Kontrolli signatuuri** igal päringul (vt
  [Veebikonksu signatuuride kontrollimine](/et/guides/verify-webhook-signatures)).
  Ära jäta seda arenduses vahele — tee see üks kord õigesti ja kasuta uuesti.
- **Vasta kiiresti**. Kümme sekundit on kindel ülempiir ning iga sekund on
  helistaja jaoks vaikus. Vajaduse korral tee andmebaasipäringuid, kuid
  ära kutsu allavoolu LLM-e sünkroonselt — kui soovid dünaamilist
  viibageneratsiooni, arvuta see eelnevalt ja salvesta vahemällu.
- **Kasuta puhast varuvarianti**. Iga ootamatu olek peab tagastama `{}`, et
  staatiliselt määratud häälagent kõne käsitleks.

<CodeGroup>
```python FastAPI
import hashlib
import hmac
import json
import os

from fastapi import FastAPI, HTTPException, Request

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

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

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

    event = json.loads(body)
    if event["type"] not in ("telephony.incoming", "web.incoming"):
        return {}  # fall back to default

    caller = event["data"]["from_number"]
    # Cheap DB lookup: is this a known VIP?
    customer = lookup_customer(caller)
    if customer and customer.tier == "vip":
        return {
            "prompt":  f"You are a VIP concierge for {customer.name}. Be proactive…",
            "voice":   "john",
            "product": "storm-base",
        }
    return {}  # default agent handles non-VIPs

def lookup_customer(phone: str):
    # ... your CRM integration ...
    pass
```

```javascript Express
import crypto from "node:crypto";
import express from "express";

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

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

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

    const IMPORTANT_TYPES = new Set([
      "telephony.incoming",
      "web.incoming",
    ]);
    if (!IMPORTANT_TYPES.has(event.type)) return res.json({});

    const customer = await lookupCustomer(event.data.from_number);
    if (customer?.tier === "vip") {
      return res.json({
        prompt:  `You are a VIP concierge for ${customer.name}. Be proactive…`,
        voice:   "john",
        product: "storm-base",
      });
    }
    res.json({}); // fall back to default agent
  },
);
```
</CodeGroup>

## 3. Vastuse skeem

Vastuse sisu vastab täpselt
[sissetuleva kõne vastuse skeemile](/et/webhooks/call-incoming).
Levinud väljad:

| Väli | Tüüp | Kirjeldus |
|-------|------|-------------|
| `prompt` | string (kohustuslik) | Agendi süsteemijuhis |
| `voice` | string (kohustuslik) | Hääle ID asukohast [`GET /v1/voices`](/api-reference/agents#voices) |
| `product` | string | Vaikimisi on `spark` |
| `background_track` | string \| null | Taustaheli ID |
| `acknowledgement_prompt_mode` | string | `auto` või `manual` (ainult Storm koos kinnitusega) |
| `acknowledgement_prompt` | string | Kohustuslik, kui režiim on `manual` |
| `tools` | array | Tekstisisesed funktsioonitööriistade skeemid — vaata [funktsioonitööriistu](/et/tools/overview) |

<Note>
  Kõnepõhine kõnelemisjärjekord ja `max_hold_seconds` pole
  veebikonksu vastuses saadaval. Määra need viidatud
  [agendis](/api-reference/agents).
</Note>

### Hoia salvestatud agenti ja edasta muutujaid

Tagasta `{"agent_id": 12, "variables": {"name": "Ada"}}`, et kasutada selle organisatsiooni
salvestatud agenti koos kõnepõhiste andmetega. Selle juhis võib sisaldada `{{name}}` või
`{{name|Friend}}`. Veebikonksu muutujad liidetakse päringutaseme muutujate peale; null
kasutab kohatäite vaikeväärtust või tühja teksti, kui vaikeväärtust pole määratud. Lõplikud
väärtused ja lahendamata nimed kuvatakse kõne üksikasjades ning lõpetamise veebikonksudes.
Salvestatud agendi vastused aktsepteerivad ainult `agent_id` ja `variables`. Kui `prompt` on
olemas, kasutab vastus tekstisisest konfiguratsiooni ja ignoreerib `agent_id`-d (sealhulgas
null- või mittetäisarvulisi metaandmeid); tekstisisene juhis peab siiski olema kehtiv.
Tekstisisese konfiguratsiooni vastused võivad samuti sisaldada `variables`. Salvestatud agendi
vastused kasutavad agendi avaldatud A/B-jaotust nii telefoni- kui ka vidinakõnedes ning seejärel
renderdavad muutujad. Piirangute ja seansi API toe kohta vaata [kõnemuutujaid](/et/guides/call-variables).
Blokeeriv konfiguratsioon pärineb pärandtelefoninumbri/organisatsiooni URL-ist või veebikonksurežiimis
vidina võtmest; lõpp-punkti süsteemi sissetulevad sündmused on ainult teavitused.

## Mustrite näited

### Sisselogitud kasutaja kontekst

Veebikonksurežiimis vidinates teab külastaja leht juba, kes ta
on. Kutsu oma veebikonks välja päringustringi parameetriga, mille vidina SDK
edastab (`?customer_id=123`), ja otsi klient serveripoolel üles.

### A/B-juhise kasutuselevõtt

Enne kui selle ise käsitsi lahendad, pane tähele, et ThunderPhone'il on sisseehitatud
[eksperimentide](/et/guides/concepts) funktsioon
(`/dashboard/experiments` ja agendiehitaja **A/B** vahekaart), mis
määratleb variandid, jagab liikluse ja võrdleb tulemusi variantide kaupa —
veebikonksu pole vaja.

Kui vajad siiski veebikonksupoolset juhtimist: räsi `call_id` → ämber;
serveeri juhist A väärtustele `0..49` ja juhist B väärtustele `50..99`. Salvesta,
millise ämbri valisid, oma andmebaasi ning korreleeri see hiljem lõpetatud
kõne hindega.

### Ajapõhine suunamine

Tööajal → „otseabi” agent; väljaspool tööaega → „võta sõnum”
agent. Puhtalt lüliti `new Date().getUTCHours()` põhjal sinu töötlejas.

---

## Järgmised sammud

<CardGroup cols={2}>
  <Card title="Sissetuleva kõne veebikonksu viide" icon="phone" href="/et/webhooks/call-incoming">
    Täpsed päringu- ja vastuseskeemid, sealhulgas kõik konfiguratsioonivõtmed.
  </Card>
  <Card title="Kontrolli veebikonksu allkirju" icon="shield-check" href="/et/guides/verify-webhook-signatures">
    Seadista HMAC üks kord õigesti; kasuta seda kõikjal uuesti.
  </Card>
  <Card title="Loo tööriistaintegratsioon" icon="screwdriver-wrench" href="/et/guides/build-tool-integration">
    Ühenda dünaamiline suunamine agendipõhiste tööriistadega.
  </Card>
  <Card title="Edastamise semantika" icon="bolt" href="/et/webhooks/overview">
    Korduskatsed, järjestamine, ajalõpud.
  </Card>
</CardGroup>
