> ## Documentation Index
> Fetch the complete documentation index at: https://docs.thunderphone.com/llms.txt
> Use this file to discover all available pages before exploring further.

# telephony.incoming / web.incoming

> Webhook chặn định hình cấu hình của cuộc gọi đến theo thời gian thực.

Khi một cuộc gọi điện thoại đến một số **chưa được gán
tác nhân**, hoặc một phiên widget web bắt đầu trên một publishable key ở
`mode="webhook"`, ThunderPhone gửi một yêu cầu `telephony.incoming` /
`web.incoming` **chặn** đến [URL webhook cũ](/api-reference/organizations#legacy-single-url-webhook)
của bạn và chờ tối đa **10 giây** để nhận phản hồi cấu hình. Sử dụng
trao đổi này để chọn động prompt, giọng nói và công cụ cho từng cuộc gọi —
xem [hướng dẫn cấu hình cuộc gọi động](/vi/guides/dynamic-call-config)
để biết quy trình đầy đủ.

<Note>
  Các [endpoint webhook](/vi/webhooks/endpoints) đã đăng ký cũng nhận
  `telephony.incoming` / `web.incoming` — cho **mọi** cuộc gọi đến
  và phiên web, dù có cấu hình tác nhân hay không — nhưng các lần gửi đó là
  thông báo gửi rồi bỏ qua kèm `event_id`, không bao giờ chặn.
  Chỉ webhook URL đơn cũ mới thực hiện trao đổi cấu hình trên trang này.
  Cấu trúc thông báo endpoint nằm trong
  [danh mục sự kiện](/vi/webhooks/events).
</Note>

Trao đổi chặn không có phương án dự phòng: nếu handler của bạn trả về
trạng thái không phải 2xx, hết thời gian chờ hoặc trả về cấu hình không vượt qua
xác thực, cuộc gọi sẽ bị từ chối (cuộc gọi điện thoại không kết nối; yêu cầu
phiên widget thất bại với `502`/`422`). Phản hồi nhanh — người gọi đang
nghe âm báo đổ chuông trong khi bạn quyết định.

## Payload yêu cầu

Đối với cuộc gọi điện thoại (`telephony.incoming`):

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

| Trường        | Loại    | Mô tả                                                       |
| ------------- | ------- | ----------------------------------------------------------- |
| `call_id`     | integer | ID cuộc gọi — ổn định trên mọi sự kiện của cuộc gọi này     |
| `from_number` | string  | Số điện thoại người gọi theo E.164                          |
| `to_number`   | string  | Đích đến theo E.164 (một trong các số ThunderPhone của bạn) |

Đối với phiên widget web (`web.incoming`), `data` xác định
trang nhúng thay vì số điện thoại:

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

| Trường                         | Loại    | Mô tả                                               |
| ------------------------------ | ------- | --------------------------------------------------- |
| `call_id`                      | integer | ID cuộc gọi                                         |
| `origin_domain`                | string  | Origin của trang lưu trữ widget                     |
| `publishable_key_prefix`       | string  | Các ký tự đầu của publishable key đã mở phiên       |
| `language`, `primary_language` | string  | Có khi phiên widget yêu cầu ghi đè ngôn ngữ         |
| `voice`                        | string  | Có khi phiên widget yêu cầu ghi đè giọng nói        |
| `website_context`              | string  | Có khi widget truyền ngữ cảnh trang theo từng phiên |

<Note>
  Widget ở chế độ webhook gửi yêu cầu này đến `webhook_url` riêng của
  publishable key khi được thiết lập, nếu không sẽ dùng URL webhook cấp tổ chức.
  Dù bằng cách nào, yêu cầu cũng được ký bằng `secret` webhook của tổ chức.
</Note>

***

## Lược đồ phản hồi

Trả về một đối tượng JSON mô tả cấu hình tác nhân cho cuộc gọi này.
`prompt` và `voice` là bắt buộc; mọi trường khác là tùy chọn.

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

| Trường                        | Loại            | Bắt buộc | Mô tả                                                                                                                                                                                                          |
| ----------------------------- | --------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `prompt`                      | string          | có       | System prompt điều khiển tác nhân                                                                                                                                                                              |
| `voice`                       | string          | có       | ID giọng nói từ [`GET /v1/voices`](/api-reference/agents#voices), ví dụ: `john`. `voice_name` được chấp nhận làm bí danh. Giọng nói không xác định sẽ không vượt qua quá trình xác thực và cuộc gọi bị từ chối |
| `product`                     | string          | không    | Mặc định là `spark`. Giá trị được phép: `spark`, `bolt`, `storm-base`, `storm-base-with-ack`, `storm-extra`, `storm-extra-with-ack`                                                                            |
| `thinking_level`              | string          | không    | `minimal`, `base` (mặc định), hoặc `extra`. Bị ghi đè cho sản phẩm Storm: `storm-extra*` bắt buộc `extra`, các `storm-*` khác bắt buộc `base`                                                                  |
| `audio_context_mode`          | string          | không    | `full` (mặc định) hoặc `reduced`                                                                                                                                                                               |
| `watchdog_enabled`            | boolean         | không    | Bật giám sát cho cuộc gọi này. Mặc định là `false`                                                                                                                                                             |
| `storm_feedback_mode`         | string          | không    | `none`, `acknowledgement` (mặc định), hoặc `tick`                                                                                                                                                              |
| `language`                    | string          | không    | Dạng viết tắt của `primary_language`                                                                                                                                                                           |
| `primary_language`            | string          | không    | Mã ngôn ngữ, được chuẩn hóa (mặc định `en`). Mã không thể phân giải sẽ từ chối cuộc gọi                                                                                                                        |
| `has_additional_languages`    | boolean         | không    | Mặc định là `false`                                                                                                                                                                                            |
| `additional_languages`        | mảng string     | không    | Ngôn ngữ bổ sung mà tác nhân có thể chuyển sang                                                                                                                                                                |
| `background_track`            | string \| null  | không    | ID âm thanh nền hoặc `null`                                                                                                                                                                                    |
| `acknowledgement_prompt_mode` | string          | không    | `auto` (mặc định) hoặc `manual` (sản phẩm Storm-with-ack)                                                                                                                                                      |
| `acknowledgement_prompt`      | string          | không    | Được dùng khi `acknowledgement_prompt_mode="manual"`                                                                                                                                                           |
| `silence_interval_seconds`    | integer \| null | không    | 5–120. Số giây người gọi im lặng trước khi kiểm tra                                                                                                                                                            |
| `silence_max_checkins`        | integer \| null | không    | 1–10                                                                                                                                                                                                           |
| `silence_checkins_enabled`    | boolean         | không    | Mặc định là `true`                                                                                                                                                                                             |
| `connect_tone_enabled`        | boolean         | không    | Mặc định là `false`                                                                                                                                                                                            |
| `voicemail_action`            | string          | không    | `prompt` (mặc định), `hangup`, hoặc `message`                                                                                                                                                                  |
| `voicemail_message`           | string          | không    | Được dùng khi `voicemail_action="message"`                                                                                                                                                                     |
| `agent_name`                  | string          | không    | Tên hiển thị được báo cáo cho bảng điều khiển và tiện ích                                                                                                                                                      |
| `org_name`                    | string          | không    | Tên hiển thị của tổ chức cho persona của tác nhân                                                                                                                                                              |
| `tools`                       | mảng            | không    | Lược đồ công cụ hàm nội tuyến (xem [Công cụ hàm](/vi/tools/overview))                                                                                                                                          |
| `call_id`                     | integer         | không    | Echo tùy chọn của ID cuộc gọi trong yêu cầu; bị bỏ qua                                                                                                                                                         |

<Note>
  Các khóa cấp cao nhất không xác định sẽ bị **bỏ qua** một cách im lặng — tên
  trường bị gõ sai không khiến cấu hình bị từ chối, mà chỉ không được áp dụng. Thứ tự phát
  và `max_hold_seconds` không được chấp nhận ở đây; chúng chỉ có thể
  được cấu hình trên chính [Tác nhân](/api-reference/agents).
</Note>

Vì `prompt` và `voice` là bắt buộc, việc trả về `{}` hoặc bất kỳ
phản hồi nào không vượt qua xác thực sẽ từ chối cuộc gọi với `422` — không có
phương án dự phòng tác nhân tĩnh trên đường dẫn này (một số điện thoại hoặc khóa ở chế độ webhook
không có tác nhân được gán).

## Trình xử lý mẫu

<CodeGroup>
  ```python Python (FastAPI) theme={null}
  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) theme={null}
  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>

***

## Phản hồi kèm công cụ hàm

Đính kèm công cụ để tác nhân AI có thể gọi API của bạn trong khi hội thoại:

```json theme={null}
{
  "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>
  Yêu cầu đến endpoint công cụ được ký bằng **cùng secret webhook của tổ chức**
  đã ký exchange này. Xem
  [Công cụ hàm](/vi/tools/overview) để biết cấu trúc chính xác và định dạng
  yêu cầu đã ký.
</Tip>

***

## Bảng tóm tắt cấp sản phẩm

| Sản phẩm               | Độ trễ     | Suy luận   | Xác nhận                                   |
| ---------------------- | ---------- | ---------- | ------------------------------------------ |
| `spark`                | Thấp nhất  | Cơ bản     | —                                          |
| `bolt`                 | Thấp       | Cải thiện  | —                                          |
| `storm-base`           | Trung bình | Mạnh       | —                                          |
| `storm-base-with-ack`  | Trung bình | Mạnh       | Tự động lấp khoảng trống khi đang suy nghĩ |
| `storm-extra`          | Cao hơn    | Chuyên sâu | —                                          |
| `storm-extra-with-ack` | Cao hơn    | Chuyên sâu | Tự động lấp khoảng trống khi đang suy nghĩ |

***

## Liên quan

<CardGroup cols={2}>
  <Card title="telephony.complete / web.complete" icon="phone-slash" href="/vi/webhooks/call-complete">
    Sự kiện kết thúc cuộc gọi không chặn.
  </Card>

  <Card title="Công cụ hàm" icon="screwdriver-wrench" href="/vi/tools/overview">
    JSON schema đầy đủ cho `tools[]` và hợp đồng endpoint đã ký.
  </Card>

  <Card title="Endpoint webhook" icon="bolt" href="/vi/webhooks/endpoints">
    Đăng ký nhiều URL cho `telephony.incoming` / `web.incoming`.
  </Card>

  <Card title="Cấu hình cuộc gọi động" icon="wand-magic-sparkles" href="/vi/guides/dynamic-call-config">
    Mẫu cho prompt, công cụ và kiểm thử A/B theo từng người gọi.
  </Card>
</CardGroup>
