---
title: "Katalog Peristiwa"
description: "Semua jenis peristiwa webhook yang dipancarkan ThunderPhone."
---

Setiap badan webhook memiliki kolom `type` yang nilainya merupakan salah satu jenis peristiwa
di halaman ini. Saat Anda berlangganan ke sebuah
[endpoint](/id/webhooks/endpoints), array `events` harus memuat
jenis peristiwa yang Anda inginkan (atau kosong untuk berlangganan ke semuanya —
kecuali peristiwa per-giliran [`telephony.turn`](#telephony-turn) /
[`web.turn`](#web-turn), yang dikirimkan hanya ke endpoint yang
menyebutkannya secara eksplisit).

Dua gaya pengiriman membawa peristiwa ini:

* Pengiriman **endpoint** selalu merupakan notifikasi **non-blocking** dengan [percobaan ulang](/id/webhooks/overview): respons dengan 2xx apa pun; envelope memuat `event_id` untuk deduplikasi.
* Pertukaran **blocking** hanya berjalan pada [webhook lama dengan satu URL](/id/webhooks/overview): permintaan konfigurasi [`telephony.incoming` / `web.incoming`](/id/webhooks/call-incoming) (nomor mode webhook: 13 d termasuk satu percobaan ulang; kunci widget mode webhook: 10 d) dan [dispatch tool](/id/tools/overview) mode webhook. Respons Anda membentuk panggilan langsung.

Contoh payload di bawah menunjukkan envelope endpoint dalam urutan transmisi
(kunci diurutkan secara alfabetis: `data`, `event_id`, `type`); pengiriman lama
memuat `data` yang sama tanpa `event_id`.

## Peristiwa panggilan

### `telephony.incoming`

Dikirim saat panggilan masuk mencapai salah satu
[nomor telepon](/api-reference/phone-numbers) Anda. Pengiriman endpoint merupakan
notifikasi fire-and-forget yang dikirim untuk **setiap** panggilan masuk, baik
nomor tersebut dikonfigurasi untuk agen maupun untuk webhook. Nomor tanpa
agen yang ditetapkan juga menerima permintaan konfigurasi **pemblokiran**
pada webhook lama — lihat
[`telephony.incoming` / `web.incoming`](/id/webhooks/call-incoming) untuk
skema permintaan / respons lengkap.

```json
{
  "data": {
    "call_id": 987654321,
    "from_number": "+14155550199",
    "to_number": "+15551234567"
  },
  "event_id": "3f6b2ad0-1c9e-4a57-9f2b-8f6f0f9d2f11",
  "type": "telephony.incoming"
}
```

| Bidang | Tipe | Deskripsi |
|-------|------|-------------|
| `agent_id` | integer \| null | Agen yang menangani panggilan, jika ada yang ditetapkan |
| `agent_name` | string \| null | Agen yang menangani panggilan, jika ada yang ditetapkan |

### `telephony.complete`

Dikirim saat panggilan telepon masuk atau keluar berakhir. Tidak memblokir.
Mencakup transkrip, URL rekaman jika tersedia, dan ringkasan penagihan. Lihat
[`telephony.complete` / `web.complete`](/id/webhooks/call-complete) untuk
skema payload.

| Bidang | Tipe | Deskripsi |
|-------|------|-------------|
| `agent_id` | integer \| null | Agen yang menangani panggilan, jika ada yang ditetapkan |
| `agent_name` | string \| null | Agen yang menangani panggilan, jika ada yang ditetapkan |

### `telephony.tool`

Dikirim setelah panggilan telepon memanggil sebuah
[function tool](/id/tools/overview). Notifikasi audit yang tidak memblokir —
tool sudah dieksekusi saat peristiwa ini dikirim; mencakup
function tool Anda sendiri dan tool bawaan `send_sms` (bukan tool bawaan lain,
basis pengetahuan, koneksi aplikasi, atau MCP). Untuk `send_sms`,
baik `arguments.to` maupun `arguments.body` adalah `[REDACTED]`. Transkrip
penyelesaian juga menyamarkan keduanya; ambil konten dan penerima dari
resource SMS dengan cakupan organisasi menggunakan `message_id` pada respons.

```json
{
  "data": {
    "arguments": { "date": "2026-04-21" },
    "call_id": 987654321,
    "from_number": "+14155550199",
    "response": {
      "response": { "available_slots": ["9:00 AM", "2:00 PM"] },
      "status": 200
    },
    "to_number": "+15551234567",
    "tool_name": "search_appointments"
  },
  "event_id": "1f0a7c3e-52d4-4a0e-8f4b-b1a6a1c0d9e2",
  "type": "telephony.tool"
}
```

`response` adalah hasil yang dieksekusi: `{"status": <http status>,
"response": <your endpoint's JSON>}` saat berhasil, atau
`{"status": <status>, "error": "<message>"}` saat gagal.

| Bidang | Tipe | Deskripsi |
|-------|------|-------------|
| `agent_id` | integer \| null | Agen yang menangani panggilan, jika ada yang ditetapkan |
| `agent_name` | string \| null | Agen yang menangani panggilan, jika ada yang ditetapkan |

### `telephony.turn`

Dikirim selama panggilan telepon **sedang berlangsung**, satu kali untuk setiap
giliran yang berisi ucapan saat terjadi — penyelesaian lisan agen dan
giliran penelepon yang ditranskripsikan. Memungkinkan Anda mengikuti percakapan
langsung melalui webhook biasa alih-alih melakukan polling pada
[`GET /v1/calls/{call_id}/transcript`](/api-reference/calls#get-transcript).
Tidak memblokir.

<Note>
  **Langganan eksplisit diperlukan.** Tidak seperti setiap jenis peristiwa lainnya,
  peristiwa giliran tidak pernah dikirim melalui array `events` kosong
  (berlangganan ke semua). Endpoint menerimanya hanya jika daftar `events`
  secara eksplisit memuat `telephony.turn` / `web.turn`.
</Note>

```json
{
  "data": {
    "call_id": 987654321,
    "entry_type": "completion",
    "from_number": "+14155550199",
    "position": 7,
    "role": "assistant",
    "start_ms": 15200,
    "text": "How many employees does your company have?",
    "to_number": "+15551234567"
  },
  "event_id": "8d3f5a2c-7b1e-4c9a-b6d0-2e4f6a8c0d1e",
  "type": "telephony.turn"
}
```

| Bidang | Tipe | Deskripsi |
|-------|------|-------------|
| `agent_id` | integer \| null | Agen yang menangani panggilan, jika ada yang ditetapkan |
| `agent_name` | string \| null | Agen yang menangani panggilan, jika ada yang ditetapkan |
| `position` | integer | Indeks giliran dalam riwayat panggilan — identitas stabil untuk pengurutan |
| `role` | string | `assistant` (ucapan agen) atau `user` (ucapan penelepon) |
| `text` | string | Teks transkrip giliran sebagaimana diketahui saat pengiriman |
| `entry_type` | string | Jenis entri riwayat yang mendasarinya: `completion` (agen), atau `user_turn` / `span` (penelepon) |
| `start_ms`, `end_ms` | integer | Offset audio dalam ms sejak panggilan dimulai; hanya ada ketika waktu pemutaran sudah diketahui saat pengiriman |

<Note>
  Pengiriman setidaknya sekali dan giliran dapat tiba **tidak berurutan**:
  lakukan deduplikasi berdasarkan `event_id` pada envelope dan urutkan berdasarkan `position`. Transkrip
  penelepon masih dapat disempurnakan setelah pengiriman; transkrip
  [`telephony.complete`](/id/webhooks/call-complete) setelah panggilan
  bersifat otoritatif. Hanya giliran yang diucapkan yang dikirim — panggilan tool, bisikan, dan
  entri internal lainnya tidak pernah muncul dalam stream ini.
</Note>

### `web.incoming`

Setara kanal web dari `telephony.incoming`, dikirim saat sesi
[widget web](/id/widget/overview) atau panggilan uji mikrofon builder
dimulai. Pengiriman endpoint bersifat fire-and-forget untuk setiap sesi web.
Publishable key dalam `mode="webhook"` juga menerima
permintaan konfigurasi **pemblokiran** pada webhook lama — permintaan
pemblokiran tersebut memiliki bentuk berbeda (`origin_domain`,
`publishable_key_prefix`; tanpa nomor telepon). Lihat
[`telephony.incoming` / `web.incoming`](/id/webhooks/call-incoming).

```json
{
  "data": {
    "call_id": 987654322,
    "from_number": "web",
    "origin_domain": "https://example.com",
    "publishable_key_prefix": "pk_live_a1b2",
    "to_number": "+15551234567"
  },
  "event_id": "9a2b4c6d-8e0f-4a1b-9c2d-3e4f5a6b7c8d",
  "type": "web.incoming"
}
```

`from_number` selalu merupakan literal `"web"`. Untuk sesi widget
mode webhook, `to_number` kosong (nomor agen sesi ditetapkan
setelah konfigurasi); untuk panggilan uji mikrofon builder, `origin_domain` dan
`publishable_key_prefix` kosong.

| Bidang | Tipe | Deskripsi |
|-------|------|-------------|
| `agent_id` | integer \| null | Agen yang menangani panggilan, jika ada yang ditetapkan |
| `agent_name` | string \| null | Agen yang menangani panggilan, jika ada yang ditetapkan |

### `web.complete`

Setara kanal web dari `telephony.complete`, mencakup panggilan widget
web (`direction: "web"`) dan panggilan uji mikrofon builder
(`direction: "test"`). Tidak memblokir. Bentuk payload sama dengan
[`telephony.complete`](/id/webhooks/call-complete), ditambah `origin_domain`,
dengan `from_number` diatur ke `"web"`.

| Bidang | Tipe | Deskripsi |
|-------|------|-------------|
| `agent_id` | integer \| null | Agen yang menangani panggilan, jika ada yang ditetapkan |
| `agent_name` | string \| null | Agen yang menangani panggilan, jika ada yang ditetapkan |

<Note>
  Pada webhook lama dengan satu URL, panggilan uji mikrofon builder
  secara historis dilaporkan sebagai `telephony.complete` — hanya panggilan `direction:
  "web"` yang menggunakan tipe `web.complete` di sana. Sistem endpoint
  memetakan panggilan web dan uji ke `web.*`. Payload historis dapat
  memuat nilai `direction` lama `widget` atau `mic`.
</Note>

### `web.tool`

Setara kanal web dari `telephony.tool`. `data` memuat
`origin_domain` alih-alih `from_number` / `to_number`.

| Bidang | Tipe | Deskripsi |
|-------|------|-------------|
| `agent_id` | integer \| null | Agen yang menangani panggilan, jika ada yang ditetapkan |
| `agent_name` | string \| null | Agen yang menangani panggilan, jika ada yang ditetapkan |

### `web.turn`

Setara kanal web dari [`telephony.turn`](#telephony-turn),
mencakup panggilan widget web dan panggilan uji mikrofon builder. Bentuk payload
sama, dengan `origin_domain` alih-alih `from_number` / `to_number`.
Seperti `telephony.turn`, ini memerlukan **langganan eksplisit** —
tidak pernah dikirim melalui array `events` kosong.

| Bidang | Tipe | Deskripsi |
|-------|------|-------------|
| `agent_id` | integer \| null | Agen yang menangani panggilan, jika ada yang ditetapkan |
| `agent_name` | string \| null | Agen yang menangani panggilan, jika ada yang ditetapkan |

### `call.transfer_completed`

Dikirim sekali per panggilan dengan hasil akhir dari
[transfer panggilan](/id/guides/call-transfers): transfer dingin, atau jembatan
manusia yang tersisa dari transfer hangat setelah agen keluar.
Tidak memblokir, dan hanya dikirim ke endpoint (tidak pernah ke webhook
URL tunggal lama). Peristiwa ini tiba setelah `telephony.complete` panggilan:
setelah operator mengambil alih panggilan atau transfer gagal, atau, ketika
ThunderPhone menjembatani panggilan, setelah jembatan berakhir.

```json
{
  "data": {
    "agent_id": 12,
    "agent_name": "Front desk",
    "bridge_kind": "cold_fallback",
    "bridge_seconds": 255,
    "bridge_total_cents": 5,
    "call_id": 987654321,
    "destination": "+15555550102",
    "sip_status": 403,
    "transfer_outcome": "bridged"
  },
  "event_id": "5b0f6c1e-9d2a-5e7b-8c4d-1a2b3c4d5e6f",
  "type": "call.transfer_completed"
}
```

| Bidang | Jenis | Deskripsi |
|-------|------|-------------|
| `call_id` | integer | Panggilan yang ditransfer |
| `agent_id` | integer \| null | Agen yang menangani panggilan, jika ada yang ditugaskan |
| `agent_name` | string \| null | Agen yang menangani panggilan, jika ada yang ditugaskan |
| `transfer_outcome` | string | `refer_accepted`: operator mengambil alih panggilan. `bridged`: ThunderPhone sendiri menjaga penelepon dan tujuan tetap terhubung, baik karena operator menolak transfer dingin maupun karena agen pada transfer hangat keluar. `bridge_unanswered`: operator menolak transfer dingin dan tujuan tidak menjawab panggilan ThunderPhone sendiri. `failed`: transfer tidak berhasil dan panggilan berakhir |
| `destination` | string \| null | Nomor tujuan transfer panggilan |
| `sip_status` | integer \| null | Status SIP operator ketika menolak transfer dingin: `403` (misalnya trunk Twilio tanpa transfer panggilan), `405`, atau `501`. `null` jika operator tidak menolaknya |
| `bridge_kind` | string \| null | Hanya `bridged`: `cold_fallback` (transfer dingin yang ditolak lalu dihubungkan oleh ThunderPhone) atau `warm` (dua orang dalam transfer hangat setelah agen keluar) |
| `bridge_seconds` | integer \| null | Hanya `bridged`: durasi jembatan yang ditagihkan ThunderPhone, dalam detik |
| `bridge_total_cents` | integer \| null | Hanya `bridged`: biaya jembatan dalam sen USD setelah kredit panggilan gratis, yang ditagihkan terpisah dari `billing_total_cents` panggilan |

Nilai yang sama tersedia pada [objek panggilan](/api-reference/calls#call-object)
sebagai `transfer_outcome`, `transfer_destination`, `transfer_sip_status`,
`transfer_bridge_kind`, `transfer_bridge_seconds`, dan
`transfer_bridge_total_cents`. `event_id` sama pada setiap pengiriman
untuk suatu panggilan, sehingga laporan berulang tidak pernah menghasilkan peristiwa kedua.

---

## Peristiwa SMS

Peristiwa SMS menjelaskan pesan yang dikirim melalui akun Twilio atau Telnyx
yang terhubung dengan organisasi. Isi pesan dihilangkan dan `to` disamarkan.
Ambil pesan dalam cakupan organisasi dari
[`GET /v1/sms/{message_id}`](/api-reference/sms#retrieve-a-message) saat isi
atau penerimanya diperlukan.

### `sms.sent`

Dikirim saat operator pelanggan menerima permintaan SMS keluar. Ini bukan
konfirmasi pengiriman ke handset.

```json
{
  "data": {
    "accepted_at": "2026-09-19T15:42:10.113Z",
    "agent_id": 12,
    "call_id": 987654321,
    "created_at": "2026-09-19T15:42:09.901Z",
    "direction": "outbound",
    "error_code": null,
    "error_message": null,
    "from_number": "+15551234567",
    "id": "313a3117-827b-46e9-a01a-ac1493838724",
    "provider": "twilio",
    "provider_id": "SM0123456789abcdef0123456789abcdef",
    "segment_count": 1,
    "source": "agent_tool",
    "status": "accepted",
    "to": "[REDACTED]"
  },
  "event_id": "0ae347b4-1716-45bb-9a0e-b52373586e37",
  "type": "sms.sent"
}
```

### `sms.failed`

Dikirim setelah penolakan permanen oleh operator, setelah percobaan ulang
sementara habis, atau saat hasilnya tidak diketahui secara final. `error_code`
adalah kode ThunderPhone yang stabil dan `error_message` menyatakan tindakan
pelanggan saat tersedia. `status: "unknown"` berarti operator mungkin telah
menerima SMS; rekonsiliasikan di log operator karena ThunderPhone tidak akan
mengirimkannya ulang secara otomatis.

### `sms.received`

Dicadangkan untuk dukungan SMS masuk. Tidak ada peristiwa `sms.received` yang
dikirim pada rilis saat ini. Konfigurasi webhook operator masuk dan verifikasi
tanda tangan akan dirilis secara terpisah.

---

## Peristiwa suara

Pembuatan suara kustom bersifat asinkron. Peristiwa nonpemblokiran ini
memungkinkan Anda merespons hasil akhir alih-alih melakukan polling pada
[endpoint detail klon](/api-reference/custom-voices#list-and-retrieve-clones).

`voice.ready` dan `voice.failed` hanya dikirim ke endpoint di seluruh
organisasi dengan `events: []`. Keduanya tidak dapat dipilih sebagai filter
peristiwa eksplisit.

### `voice.ready`

Dikirim saat suara kustom selesai diproses dan dapat ditetapkan ke agen.

```json
{
  "data": {
    "voice": {
      "created_at": "2026-07-30T14:12:08.317Z",
      "display_name": "Support voice",
      "failure_reason": "",
      "gender": "female",
      "id": "cv_2f6f90b0e9a34ee8b39be7d1",
      "language": "en",
      "name": "custom:cv_2f6f90b0e9a34ee8b39be7d1",
      "status": "ready",
      "updated_at": "2026-07-30T14:13:31.605Z"
    }
  },
  "event_id": "2d5f0a61-e9b5-4a3c-b684-29d7d9e4b214",
  "type": "voice.ready"
}
```

### `voice.failed`

Dikirim saat pemrosesan suara kustom mengalami kegagalan permanen.

```json
{
  "data": {
    "reason": "audio sample could not be processed",
    "voice": {
      "created_at": "2026-07-30T14:12:08.317Z",
      "display_name": "Support voice",
      "failure_reason": "audio sample could not be processed",
      "gender": "female",
      "id": "cv_2f6f90b0e9a34ee8b39be7d1",
      "language": "en",
      "name": "custom:cv_2f6f90b0e9a34ee8b39be7d1",
      "status": "failed",
      "updated_at": "2026-07-30T14:13:31.605Z"
    }
  },
  "event_id": "3493e985-1a75-4f77-a10a-e74af440cd31",
  "type": "voice.failed"
}
```

| Bidang | Tipe | Deskripsi |
|-------|------|-------------|
| `voice.id` | string | ID publik suara kustom |
| `voice.name` | string | Nilai suara agen dalam bentuk `custom:<public_id>` |
| `voice.display_name` | string | Nama suara yang terlihat oleh organisasi |
| `voice.language` | string | Kode bahasa tunggal klon |
| `voice.gender` | string | `male`, `female`, atau string kosong |
| `voice.status` | string | `ready` untuk `voice.ready`; `failed` untuk `voice.failed` |
| `voice.failure_reason` | string | Kosong saat berhasil; detail kegagalan pemrosesan saat gagal |
| `voice.created_at`, `voice.updated_at` | timestamp | Stempel waktu ISO 8601 |
| `reason` | string | Detail kegagalan; hanya ada pada `voice.failed` |

---

## Peristiwa kualitas

### `call.graded`

Dikirim setiap kali [proses penilaian AI](/api-reference/calls#ai-call-grading)
selesai untuk sebuah panggilan. Tidak memblokir.

```json
{
  "data": {
    "call_id": 987654321,
    "grade": {
      "call_outcome": "success",
      "created_at": "2026-04-20T18:25:11.002Z",
      "detected_issues": [],
      "graded_at": "2026-04-20T18:25:11.002Z",
      "grader_model": "heuristic-v1",
      "id": 5512,
      "score": 92,
      "status": "completed",
      "summary": "Caller asked about their policy and got a full answer…"
    },
    "report": null
  },
  "event_id": "7c1d2e3f-4a5b-4c6d-8e9f-0a1b2c3d4e5f",
  "type": "call.graded"
}
```

| Bidang | Tipe | Deskripsi |
|-------|------|-------------|
| `agent_id` | integer \| null | Agen yang menangani panggilan, jika ada yang ditetapkan |
| `agent_name` | string \| null | Agen yang menangani panggilan, jika ada yang ditetapkan |
| `grade.id` | integer | ID nilai |
| `grade.score` | integer \| null | 0–100 |
| `grade.call_outcome` | string | `success`, `failure`, `unknown`, atau `no_conversation` |
| `grade.summary` | string | Ringkasan satu paragraf |
| `grade.detected_issues` | array | String masalah yang ditemukan oleh penilai |
| `grade.status` | string | Selalu `completed` — hanya proses yang selesai yang mengirimkan event |
| `grade.grader_model` | string | Penilai yang menghasilkan hasil (misalnya `heuristic-v1`) |
| `grade.graded_at`, `grade.created_at` | timestamp | |
| `report` | object \| null | [Laporan panggilan](/api-reference/calls#call-report) yang ditujukan untuk pelanggan: label hasil, penanda dan alasan perlu perhatian, penelepon, ringkasan satu baris, pengalaman penelepon. Pada nilai heuristik awal, nilainya adalah `null` atau laporan `pending` dengan bidang placeholder; event nilai lengkap membawa laporan yang telah selesai |

<Note>
  Sebuah panggilan dapat dinilai lebih dari sekali — penilaian heuristik cepat
  sering diikuti oleh penilaian model penuh setelah rekaman tersedia, dan
  penilaian ulang manual dimungkinkan. Setiap proses yang selesai mengirim
  peristiwa `call.graded` sendiri; perlakukan `graded_at` terbaru sebagai
  acuan.
</Note>

### `call.data_extracted`

Dikirim setiap kali ekstraksi data terstruktur berhasil diselesaikan, termasuk
percobaan ulang terlambat setelah `telephony.complete` / `web.complete` atau
proses ulang manual melalui
[`POST /v1/calls/{call_id}/extract`](/api-reference/calls#run-data-extraction).
Tidak memblokir.

Dalam mode ekstraksi pemblokiran, peristiwa penyelesaian biasanya menunggu tidak
lebih dari anggaran ekstraksi 75 detik. Jika proses pekerja ekstraksi hilang,
pengurasan finalisasi produksi (setiap lima menit) merilis penyelesaian yang
`blocking_deadline_at`-nya telah lewat sebelum memulai upaya ekstraksi lainnya.
Keberhasilan berikutnya dikirimkan secara terpisah sebagai peristiwa ini.

<Warning>
  Nilai yang diekstrak adalah interpretasi model terhadap percakapan. Setiap
  nilai non-null mencakup kutipan, peran pembicara, dan indeks giliran yang
  menjadi dasarnya, serta flag verifikasi tingkat proses. Verifikasi hasilnya
  sebelum membuat keputusan yang melibatkan uang, identitas, atau akses.
</Warning>

```json
{
  "data": {
    "call_id": 987654321,
    "agent_id": 12,
    "agent_name": "Acme intake",
    "extracted_data": {
      "status": "completed",
      "fields": {
        "customer_name": "Alex Morgan",
        "appointment_date": "2026-04-23"
      },
      "evidence": {
        "customer_name": {
          "quote": "My name is Alex Morgan",
          "speaker_role": "caller",
          "turn_index": 4
        },
        "appointment_date": {
          "quote": "April 23 works for me",
          "speaker_role": "caller",
          "turn_index": 7
        }
      },
      "verification": "verified",
      "field_reasons": {},
      "schema_version": "92850758e231a3c95a..."
    },
    "extracted_at": "2026-04-20T18:25:11.002Z",
    "model": "<model id>",
    "summary": "Alex Morgan called to book a follow-up visit and confirmed Thursday, April 23."
  },
  "event_id": "4d79ef1d-c2b1-4ed6-85b8-8326bd2895ef",
  "type": "call.data_extracted"
}
```

| Bidang | Tipe | Deskripsi |
|-------|------|-------------|
| `agent_id` | integer \| null | Agen yang menangani panggilan, jika ditetapkan |
| `agent_name` | string \| null | Agen yang menangani panggilan, jika ditetapkan |
| `summary` | string \| null | [Ringkasan panggilan](/id/webhooks/call-complete) yang selesai ketika agen mengaktifkan **Buat ringkasan panggilan**, sehingga peristiwa ekstraksi terlambat juga mengirimkan ringkasan yang melewati jendela penyelesaian; selain itu `null`. Agen dengan ringkasan tetapi tanpa bidang ekstraksi tidak pernah mengirim peristiwa ini: baca ringkasan terlambat dari `GET /v1/calls/{call_id}` |
| `extracted_data.status` | string | Selalu `completed` untuk peristiwa ini |
| `extracted_data.fields` | object | Nilai yang dikunci oleh kunci bidang ekstraksi yang dikonfigurasi; nilai yang tidak tersedia adalah `null` |
| `extracted_data.evidence` | object | Bukti yang dikunci oleh bidang ekstraksi. Nilai non-null memiliki kutipan persis yang diperiksa secara struktural (maksimal 1.000 karakter), `speaker_role` (`caller` atau `agent`), dan `turn_index`; kutipan yang dikembalikan model dengan panjang lebih besar ditolak, bukan dipotong, dan bukti adalah `null` setiap kali bidangnya `null` |
| `extracted_data.verification` | string | `verified` hanya ketika proses bukti independen mengembalikan tepat satu putusan valid untuk setiap bidang kandidat. `unavailable` berarti proses gagal, habis waktu, tidak memiliki anggaran yang cukup, atau mengembalikan output yang salah format atau parsial. Proses yang sepenuhnya tidak tersedia mempertahankan nilai yang didasarkan secara struktural untuk ditinjau pelanggan. Untuk output parsial, putusan valid diterapkan dan setiap kandidat tanpa tepat satu putusan valid dinullkan |
| `extracted_data.field_reasons` | object | Alasan yang dikunci oleh bidang yang dinullkan oleh landasan struktural atau verifier independen |
| `extracted_data.schema_version` | string | Hash skema bidang persis yang digunakan untuk ekstraksi ini |
| `extracted_at` | timestamp | Waktu penyelesaian ISO 8601 |
| `model` | string | Model yang digunakan untuk ekstraksi |

### `campaign.completed`

Dikirim sekali ketika kampanye berpindah dari `running` ke `completed`, baik
jadwalnya berakhir maupun semua kontak mencapai status terminal. Percobaan ulang
runner tidak mengirim peristiwa lain. Peristiwa siklus hidup tingkat organisasi
ini hanya dikirim ke endpoint dengan cakupan organisasi, bukan endpoint dengan
cakupan agen. Tidak memblokir.

```json
{
  "data": {
    "campaign_id": "3f6b2c9e-2a0d-4c63-b6d6-a708dc98f403",
    "name": "May win-back",
    "agent_id": 12,
    "status": "completed",
    "started_at": "2026-04-20T17:00:00Z",
    "completed_at": "2026-04-20T18:25:11Z",
    "counts": {
      "contacts_total": 150,
      "completed": 121,
      "failed": 11,
      "no_answer": 18,
      "remaining": 0
    }
  },
  "event_id": "8d8f52ce-6b46-423f-9dde-cea0b91ec135",
  "type": "campaign.completed"
}
```

Keempat jumlah hasil saling terpisah dan jika dijumlahkan menghasilkan
`contacts_total`: `completed` berisi kontak yang berhasil; `no_answer` berisi
kontak gagal/kehabisan upaya yang terminal dengan hasil akhir tidak ada jawaban;
`failed` berisi semua kontak gagal/kehabisan upaya terminal lainnya; dan
`remaining` berisi kontak yang tertunda, terjadwal, atau sedang dihubungi.
Kontak yang menunggu percobaan ulang adalah `remaining`, bahkan ketika upaya
terbarunya tidak dijawab. Panggilan yang sedang berlangsung direkonsiliasi
sebelum snapshot penyelesaian sekali saja. `started_at` adalah waktu mulai
kampanye yang dikonfigurasi, atau waktu pembuatan kampanye jika waktu mulai tidak
dikonfigurasi.

### `issue.reported`

Dikirim ketika sebuah [laporan masalah](/api-reference/issue-reports) dibuat —
baik diajukan oleh pengguna dari dashboard (`source: "user"`) maupun dibuat
secara otomatis oleh penilaian panggilan (`source: "system"`). Tidak memblokir.

```json
{
  "data": {
    "call_id": 987654321,
    "issue_report": {
      "created_at": "2026-04-20T18:25:11.002Z",
      "description": "Five-second silence before responding to the main question.",
      "id": 4321,
      "severity": "warning",
      "source": "system",
      "status": "open",
      "title": "Agent paused too long"
    }
  },
  "event_id": "5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a8b",
  "type": "issue.reported"
}
```

| Bidang | Tipe | Deskripsi |
|-------|------|-------------|
| `agent_id` | integer \| null | Agen yang menangani panggilan, jika ditetapkan |
| `agent_name` | string \| null | Agen yang menangani panggilan, jika ditetapkan |
| `issue_report.severity` | string | `critical`, `warning`, atau `info` |
| `issue_report.status` | string | `open` atau `resolved` |
| `issue_report.source` | string | `user` (diajukan dari dashboard) atau `system` (dibuat oleh penilaian) |

<Note>
  Menilai ulang panggilan membangun ulang laporan masalah yang dibuat sistem,
  yang mengirim ulang `issue.reported` untuk laporan yang dibuat ulang.
  Lakukan deduplikasi berdasarkan `call_id` + `title` jika Anda hanya
  menginginkan satu notifikasi untuk setiap masalah yang mendasarinya.
</Note>

### `issue.escalated`

Dikirim ketika pola masalah dikirim ke ThunderPhone untuk ditinjau staf: setelah
**Laporkan ke ThunderPhone**, atau ketika Perbaiki dengan AI tidak dapat
mengonfirmasi perbaikan di sisi pelanggan dan merutekan masalah secara otomatis.
Hanya endpoint seluruh organisasi yang menerima peristiwa ini.

```json
{
  "data": {
    "automatic": false,
    "cluster_id": "7ac2844c-2df0-4fa8-a560-7378da649e19",
    "escalation_id": "ec99f52b-c8c0-41dd-a4f2-dd8a07b10894",
    "status": "open"
  },
  "event_id": "d8f8f420-7a42-45ba-bcf1-b747f9bbecda",
  "type": "issue.escalated"
}
```

| Bidang | Tipe | Deskripsi |
|-------|------|-------------|
| `automatic` | boolean | `true` untuk eskalasi otomatis; `false` untuk eskalasi manual |
| `cluster_id` | UUID | Pola masalah yang dieskalasikan |
| `escalation_id` | UUID | Rekaman eskalasi |
| `status` | string | `open` ketika peristiwa dikirim |

Ini adalah notifikasi, bukan kumpulan bukti. Gunakan `cluster_id` untuk
mengorelasikannya dengan pola masalah. Lihat
[Laporkan ke ThunderPhone](/id/guides/issues).

---

## Peristiwa panggilan uji

### `test-call.completed`

Dikirim ketika sebuah
[eksekusi test-call](/api-reference/test-calls#test-call-run-object)
mencapai status terminal — `completed` atau `failed`, termasuk eksekusi
yang gagal saat diluncurkan dan tidak pernah menghasilkan panggilan. Tidak memblokir. Berguna
untuk menghubungkan eksekusi CI batch ke sistem chat/notifikasi Anda.

```json
{
  "data": {
    "test_call_run": {
      "call_id": 987654321,
      "completed_at": "2026-04-20T18:25:04.822Z",
      "error_message": "",
      "id": 7110,
      "status": "completed",
      "target_id": 12,
      "target_type": "agent"
    }
  },
  "event_id": "2b3c4d5e-6f7a-4b8c-9d0e-1f2a3b4c5d6e",
  "type": "test-call.completed"
}
```

| Bidang | Tipe | Deskripsi |
|-------|------|-------------|
| `agent_id` | integer \| null | Agen yang menangani panggilan, jika ada yang ditetapkan |
| `agent_name` | string \| null | Agen yang menangani panggilan, jika ada yang ditetapkan |
| `test_call_run.target_type` | string | `agent` atau `phone_number` |
| `test_call_run.target_id` | integer | ID agen atau ID nomor telepon yang dituju eksekusi, sesuai dengan `target_type` |
| `test_call_run.status` | string | `completed` atau `failed` |
| `test_call_run.call_id` | integer \| null | `null` jika eksekusi gagal sebelum panggilan dilakukan |
| `test_call_run.error_message` | string | Kosong jika berhasil |

---

## Peristiwa peringatan

### `alert.triggered`

Dikirim ketika sebuah [aturan peringatan](/id/guides/alerts) dengan saluran
**Kirim ke webhook developer** diaktifkan melampaui ambang batasnya.
Tidak memblokir. Aturan dipicu sekali lalu mematuhi masa tunggunya, sehingga
pelanggaran berkelanjutan menghasilkan satu peristiwa per jendela masa tunggu.

```json
{
  "data": {
    "comparator": "lt",
    "event_id": "b8e6a1d4-2c3f-4a5b-9c8d-7e6f5a4b3c2d",
    "fired_at": "2026-04-20T18:00:00+00:00",
    "metric": "success_rate",
    "metric_value": 71.4,
    "rule_id": "d2c3b4a5-6f7e-4d8c-9b0a-1c2d3e4f5a6b",
    "rule_name": "Success rate below 80%",
    "threshold": 80.0,
    "window_hours": 24
  },
  "event_id": "4d5e6f7a-8b9c-4d0e-9f1a-2b3c4d5e6f7a",
  "type": "alert.triggered"
}
```

| Bidang | Tipe | Deskripsi |
|-------|------|-------------|
| `event_id` (dalam `data`) | UUID | ID **pemicu** peringatan — berbeda dari `event_id` pengiriman pada envelope |
| `rule_id`, `rule_name` | UUID, string | Aturan yang dipicu |
| `metric` | string | `success_rate`, `failure_rate`, `avg_score`, `call_volume`, atau `suite_regression` |
| `comparator` | string | `lt`, `lte`, `gt`, atau `gte` |
| `metric_value` | number | Nilai metrik pada jendela saat aturan dipicu |
| `threshold` | number | Ambang batas yang dikonfigurasi |
| `window_hours` | integer | Jendela evaluasi berjalan mundur |
| `fired_at` | timestamp | |

Lihat [panduan Peringatan](/id/guides/alerts) untuk membuat aturan, metrik,
masa tunggu, serta saluran email / Slack.

---

## Peristiwa migrasi

### `migration.call.complete`

Dikirim ketika panggilan yang dilakukan melalui
[`POST /v1/migrations/{id}/calls`](/api-reference/migration-calls) berakhir,
terlepas dari platform yang menanganinya. Tidak memblokir. Peristiwa ini tiba hingga sekitar 5 menit
setelah panggilan berakhir. `data` memiliki setiap field dari
[`telephony.complete`](/id/webhooks/call-complete) beserta blok `migration`. Panggilan yang dilakukan ThunderPhone juga mengirim `telephony.complete` seperti biasa, dengan
`call_id` yang sama. Panggilan di platform lain yang belum berakhir setelah 2
jam, atau yang migrasinya selesai atau kehilangan kuncinya terlebih dahulu, dikirim dengan
`status: "unknown"` dan tanpa transkrip.

```json
{
  "data": {
    "agent_id": 42,
    "agent_name": "Front desk",
    "billable_minutes": null,
    "billing_total_cents": null,
    "call_id": null,
    "direction": "outbound",
    "duration_seconds": 94,
    "end_reason": "user_hangup",
    "end_time": "2026-10-06T17:01:34+00:00",
    "extracted_data": null,
    "from_number": "+15551234567",
    "migration": {
      "arm": "vendor",
      "id": "5f0c2a9e-7b1d-4c3e-8a6f-2d9e1b4c7a30",
      "metadata": {
        "lead_id": "8812"
      },
      "routing_id": "9b2f8c1e-4d3a-4f6b-8e2d-1a7c5b9e0f34",
      "vendor": "retell",
      "vendor_call_id": "call_6a1f0e",
      "vendor_cost_usd": "0.1240"
    },
    "product": "",
    "recording_mode": "vendor",
    "recording_url": "https://…",
    "start_time": "2026-10-06T17:00:00+00:00",
    "status": "completed",
    "summary": "The callee booked a table for two on Thursday.",
    "thunderphone_retained_call_content": true,
    "to_number": "+14155552671",
    "transcripts": [
      {
        "content": "Hi Ada, this is Rosa's calling about your booking.",
        "role": "agent"
      },
      {
        "content": "Yes, Thursday works.",
        "role": "user"
      }
    ],
    "transfer_number": null,
    "unresolved_variables": [],
    "variables": {
      "first_name": "Ada"
    },
    "voice": ""
  },
  "event_id": "7c1e2f3a-4b5c-4d6e-8f7a-9b0c1d2e3f4a",
  "type": "migration.call.complete"
}
```

| Field | Tipe | Deskripsi |
|-------|------|-------------|
| `migration.id` | UUID | Migrasi |
| `migration.routing_id` | UUID | Panggilan yang dirutekan, seperti yang dikembalikan saat Anda melakukannya |
| `migration.arm` | string | `ours` (ThunderPhone yang melakukan panggilan) atau `vendor` (platform lain yang melakukannya) |
| `migration.vendor` | string | `retell`, `vapi`, `elevenlabs`, atau `bland` |
| `migration.vendor_call_id` | string \| null | ID panggilan platform lain; `null` untuk `ours` |
| `migration.metadata` | object | `metadata` yang Anda kirim saat melakukan panggilan |
| `migration.vendor_cost_usd` | string \| null | Biaya panggilan yang dilaporkan platform lain, jika dilaporkan |
| `call_id` | integer \| null | ID panggilan ThunderPhone; `null` untuk `vendor` |
| `status` | string | Untuk `vendor`: `completed` ketika penerima panggilan berbicara, `failed` ketika tidak, `unknown` seperti di atas. Untuk `ours`: seperti dalam `telephony.complete` |

Untuk panggilan `vendor`, ThunderPhone tidak memiliki data penagihan atau ekstraksi:
`billable_minutes`, `billing_total_cents`, dan `extracted_data` bernilai `null`,
`product` dan `voice` kosong, `recording_mode` adalah `vendor`, dan
`recording_url` adalah tautan platform lain, jika platform tersebut memberikannya.

### `migration.rollback`

Dikirim ketika peralihan bertahap suatu migrasi secara otomatis memindahkan
semua panggilan kembali ke platform lain: 3 panggilan gagal terhubung ke
ThunderPhone dalam 15 menit, 2 panggilan yang dikirim ke ThunderPhone berakhir
sebelum agen menjawab, atau tingkat hasil tugas ThunderPhone turun lebih dari
selisih yang dikonfigurasi di bawah tingkat platform lain. Tidak memblokir,
sekali untuk setiap rollback. Setelahnya, porsi ThunderPhone adalah 0% dan
migrasi dijeda hingga seseorang melanjutkannya di tab Migrasi agen. Payload
tidak memuat nomor telepon, transkrip, atau ID panggilan.

```json
{
  "data": {
    "agent_id": 42,
    "agent_name": "Front desk",
    "dropped": 0,
    "migration_id": "5f0c2a9e-7b1d-4c3e-8a6f-2d9e1b4c7a30",
    "ours": {
      "graded": 20,
      "success_rate": 0.8
    },
    "ours_failed": 3,
    "ours_percent": 0,
    "previous_percent": 25,
    "reason": "ours_failed_burst",
    "rolled_back_at": "2026-10-10T12:05:00+00:00",
    "step": 1,
    "theirs": {
      "graded": 20,
      "success_rate": 0.8
    },
    "vendor": "retell"
  },
  "event_id": "3d1f6b2a-8c4e-5a7b-9d0e-2f1a3b4c5d6e",
  "type": "migration.rollback"
}
```

| Bidang | Tipe | Deskripsi |
|-------|------|-------------|
| `migration_id` | UUID | Migrasi |
| `vendor` | string | `retell`, `vapi`, `elevenlabs`, atau `bland` |
| `reason` | string | `ours_failed_burst` (3 koneksi gagal dalam 15 menit), `dropped_calls` (2 panggilan berakhir sebelum agen menjawab, dalam 30 menit), atau `rate_gap` (selisih tingkat hasil tugas pada setidaknya `rollback_min_calls` panggilan yang dinilai untuk masing-masing pihak) |
| `previous_percent` | integer | Porsi panggilan ThunderPhone sebelum rollback |
| `ours_percent` | integer | Selalu `0` |
| `step` | integer \| null | Langkah jadwal yang sedang digunakan (berbasis 0) |
| `ours` / `theirs` | object \| null | Panggilan yang dinilai pada langkah saat ini dan tingkat keberhasilannya; `theirs` dapat mencakup 30 hari terakhir ketika langkah tersebut memiliki terlalu sedikit panggilan. `null` jika tidak ada |
| `ours_failed` | integer | Koneksi gagal yang dihitung dalam 15 menit terakhir |
| `dropped` | integer | Panggilan yang dihitung sebagai terputus dalam 30 menit terakhir |
| `rolled_back_at` | timestamp | |

---

## Terkait

<CardGroup cols={2}>
  <Card title="telephony.incoming / web.incoming" icon="phone" href="/id/webhooks/call-incoming">
    Payload panggilan masuk yang memblokir dan harus Anda respons.
  </Card>
  <Card title="telephony.complete / web.complete" icon="phone-slash" href="/id/webhooks/call-complete">
    Transkrip dan metrik setelah panggilan.
  </Card>
  <Card title="Endpoint webhook" icon="bolt" href="/id/webhooks/endpoints">
    Daftarkan URL ke sebagian peristiwa ini.
  </Card>
  <Card title="Alat Fungsi" icon="screwdriver-wrench" href="/id/tools/overview">
    Cara peristiwa `telephony.tool` / `web.tool` dibuat.
  </Card>
</CardGroup>
