---
title: "Ringkasan Webhook"
description: "Cara ThunderPhone mengirimkan peristiwa secara real-time, cara memverifikasi tanda tangan, serta perbandingan model pengiriman lama dan berbasis endpoint."
---

ThunderPhone mengirim permintaan HTTP `POST` ke server Anda ketika peristiwa
terjadi selama panggilan — panggilan masuk dimulai, panggilan berakhir, proses penilaian
selesai, peringatan dipicu, dan sebagainya. Ada **dua model
pengiriman**:

<CardGroup cols={2}>
  <Card title="Endpoint webhook (direkomendasikan)" icon="bolt" href="/id/webhooks/endpoints">
    Beberapa URL, secret per endpoint, filter peristiwa per endpoint,
    dan percobaan ulang otomatis.
    Kelola melalui `GET/POST/PATCH/DELETE /v1/developer/webhook-endpoints`.
  </Card>
  <Card title="Webhook lama satu URL" icon="link" href="/api-reference/organizations#legacy-single-url-webhook">
    Satu URL per organisasi. Membawa peristiwa siklus hidup panggilan, termasuk
    pertukaran konfigurasi **pemblokiran**. Dikelola di `GET/PUT /v1/webhook`.
  </Card>
</CardGroup>

Semua jenis peristiwa yang tercantum dalam [katalog peristiwa](/id/webhooks/events)
dikirimkan melalui endpoint webhook. Enam peristiwa siklus hidup panggilan
(`telephony.incoming`, `telephony.complete`, `telephony.tool`,
`web.incoming`, `web.complete`, `web.tool`) **juga** dikirim ke
webhook lama satu URL — jika Anda memiliki URL lama dan endpoint yang
sesuai, Anda menerima peristiwa di **kedua** jalur. Perilaku pemblokiran
([pertukaran konfigurasi `telephony.incoming` / `web.incoming`](/id/webhooks/call-incoming)
dan [pengiriman tool](/id/tools/overview) mode webhook)
hanya tersedia pada jalur lama; setiap pengiriman endpoint adalah
notifikasi tanpa menunggu respons.

## Format payload

Pengiriman endpoint berupa objek JSON dengan `data`, `event_id`, dan
`type`:

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

`event_id` bersifat unik untuk setiap peristiwa yang diterbitkan. Nilainya sama pada percobaan ulang
**dan** pada setiap endpoint yang menerima peristiwa tersebut — lakukan deduplikasi berdasarkan ID ini.

Webhook lama satu URL mengirim `type` dan `data` yang sama, tetapi
**tanpa** `event_id`:

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

Dalam transmisi, setiap body diserialisasi secara kanonis — kunci diurutkan
secara alfabetis, tanpa spasi kosong, UTF-8. Contoh yang ditampilkan dengan format rapi dalam
dokumentasi ini hanya untuk keterbacaan.

Lihat [Katalog peristiwa](/id/webhooks/events) untuk daftar lengkap jenis peristiwa
dan field payload.

## Verifikasi tanda tangan

Setiap permintaan membawa dua tanda tangan, keduanya HMAC-SHA256 yang menggunakan `secret` endpoint sebagai kunci (atau `secret` webhook tingkat organisasi Anda untuk pengiriman lama):

- `X-ThunderPhone-Signature-V2: t=<unix seconds>,v1=<hex>` menandatangani stempel waktu, metode, jalur dan kueri, serta body mentah. Verifikasi tanda tangan ini, dan tolak permintaan yang `t`-nya lebih dari **5 menit** dari waktu Anda agar permintaan yang ditangkap tidak dapat diputar ulang nanti. Percobaan ulang ditandatangani kembali saat dikirim.
- `X-ThunderPhone-Signature` hanya menandatangani body mentah. Tanda tangan ini tetap dikirim tanpa perubahan agar verifier yang sudah ada tetap berfungsi.

### Langkah

