---
title: "telephony.incoming / web.incoming"
description: "เว็บฮุกแบบบล็อกที่กำหนดการตั้งค่าของสายเรียกเข้าแบบเรียลไทม์"
---

เมื่อสายโทรเข้ามาถึงหมายเลขที่**ไม่ได้กำหนด
เอเจนต์**หรือเซสชันวิดเจ็ตเว็บเริ่มต้นบนคีย์เผยแพร่ใน
`mode="webhook"` ThunderPhone จะส่งคำขอ `telephony.incoming` /
`web.incoming` **แบบบล็อก**ไปยัง
[URL เว็บฮุกเดิม](/api-reference/organizations#legacy-single-url-webhook)
ของคุณ และรอการตอบกลับการกำหนดค่าสูงสุด **10 วินาที** ใช้การแลกเปลี่ยนนี้
เพื่อเลือกพรอมต์ เสียง และเครื่องมือแบบไดนามิกสำหรับแต่ละสาย —
ดูรูปแบบการทำงานแบบครบวงจรได้ที่
[คู่มือการกำหนดค่าสายแบบไดนามิก](/th/guides/dynamic-call-config)

<Note>
  [ปลายทางเว็บฮุก](/th/webhooks/endpoints)ที่สมัครใช้งานจะได้รับ
  `telephony.incoming` / `web.incoming` ด้วย — สำหรับสายโทรเข้า
  และเซสชันเว็บ**ทุก**รายการ ไม่ว่าจะกำหนดค่าเอเจนต์หรือไม่ — แต่การส่งเหล่านั้น
  เป็นการแจ้งเตือนแบบส่งแล้วไม่รอผลพร้อม `event_id` และไม่เคยบล็อก
  เฉพาะเว็บฮุก URL เดี่ยวแบบเดิมเท่านั้นที่ดำเนินการแลกเปลี่ยนการกำหนดค่า
  ในหน้านี้ รูปแบบการแจ้งเตือนปลายทางอยู่ใน
  [แค็ตตาล็อกเหตุการณ์](/th/webhooks/events)
</Note>

การแลกเปลี่ยนแบบบล็อกไม่มีการสำรอง: หากตัวจัดการของคุณส่งคืนสถานะ
ที่ไม่ใช่ 2xx หมดเวลา หรือส่งคืนการกำหนดค่าที่ไม่ผ่านการตรวจสอบ
สายจะถูกปฏิเสธ (สายโทรศัพท์จะไม่เชื่อมต่อ; คำขอเซสชันวิดเจ็ต
ล้มเหลวด้วย `502`/`422`) ตอบกลับอย่างรวดเร็ว — ผู้โทรกำลังได้ยินเสียงเรียกเข้า
ขณะที่คุณตัดสินใจ

<Warning>
  **สายที่กำหนดค่าผ่านเว็บฮุกไม่มีประกาศความยินยอมของ ThunderPhone**
  สายที่กำหนดค่าผ่านการแลกเปลี่ยนนี้จะข้ามประกาศเมื่อเริ่มสายระดับเอเจนต์
  และถูกยกเว้นอย่างชัดเจนจากกรอบการทำงานด้านประกาศความยินยอมของ ThunderPhone
  (ข้อกำหนดการให้บริการ ส่วน "การบันทึกและความยินยอม") องค์กรของคุณ
  เป็นผู้รับผิดชอบแต่เพียงผู้เดียวสำหรับการแจ้งและความยินยอมเกี่ยวกับการบันทึก
  การติดตาม การมีส่วนร่วมของ AI และการระบุตัวผู้โทรทั้งหมดที่จำเป็นสำหรับสายเหล่านี้ —
  สายยังคงสามารถถูกบันทึก ถอดเสียง วิเคราะห์ และให้บริการโดย AI ได้
  สร้างการเปิดเผยข้อมูลที่จำเป็นไว้ในโฟลว์สายของคุณเองก่อนเปิดใช้เส้นทางนี้
</Warning>

## เพย์โหลดคำขอ

สำหรับสายโทรศัพท์ (`telephony.incoming`):

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

| ฟิลด์ | ประเภท | คำอธิบาย |
|-------|------|-------------|
| `call_id` | integer | รหัสสาย — คงที่ตลอดทุกเหตุการณ์ของสายนี้ |
| `from_number` | string | หมายเลขผู้โทรรูปแบบ E.164 |
| `to_number` | string | ปลายทางรูปแบบ E.164 (หนึ่งในหมายเลข ThunderPhone ของคุณ) |

สำหรับเซสชันวิดเจ็ตเว็บ (`web.incoming`) `data` จะระบุหน้าที่ฝังวิดเจ็ต
แทนหมายเลขโทรศัพท์:

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

| ฟิลด์ | ประเภท | คำอธิบาย |
|-------|------|-------------|
| `call_id` | integer | รหัสสาย |
| `origin_domain` | string | ต้นทางของหน้าที่โฮสต์วิดเจ็ต |
| `publishable_key_prefix` | string | อักขระแรกของคีย์เผยแพร่ที่เปิดเซสชัน |
| `language`, `primary_language` | string | มีอยู่เมื่อเซสชันวิดเจ็ตขอแทนที่ภาษาที่ใช้ |
| `voice` | string | มีอยู่เมื่อเซสชันวิดเจ็ตขอแทนที่เสียงที่ใช้ |
| `website_context` | string | มีอยู่เมื่อวิดเจ็ตส่งบริบทหน้าต่อเซสชัน |

<Note>
  วิดเจ็ตโหมดเว็บฮุกจะส่งคำขอนี้ไปยัง `webhook_url` ของคีย์เผยแพร่
  เองเมื่อมีการตั้งค่าไว้ โดยจะใช้ URL เว็บฮุกระดับองค์กรเป็นทางเลือกสำรอง
  ไม่ว่ากรณีใด คำขอจะลงนามด้วย `secret` ของเว็บฮุกองค์กร
</Note>

---

## สคีมาการตอบกลับ

ส่งคืนอ็อบเจ็กต์ JSON ที่อธิบายการกำหนดค่าเอเจนต์สำหรับการโทรนี้
ต้องระบุ `prompt` และ `voice`; ส่วนอื่นทั้งหมดเป็นตัวเลือก

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

| ฟิลด์ | ประเภท | จำเป็น | คำอธิบาย |
|-------|------|----------|-------------|
| `prompt` | string | ใช่ | System prompt ที่ควบคุมเอเจนต์ |
| `voice` | string | ใช่ | รหัสเสียงจาก [`GET /v1/voices`](/api-reference/agents#voices) เช่น `john` ยอมรับ `voice_name` เป็นชื่อแทน เสียงที่ไม่รู้จักจะไม่ผ่านการตรวจสอบและปฏิเสธการโทร |
| `product` | string | ไม่ | ค่าเริ่มต้นคือ `spark` อนุญาต: `spark`, `bolt`, `storm-base`, `storm-base-with-ack`, `storm-extra`, `storm-extra-with-ack` |
| `thinking_level` | string | ไม่ | `minimal`, `base` (ค่าเริ่มต้น) หรือ `extra` ถูกแทนที่สำหรับผลิตภัณฑ์ Storm: `storm-extra*` บังคับใช้ `extra` ส่วน `storm-*` อื่นบังคับใช้ `base` |
| `audio_context_mode` | string | ไม่ | `full` (ค่าเริ่มต้น) หรือ `reduced` |
| `watchdog_enabled` | boolean | ไม่ | เปิดใช้การกำกับดูแลสำหรับการโทรนี้ ค่าเริ่มต้นคือ `false` |
| `additional_audio_context` | boolean \| null | ไม่ | รวมเสียงของผู้โทรจากสองสามเทิร์นล่าสุด แทนที่จะรวมเฉพาะเทิร์นล่าสุด เพื่อปรับปรุงการแก้ไขและการเก็บข้อมูลที่มีการสะกด/ตัวเลขจำนวนมาก โดยมีค่าเวลาแฝง/ต้นทุนเพิ่มขึ้นเล็กน้อย ค่าเริ่มต้นเป็นเปิดสำหรับเซสชันขาเข้าและปิดสำหรับการโทรออก `null` จะคงค่าเริ่มต้นไว้ |
| `storm_feedback_mode` | string | ไม่ | `none`, `acknowledgement` (ค่าเริ่มต้น) หรือ `tick` |
| `language` | string | ไม่ | รูปแบบย่อของ `primary_language` |
| `primary_language` | string | ไม่ | รหัสภาษา ซึ่งผ่านการปรับรูปแบบแล้ว (ค่าเริ่มต้น `en`) รหัสที่ไม่สามารถระบุได้จะทำให้การโทรถูกปฏิเสธ |
| `has_additional_languages` | boolean | ไม่ | ค่าเริ่มต้นคือ `false` |
| `additional_languages` | array of string | ไม่ | ภาษาเพิ่มเติมที่เอเจนต์อาจสลับไปใช้ |
| `native_voice_switching` | boolean | ไม่ | ค่าเริ่มต้นคือ `false` เมื่อการโทรสลับไปยังภาษาอื่น ให้สลับเป็นเสียงเจ้าของภาษาสำหรับภาษานั้น (จับคู่ตามเพศ) แทนที่จะใช้เสียงที่กำหนดค่าไว้ต่อไป |
| `background_track` | string \| null | ไม่ | รหัสเสียงบรรยากาศหรือ `null` |
| `acknowledgement_prompt_mode` | string | ไม่ | `auto` (ค่าเริ่มต้น) หรือ `manual` (ผลิตภัณฑ์ Storm-with-ack) |
| `acknowledgement_prompt` | string | ไม่ | ใช้เมื่อ `acknowledgement_prompt_mode="manual"` |
| `silence_interval_seconds` | integer \| null | ไม่ | 5–120 วินาทีที่ผู้โทรเงียบก่อนมีการตรวจสอบ |
| `silence_max_checkins` | integer \| null | ไม่ | 1–10 |
| `silence_checkins_enabled` | boolean | ไม่ | ค่าเริ่มต้นคือ `true` |
| `connect_tone_enabled` | boolean | ไม่ | ค่าเริ่มต้นคือ `false` |
| `voicemail_action` | string | ไม่ | `prompt` (ค่าเริ่มต้น), `hangup` หรือ `message` |
| `voicemail_message` | string | ไม่ | ใช้เมื่อ `voicemail_action="message"` |
| `agent_name` | string | ไม่ | ชื่อที่แสดงซึ่งรายงานไปยังแดชบอร์ดและวิดเจ็ต |
| `org_name` | string | ไม่ | ชื่อองค์กรที่แสดงสำหรับบุคลิกของเอเจนต์ |
| `tools` | array | ไม่ | สคีมาเครื่องมือฟังก์ชันแบบอินไลน์ (ดู [เครื่องมือฟังก์ชัน](/th/tools/overview)) |
| `call_id` | integer | ไม่ | การส่งกลับรหัสการโทรของคำขอแบบเลือกได้ จะถูกละเว้น |

<Note>
  คีย์ระดับบนสุดที่ไม่รู้จักจะถูก **ละเว้น** โดยไม่มีการแจ้งเตือน — ชื่อฟิลด์ที่พิมพ์ผิด
  จะไม่ทำให้การกำหนดค่าถูกปฏิเสธ แต่จะไม่มีผล ลำดับการพูด
  และ `max_hold_seconds` ไม่ได้รับการยอมรับที่นี่ โดยกำหนดค่าได้
  เฉพาะใน [เอเจนต์](/api-reference/agents) เท่านั้น
</Note>

เนื่องจากต้องระบุ `prompt` และ `voice` การส่งคืน `{}` หรือการตอบกลับใดก็ตาม
ที่ไม่ผ่านการตรวจสอบจะทำให้การโทรถูกปฏิเสธด้วย `422` — ไม่มีการใช้เอเจนต์แบบคงที่เป็นทางเลือกในเส้นทางนี้ (หมายเลขหรือคีย์ในโหมด webhook
ไม่มีเอเจนต์ที่กำหนดไว้)

## ขีดจำกัดขนาดการตอบกลับ

<Warning>
  การตอบกลับการกำหนดค่าถูกจำกัดไว้ที่ **5 MiB** หากตัวจัดการ
  ส่งคืนการตอบกลับที่มีขนาดใหญ่กว่า แม้จะมีสถานะ `2xx`
  ThunderPhone จะรายงานว่าการตอบกลับเกินขีดจำกัด และ
  ปฏิเสธการโทรหรือเซสชันวิดเจ็ต เก็บเฉพาะฟิลด์ที่จำเป็นสำหรับ
  การตั้งค่าการโทรไว้ในการตอบกลับ โฮสต์ข้อมูลขนาดใหญ่ไว้หลังเครื่องมือฟังก์ชันหรือ
  บริการอื่นแทนการฝังไว้ในการกำหนดค่า
</Warning>

---

## ตัวอย่างตัวจัดการ

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

---

## การตอบกลับพร้อมเครื่องมือฟังก์ชัน

แนบเครื่องมือเพื่อให้ AI สามารถเรียก API ของคุณระหว่างการสนทนาได้:

```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>
  คำขอไปยังปลายทางเครื่องมือจะลงนามด้วย **ความลับ webhook ขององค์กรเดียวกัน**
  ที่ใช้ลงนามการแลกเปลี่ยนนี้ ดู
  [เครื่องมือฟังก์ชัน](/th/tools/overview) สำหรับโครงสร้างที่แน่นอนและรูปแบบ
  คำขอที่ลงนาม
</Tip>

---

## สรุประดับผลิตภัณฑ์

| ผลิตภัณฑ์ | เวลาแฝง | การให้เหตุผล | การตอบรับ |
|---------|---------|-----------|-----------------|
| `spark` | ต่ำสุด | พื้นฐาน | — |
| `bolt` | ต่ำ | ดีขึ้น | — |
| `storm-base` | ปานกลาง | แข็งแกร่ง | — |
| `storm-base-with-ack` | ปานกลาง | แข็งแกร่ง | ข้อความเติมอัตโนมัติระหว่างประมวลผล |
| `storm-extra` | สูงกว่า | เชิงลึก | — |
| `storm-extra-with-ack` | สูงกว่า | เชิงลึก | ข้อความเติมอัตโนมัติระหว่างประมวลผล |

---

## ที่เกี่ยวข้อง

<CardGroup cols={2}>
  <Card title="telephony.complete / web.complete" icon="phone-slash" href="/th/webhooks/call-complete">
    เหตุการณ์สิ้นสุดการโทรแบบไม่บล็อก
  </Card>
  <Card title="เครื่องมือฟังก์ชัน" icon="screwdriver-wrench" href="/th/tools/overview">
    JSON schema ฉบับเต็มสำหรับ `tools[]` และสัญญา endpoint ที่ลงนามแล้ว
  </Card>
  <Card title="Webhook endpoints" icon="bolt" href="/th/webhooks/endpoints">
    สมัครรับหลาย URL สำหรับ `telephony.incoming` / `web.incoming`
  </Card>
  <Card title="การกำหนดค่าการโทรแบบไดนามิก" icon="wand-magic-sparkles" href="/th/guides/dynamic-call-config">
    รูปแบบสำหรับพรอมป์ต์ เครื่องมือ และการทดสอบ A/B รายผู้โทร
  </Card>
</CardGroup>
