---
title: "Usanidi unaobadilika kwa kila simu"
description: "Chagua ejenti anayejibu — au andika upya prompt na mipangilio yake — kivyake kwa kila simu inayoingia, kwa kuongozwa na mantiki maalum katika webhook unayoidhibiti."
---

Kwa chaguomsingi, kila nambari ya simu na ufunguo unaoweza kuchapishwa una ejenti tuli
iliyowekwa. Unapohitaji ubinafsishaji wa **kila mpigaji** au **kila mgeni**
— uelekezaji wa VIP, muktadha wa mtumiaji aliyeingia, majaribio ya prompt ya A/B — badilisha hadi
hali ya webhook na uruhusu seva yako iamue.

## Jinsi inavyofanya kazi

1. Jiandikishe kwa tukio la [`telephony.incoming`](/sw/webhooks/events)
   (simu) au [`web.incoming`](/sw/webhooks/events) (wijeti).
   Zote ni webhook **zinazosubiri**: ThunderPhone husubiri hadi
   sekunde 10 kwa jibu lako kabla ya kuendelea na simu.
2. ThunderPhone hukutumia `{call_id, from_number, to_number}` (vipindi vya wijeti
   hubeba sehemu mahususi za wijeti badala ya nambari — tazama
   [schema ya ombi](/sw/webhooks/call-incoming)).
3. Seva yako hujibu kwa usanidi wa ejenti (prompt, sauti,
   bidhaa, zana). ThunderPhone hutumia usanidi huo kwa simu.
4. Ukirejesha `{}`, ukichelewa kujibu, au hitilafu ikitokea, ejenti iliyowekwa
   kwa njia tuli hutumika kama mbadala. Chaguo-msingi salama.

<Note>
  Hufanya kazi kwa namna sawa kwa simu za kawaida (`telephony.incoming`) na vipindi vya
  wijeti (`web.incoming`), iwe vinawasilishwa kwenye endpoint ya webhook
  au webhook ya zamani ya URL moja.
</Note>

<Warning>
  **Hakuna tangazo la idhini la ThunderPhone linalochezwa kwenye simu
  zilizosanikishwa kwa webhook ya ndani.** Njia hii hupita tangazo la kuanza simu
  la kiwango cha ejenti na imetengwa wazi kutoka kwenye mfumo wa ThunderPhone wa
  matangazo ya idhini (Masharti ya Huduma, sehemu ya "Kurekodi na idhini").
  Shirika lako linawajibika kikamilifu kwa notisi na idhini zote za kurekodi,
  kufuatilia, ushiriki wa AI, na utambulisho wa mpigaji kwenye simu hizi. Zitoe
  katika mtiririko wako mwenyewe — kwa mfano katika script ya ufunguzi ya prompt —
  kabla ya kuwasha hali ya webhook ya ndani. Jibu la webhook linalorejelea
  `agent_id` iliyohifadhiwa hutumia sera ya kawaida ya ejenti hiyo ya kurekodi na ufichuzi.
</Warning>

## 1. Sanidi mahali webhook itakapotumwa

<Tabs>
<Tab title="Simu za kawaida">
Kwa nambari za simu, jiandikishe endpoint yako kwa `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"]
  }'
```

Jibu linajumuisha `secret` ya matumizi ya mara moja — ihifadhi; utaitumia
kwa uthibitishaji wa sahihi.
</Tab>
<Tab title="Wijeti ya wavuti">
Kwa vipindi vya wijeti, unda ufunguo unaoweza kuchapishwa katika `mode="webhook"`
ukiwa na URL ya endpoint yako tayari imejumuishwa:

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

Wijeti itatuma POST kwenye URL hii kila kipindi kinapoanza.
</Tab>
</Tabs>

## 2. Tekeleza kishughulikiaji

Kanuni tatu za kuzingatia:

- **Thibitisha sahihi** kwenye kila ombi (tazama
  [Thibitisha sahihi za webhook](/sw/guides/verify-webhook-signatures)).
  Usiruke hatua hii katika dev — ifanye kwa usahihi mara moja na uitumie tena.
- **Jibu haraka**. Sekunde kumi ndiyo kikomo cha juu, na kila sekunde ni
  ukimya kwa mpigaji simu. Fanya utafutaji wa hifadhidata ikihitajika, lakini
  usipigie LLM za baadaye kwa usawazishaji — ukitaka utengenezaji wa prompt unaobadilika, hesabu mapema na uhifadhi kwenye cache.
- **Rudi kwenye chaguo la msingi kwa usafi**. Hali yoyote isiyotarajiwa inapaswa kurejesha `{}` ili
  ejenti iliyowekwa kwa uthabiti ishughulikie simu.

<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. Schema ya jibu

Mwili wa jibu unalingana na
[schema ya jibu la simu inayoingia](/sw/webhooks/call-incoming)
haswa. Sehemu zinazotumiwa mara nyingi:

| Sehemu | Aina | Maelezo |
|-------|------|-------------|
| `prompt` | mfuatano wa maandishi (inahitajika) | Prompt ya mfumo kwa ejenti |
| `voice` | mfuatano wa maandishi (inahitajika) | Kitambulisho cha sauti kutoka [`GET /v1/voices`](/api-reference/agents#voices) |
| `product` | mfuatano wa maandishi | Chaguo-msingi ni `spark` |
| `background_track` | mfuatano wa maandishi \| null | Kitambulisho cha sauti ya mazingira |
| `acknowledgement_prompt_mode` | mfuatano wa maandishi | `auto` au `manual` (Storm yenye uthibitisho pekee) |
| `acknowledgement_prompt` | mfuatano wa maandishi | Inahitajika wakati hali ni `manual` |
| `tools` | orodha | Schema za zana za vitendaji za ndani — tazama [Zana za Vitendaji](/sw/tools/overview) |

<Note>
  Mpangilio wa kuzungumza kwa simu moja na `max_hold_seconds` havipatikani kwenye
  jibu la webhook. Viweke kwenye
  [Ejenti](/api-reference/agents) unayorejelea.
</Note>

### Hifadhi ejenti iliyohifadhiwa na utoe vigeu

Rudisha `{"agent_id": 12, "variables": {"name": "Ada"}}` ili kutumia ejenti
iliyohifadhiwa ya shirika hilo pamoja na data ya kila simu. Prompt yake inaweza kuwa na `{{name}}` au
`{{name|Friend}}`. Vigeu vya webhook huunganishwa juu ya vigeu vya kiwango cha ombi; null
hutumia chaguo-msingi la kishikilia nafasi, au maandishi matupu ikiwa hakuna lililotolewa. Thamani
za mwisho na majina ambayo hayajatatuliwa huonekana katika maelezo ya simu na webhook za ukamilishaji.
Majibu ya ejenti iliyohifadhiwa hukubali `agent_id` na `variables` pekee. Ikiwa `prompt`
ipo, jibu hutumia usanidi wa ndani na hupuuza `agent_id` (ikiwemo metadata ya
null au isiyo namba kamili); prompt ya ndani lazima bado iwe halali. Majibu ya usanidi wa ndani
yanaweza pia kujumuisha `variables`. Majibu ya ejenti iliyohifadhiwa hutumia mgawanyo wa A/B
uliotumwa wa ejenti kwenye simu za simu na wijeti, kisha hutoa vigeu. Tazama [vigeu vya simu](/sw/guides/call-variables)
kwa vikwazo na usaidizi wa API ya kipindi. Usanidi wa kuzuia hutoka kwenye URL ya zamani ya
namba ya simu/shirika au ufunguo wa wijeti wa hali ya webhook; matukio ya endpoint-system
yanayoingia ni arifa pekee.

## Mifumo

### Muktadha wa mtumiaji aliyeingia

Katika wijeti za hali ya webhook, ukurasa wa mgeni tayari unajua yeye
ni nani. Piga webhook yako kwa kigezo cha mfuatano wa hoja ambacho SDK
ya wijeti hupitisha (`?customer_id=123`) na umtafute mteja upande wa seva.

### Utoaji wa prompt wa A/B

Kabla ya kutekeleza hili mwenyewe, fahamu kwamba ThunderPhone ina kipengele cha ndani cha
[Majaribio](/sw/guides/concepts)
(`/dashboard/experiments` na kichupo cha **A/B** cha kiunda ejenti) kinachofafanua
vibadala, kugawanya trafiki, na kulinganisha matokeo kwa kila kibadala —
hakuna webhook inayohitajika.

Ikiwa bado unahitaji udhibiti upande wa webhook: fanya hesabu ya mseto wa `call_id` → kikundi;
toa prompt A kwa `0..49` na prompt B kwa `50..99`. Rekodi kikundi
ulichochagua katika DB yako mwenyewe na baadaye ukihusianishe na alama ya simu
iliyokamilika.

### Uelekezaji kulingana na muda

Saa za kazi → ejenti wa "usaidizi wa moja kwa moja"; baada ya saa za kazi → ejenti wa "chukua ujumbe".
Badilisha moja kwa moja kwa kutumia `new Date().getUTCHours()` katika kishughulikiaji chako.

---

## Hatua zinazofuata

<CardGroup cols={2}>
  <Card title="Marejeleo ya webhook ya simu inayoingia" icon="phone" href="/sw/webhooks/call-incoming">
    Schema sahihi za ombi + jibu, zikiwemo kila ufunguo wa usanidi.
  </Card>
  <Card title="Thibitisha sahihi za webhook" icon="shield-check" href="/sw/guides/verify-webhook-signatures">
    Sanidi HMAC kwa usahihi mara moja; itumie tena kila mahali.
  </Card>
  <Card title="Jenga ujumuishaji wa zana" icon="screwdriver-wrench" href="/sw/guides/build-tool-integration">
    Unganisha uelekezaji unaobadilika na zana za kila ejenti.
  </Card>
  <Card title="Semantiki za uwasilishaji" icon="bolt" href="/sw/webhooks/overview">
    Majaribio tena, mpangilio, muda wa kuisha.
  </Card>
</CardGroup>