1. Baca header, metode, jalur dan kueri mentah, serta
   body mentah **sebelum** penguraian apa pun.
2. Hitung HMAC-SHA256 heksadesimal dari
   `<t>\n<METHOD>\n<path and query>\n<body>` menggunakan secret Anda.
3. Tolak permintaan jika `t` meleset lebih dari 300 detik, atau jika
   hasilnya tidak cocok dengan `v1` dalam perbandingan waktu konstan.

Body adalah serialisasi JSON kanonis (kunci diurutkan, pemisah ringkas), dan kami menandatangani tepat byte yang kami kirimkan. [Panduan verifikasi](/id/guides/verify-webhook-signatures) memiliki string yang ditandatangani secara tepat, contoh lengkap, kode tanpa SDK, dan header lama.

<CodeGroup>
```python Python (Flask)
from flask import Flask, abort, request
from thunderphone import verify_signature

app = Flask(__name__)

@app.post("/thunderphone-webhook")
def handle():
    query = request.query_string.decode()
    if not verify_signature(
        request.headers.get("X-ThunderPhone-Signature-V2"),
        secret=WEBHOOK_SECRET,
        method=request.method,
        path=request.path + (f"?{query}" if query else ""),
        body=request.get_data(),  # raw bytes
    ):
        abort(401)
    event = request.get_json()
    # dispatch on event["type"] …
    return "", 204
```

```javascript Node.js (Express)
import express from "express";
import { verifySignature } from "@thunderphone/sdk";

const app = express();
app.post(
  "/thunderphone-webhook",
  express.raw({ type: "application/json" }),
  (req, res) => {
    const ok = verifySignature({
      header: req.header("X-ThunderPhone-Signature-V2"),
      secret: process.env.WEBHOOK_SECRET,
      method: req.method,
      path: req.originalUrl,
      body: req.body,
    });
    if (!ok) return res.sendStatus(401);
    const event = JSON.parse(req.body.toString("utf8"));
    // dispatch on event.type …
    res.sendStatus(204);
  },
);
```
</CodeGroup>

## Semantik pengiriman

Semantik ini berlaku untuk pengiriman **endpoint**. Webhook lama dengan satu URL
adalah satu upaya sinkron tanpa percobaan ulang, kecuali pertukaran konfigurasi
panggilan telepon [`telephony.incoming`](/id/webhooks/call-incoming),
yang dicoba ulang sekali.

<AccordionGroup>
  <Accordion title="Percobaan ulang">
    Setiap event segera dicoba sekali. Respons `2xx` apa pun
    mengonfirmasi pengiriman. Untuk hasil lainnya (non-2xx,
    kesalahan koneksi, waktu habis), kami mencoba ulang pada **1 m, 5 m, 30 m, 2 h, 6 h,
    12 h, dan 24 h setelah upaya pertama** — 8 upaya selama
    24 jam. Jika semua upaya gagal, pengiriman berhenti dan endpoint
    ditandai `status="failing"` di
    [endpoint webhook](/id/webhooks/endpoints); kami mengirim email kepada
    pemilik dan admin organisasi Anda serta menampilkannya di dasbor. Kembalikan `2xx` segera setelah
    payload diterima secara tahan lama; proses secara asinkron.
    Pengiriman uji dari API pengujian endpoint mendapatkan satu percobaan ulang sekitar satu menit setelah
    upaya pertama dan tidak pernah mengubah status endpoint.
  </Accordion>

  <Accordion title="Urutan">
    Urutan pengiriman adalah upaya terbaik. Dalam praktiknya, kami mengirimkan sesuai
    urutan event dipancarkan, tetapi percobaan ulang dapat mengubah urutan saat terjadi kegagalan.
    Selalu lakukan deduplikasi dan rekonsiliasi berdasarkan `call_id` / ID objek.
  </Accordion>

  <Accordion title="Duplikat">
    Pengiriman bersifat **setidaknya sekali**: percobaan ulang setelah respons yang tidak
    kami terima dapat menduplikasi event. Setiap percobaan ulang membawa
    `event_id` yang sama, jadi simpan ID yang sudah diproses dan lewati pengulangan. `event_id` juga
    dibagikan antar-endpoint — dua endpoint yang berlangganan pada
    event yang sama menerima `event_id` yang sama.
  </Accordion>

  <Accordion title="Waktu habis">
    Pengiriman endpoint memiliki waktu habis **30 s** per upaya. Pada
    jalur lama, permintaan pemblokiran yang mengendalikan perilaku panggilan langsung — pertukaran
    konfigurasi [`telephony.incoming` / `web.incoming`](/id/webhooks/call-incoming)
    — dibatasi hingga **10 s** untuk upaya pertama panggilan telepon
    dan **13 s** untuk seluruh pertukaran termasuk satu
    percobaan ulangnya, serta hingga **10 s** untuk sesi widget. Respons yang lambat menunda
    pengangkatan panggilan, jadi usahakan menjawab dalam beberapa detik. [Pengiriman tool](/id/tools/overview) mode webhook mengizinkan 20 s
    secara default, dan deklarasi tool dapat menetapkan `timeout` tingkat atas.
  </Accordion>

  <Accordion title="IP sumber">
    Webhook keluar berasal dari rentang IP cloud ThunderPhone.
    Jika firewall Anda memerlukan daftar izin, hubungi dukungan dan kami akan
    membagikan rentang saat ini.
  </Accordion>
