Open in
telephony.incoming / web.incoming
Webhook pemblokiran yang membentuk konfigurasi panggilan masuk secara real time.
Saat panggilan telepon masuk mencapai nomor tanpa agen
yang ditetapkan, atau sesi widget web dimulai pada kunci yang dapat dipublikasikan dalam
mode="webhook", ThunderPhone mengirimkan permintaan yang memblokir
telephony.incoming / web.incoming ke
URL webhook lama Anda
dan menunggu respons konfigurasi: hingga 13 detik untuk
panggilan telepon, termasuk satu percobaan ulang, dan hingga 10 detik untuk sesi
widget (lihat Batas waktu dan percobaan ulang). Gunakan
pertukaran ini untuk memilih Prompt, suara, dan alat secara dinamis per panggilan —
lihat panduan konfigurasi panggilan dinamis
untuk pola menyeluruh.
Batas waktu dan percobaan ulang
Jawablah dengan cepat: penelepon menunggu di saluran saat Anda memutuskan.
Untuk panggilan telepon (telephony.incoming):
- Upaya pertama dapat memakan waktu hingga 10 detik.
- Jika mengalami batas waktu habis, tidak dapat terhubung, atau menerima respons
5xx,408, atau429, ThunderPhone mencoba sekali lagi. Percobaan ulang mendapatkan sisa dari batas waktu 13 detik untuk seluruh pertukaran: sekitar 3 detik setelah upaya pertama yang mengalami batas waktu habis, hampir seluruh 13 detik setelah kegagalan cepat. Tidak ada percobaan ulang jika sisa waktunya kurang dari 2 detik. - Respons
4xxlainnya, respons yang melebihi batas ukuran, atau2xxyang isi responsnya bukan konfigurasi valid bersifat final dan tidak dicoba ulang. - Kedua upaya mengirim isi respons yang sama, header
X-ThunderPhone-Signatureyang sama, dan headerX-ThunderPhone-Idempotency-Keyyang sama (telephony-incoming-<call_id>), sehingga handler yang memiliki efek samping dapat mengenali percobaan ulang.
Tidak ada agen cadangan. Jika tidak ada konfigurasi yang dapat digunakan, panggilan
langsung diakhiri alih-alih membiarkan penelepon berada di saluran hening.
Panggilan tersebut muncul dalam riwayat panggilan sebagai panggilan gagal yang tidak ditagihkan dengan
end_reason inbound_config_webhook_failed, dan peristiwa
telephony.complete biasa dikirim.
Sesi widget (web.incoming) mendapatkan satu upaya hingga 10
detik. Jika gagal, permintaan sesi gagal dengan 502 atau 422.
Payload permintaan
Untuk panggilan telepon (telephony.incoming):
{
"type": "telephony.incoming",
"data": {
"call_id": 987654321,
"from_number": "+14155550199",
"to_number": "+15551234567"
}
}| Kolom | Tipe | Deskripsi |
|---|---|---|
call_id | integer | ID panggilan — tetap sama di seluruh peristiwa untuk panggilan ini |
from_number | string | Nomor penelepon E.164 |
to_number | string | Tujuan E.164 (salah satu nomor ThunderPhone Anda) |
Untuk sesi widget web (web.incoming), data mengidentifikasi
halaman yang menyematkan widget, bukan nomor telepon:
{
"type": "web.incoming",
"data": {
"call_id": 987654322,
"origin_domain": "https://example.com",
"publishable_key_prefix": "pk_live_a1b2"
}
}| Bidang | Tipe | Deskripsi |
|---|---|---|
call_id | integer | ID panggilan |
origin_domain | string | Asal halaman yang menghosting widget, sebagaimana dikirim oleh peramban. Klien non-peramban dapat mengirim nilai apa pun, jadi jangan anggap ini sebagai bukti asal permintaan |
publishable_key_prefix | string | Karakter pertama dari kunci yang dapat dipublikasikan yang membuka sesi |
language, primary_language | string | Ada saat sesi widget meminta override bahasa |
voice | string | Ada saat sesi widget meminta override suara |
website_context | string | Ada saat widget meneruskan konteks halaman per sesi |
Bidang override (language, voice, website_context, dan variables
peramban) hanya tiba saat kunci yang dapat dipublikasikan mengizinkannya di
override peramban. Permintaan
sesi yang menyertakan override yang tidak diizinkan oleh kunci tersebut ditolak
sebelum webhook ini dipanggil.
Skema respons
Kembalikan objek JSON yang menjelaskan konfigurasi agen untuk panggilan ini.
prompt dan voice wajib diisi; semua yang lain bersifat opsional.
{
"prompt": "You are a helpful booking assistant for Acme Restaurant.",
"voice": "john",
"product": "spark",
"background_track": null,
"tools": []
}| Bidang | Jenis | Wajib | Deskripsi |
|---|---|---|---|
prompt | string | ya | Prompt sistem yang mengarahkan agen |
voice | string | ya | ID suara dari GET /v1/voices, misalnya john. voice_name diterima sebagai alias. Suara yang tidak dikenal gagal validasi dan panggilan ditolak |
product | string | tidak | Defaultnya spark. Diizinkan: spark, bolt, storm-base, storm-base-with-ack, storm-extra, storm-extra-with-ack |
thinking_level | string | tidak | minimal, base (default), atau extra. Ditimpa untuk produk Storm: storm-extra* memaksa extra, storm-* lainnya memaksa base |
audio_context_mode | string | tidak | full (default) atau reduced |
watchdog_enabled | boolean | tidak | Aktifkan pengawasan untuk panggilan ini. Default false |
additional_audio_context | boolean | null | tidak | Sertakan beberapa giliran terakhir audio penelepon, bukan hanya giliran terbaru, untuk meningkatkan koreksi serta pengumpulan data yang banyak berisi ejaan/angka dengan sedikit tambahan latensi/biaya. Default aktif untuk sesi masuk dan nonaktif untuk panggilan telepon keluar; null mempertahankan default |
storm_feedback_mode | string | tidak | none, acknowledgement (default), atau tick. Juga menentukan apa yang didengar penelepon saat model audio memproses giliran ketika audio_tier adalah hearing |
language | string | tidak | Singkatan untuk primary_language |
primary_language | string | tidak | Kode bahasa, dinormalisasi (default en). Kode yang tidak dapat diuraikan akan menolak panggilan |
has_additional_languages | boolean | tidak | Default false |
additional_languages | array of string | tidak | Bahasa tambahan yang dapat digunakan agen |
native_voice_switching | boolean | tidak | Default false. Saat panggilan beralih ke bahasa lain, ganti ke suara native untuk bahasa tersebut (disesuaikan berdasarkan gender), alih-alih mempertahankan suara yang dikonfigurasi |
language_switching | string | null | tidak | ask_first (default) atau follow_caller; null sama dengan ask_first. Menentukan cara agen multibahasa menangani lawan bicara yang menggunakan bahasa lain yang didukungnya. ask_first langsung beralih ketika mereka meminta bahasa tersebut atau mengucapkan kalimat lengkap dalam bahasa itu, dan menanyakan apakah akan melanjutkan dalam bahasa tersebut setelah ucapan singkat, seperti "ya". follow_caller beralih ke bahasa terkonfigurasi apa pun yang mereka gunakan, bahkan hanya satu kata. Hanya berlaku ketika additional_languages ditetapkan |
audio_tier | string | null | tidak | Hanya product: "bolt". standard (default; "" dan null berarti sama) menjalankan tiga transkriptor (panggilan yang memproses PHI hanya menggunakan transkriptor yang tercakup BAA). lean menurunkan ke satu transkriptor, yang lebih sering salah dengar. hearing meningkatkan ke standard ditambah ansambel kuat model audio yang mendengarkan audio penelepon pada beberapa giliran, yang membutuhkan waktu sekitar 1–2 detik lebih lama. Produk lain mengabaikannya |
endpointing | string | null | tidak | faster (200 md), fast (300 md), normal (500 md, default), atau patient (800 md); null sama dengan normal. Menentukan berapa lama agen Bolt v2 menunggu setelah penelepon berhenti sebelum meminta model memberikan respons. Waktu tunggu yang lebih singkat dapat menjawab lebih cepat, tetapi mengirim lebih banyak permintaan yang menjadi usang oleh kata-kata penelepon berikutnya, sehingga meningkatkan biaya model. Produk lain mengabaikannya |
eagerness | string | null | tidak | Tidak digunakan lagi: nama endpointing hingga 19 Oktober 2026, tetapi masih dibaca. Diabaikan saat endpointing ditetapkan |
intelligence | string | null | tidak | express, lite, atau standard (default); null sama dengan standard. Tingkat kecerdasan agen bolt pada pipeline Bolt v2: menentukan model yang menulis responsnya. express memerlukan sekitar 600 md hingga kata pertama, lebih murah daripada Standard dan paling tidak andal dalam mengikuti instruksi; lite memerlukan sekitar 900 md, paling murah dan kurang andal; standard memerlukan sekitar 900 md dan terbaik dalam mengikuti instruksi. Respons Express dan Lite yang hanya akan memanggil tool diserahkan ke model Standard. Produk lain mengabaikannya |
background_track | string | null | tidak | ID audio ambient atau null |
acknowledgement_prompt_mode | string | tidak | auto (default) atau manual (produk Storm-with-ack) |
acknowledgement_prompt | string | tidak | Digunakan saat acknowledgement_prompt_mode="manual" |
silence_interval_seconds | integer | null | tidak | 5–120. Detik keheningan penelepon sebelum check-in |
silence_max_checkins | integer | null | tidak | 1–10 |
silence_checkins_enabled | boolean | tidak | Default true |
silence_final_action | string | tidak | Setelah check-in terakhir: hang_up (default) atau transfer ke silence_transfer_destination |
silence_transfer_destination | string | tidak | Nama entri dalam transfer_destinations konfigurasi ini; wajib dengan transfer. Tanpa transfer_destinations, transfer tidak dapat dilakukan dan agen menutup panggilan |
silence_final_message | string | tidak | ≤ 300 karakter, dibaca sebagaimana ditulis; "" (default) menggunakan baris default |
connect_tone_enabled | boolean | tidak | Default false |
pronunciation_dicts | boolean | null | tidak | Default false; null sama dengan false. Saat true, suara Cartesia panggilan menggunakan kamus pelafalan ThunderPhone untuk bahasa panggilan, jika tersedia. Berfungsi untuk panggilan telepon dan widget. Hanya berlaku untuk suara Cartesia milik ThunderPhone sendiri, bukan suara pada kunci API Cartesia Anda sendiri. Tidak digunakan dengan agent_id, karena pengaturan agen yang tersimpan berlaku |
voicemail_action | string | tidak | prompt (default), hangup, atau message |
voicemail_message | string | tidak | Digunakan saat voicemail_action="message" |
agent_name | string | tidak | Nama tampilan yang dilaporkan ke dasbor dan widget |
org_name | string | tidak | Nama tampilan organisasi untuk persona agen |
tools | array | tidak | Skema function-tool inline (lihat Alat Fungsi) |
transfer_destinations | array of object | null | tidak | Hingga 50 entri {name, number} yang dapat dialihkan oleh panggilan ini, divalidasi seperti nomor transfer agen. Agen mengalihkan berdasarkan nama dan hanya ke entri tersebut; [] berarti tidak ada transfer. Hilangkan atau kirim null agar agen dapat mengalihkan ke nomor yang dipilihnya |
timezone | string | tidak | Zona waktu IANA untuk panggilan ini, seperti America/Chicago. Agen menyatakan tanggal dan waktu dalam zona waktu tersebut, bukan zona waktunya sendiri. Juga diizinkan di samping agent_id. Nilai yang bukan zona IANA diabaikan. Lihat Atur zona waktu per panggilan |
call_id | integer | tidak | Gema opsional ID panggilan permintaan; diabaikan |
Karena prompt dan voice wajib, mengembalikan {} atau respons apa pun
yang gagal validasi akan menolak panggilan: panggilan telepon diakhiri
seperti dijelaskan dalam Batas waktu dan percobaan ulang, dan permintaan sesi
widget gagal dengan 422. Tidak ada fallback agen statis pada jalur ini
(nomor atau kunci dalam mode webhook tidak memiliki agen yang ditetapkan).
Batas ukuran respons
Contoh handler
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 {}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({});
},
);Respons dengan function tool
Tambahkan tool agar AI dapat memanggil API Anda di tengah percakapan:
{
"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"
}
}
}
]
}Lembar ringkas tingkatan produk
| Produk | Latensi | Penalaran | Konfirmasi |
|---|---|---|---|
spark | Terendah | Dasar | — |
bolt | Rendah | Ditingkatkan | — |
storm-base | Sedang | Kuat | — |
storm-base-with-ack | Sedang | Kuat | Pengisi otomatis saat berpikir |
storm-extra | Lebih tinggi | Mendalam | — |
storm-extra-with-ack | Lebih tinggi | Mendalam | Pengisi otomatis saat berpikir |
Terkait
Peristiwa akhir panggilan yang tidak memblokir.
Skema JSON lengkap untuk tools[] dan kontrak endpoint yang ditandatangani.
Berlangganan beberapa URL ke telephony.incoming / web.incoming.
Pola untuk Prompt, alat, dan pengujian A/B per penelepon.