</AccordionGroup>

## Memilih antara webhook legacy dan berbasis endpoint

| Fitur | Legacy (`/v1/webhook`) | Endpoint (`/v1/developer/webhook-endpoints`) |
|---------|------------------------|----------------------------------------------|
| Jumlah URL | 1 per organisasi | Banyak per organisasi |
| Cakupan event | Hanya `telephony.*` / `web.*` | Semua 10 jenis event |
| Filter event | — | Per endpoint |
| Percobaan ulang | Tidak ada, kecuali satu untuk pertukaran konfigurasi panggilan telepon | 8 percobaan selama 24 jam |
| Envelope | `type` + `data` | `type` + `data` + `event_id` |
| Rotasi secret | Mengganti satu secret | Secret per endpoint |
| Nonaktifkan tanpa menghapus | `PUT /v1/webhook` dengan `{"url": ""}` | `status=disabled` |
| Visibilitas Status | — | `active` / `disabled` / `failing` |
| Pertukaran konfigurasi yang memblokir | Ya ([`telephony.incoming` / `web.incoming`](/id/webhooks/call-incoming)) | Tidak pernah — hanya notifikasi |
| Paling sesuai untuk | Konfigurasi panggilan dinamis | Konsumsi event di produksi |

Integrasi baru sebaiknya menerima event melalui webhook berbasis
endpoint. Pertahankan (atau tambahkan) URL legacy hanya jika Anda mengonfigurasi panggilan
secara dinamis saat panggilan diangkat atau menggunakan pengiriman tool mode webhook — pertukaran
permintaan/respons tersebut hanya berjalan pada jalur legacy.

---

## Terkait

<CardGroup cols={2}>
  <Card title="Katalog event" icon="list" href="/id/webhooks/events">
    Semua jenis event dan payload-nya.
  </Card>
  <Card title="Endpoint webhook" icon="bolt" href="/id/webhooks/endpoints">
    Kelola beberapa endpoint, filter event, dan secret.
  </Card>
  <Card title="telephony.incoming / web.incoming" icon="phone" href="/id/webhooks/call-incoming">
    Permintaan pemblokiran yang harus dijawab server Anda untuk mengonfigurasi panggilan.
  </Card>
  <Card title="telephony.complete / web.complete" icon="phone" href="/id/webhooks/call-complete">
    Payload pascapanggilan dengan transkrip, rekaman, dan metrik.
  </Card>
</CardGroup>
