---
title: "Menggunakan data aplikasi dalam sequence suara"
description: "Minta data bertipe dari penerima HTTPS selama panggilan, buat percabangan berdasarkan data itu di langkah berikutnya, tangani kegagalan teknis secara eksplisit, dan pisahkan respons privat dari data panggilan yang disimpan."
canonical: "https://bird.com/id-id/dokumentasi/guides/voice/sequences/code/webhooks"
---

# Menggunakan data aplikasi dalam sequence suara

Gunakan `voice.webhook` saat aplikasi Anda menyimpan data yang dibutuhkan panggilan, misalnya status pesanan, tim, atau konfirmasi janji temu. Langkah ini mengembalikan data tersebut di `success`, lalu langkah Branch berikutnya memilih jalur berdasarkan data itu.

## Menentukan respons

Atur `config.output_schema` ke skema objek untuk data yang dikembalikan. Langkah ini memiliki dua output: `success` membawa data sesuai skema tersebut, dan `failure` menangani masalah teknis. Jika konfigurasi tidak diisi, output-nya berupa objek kosong.

Input berisi HTTPS `url` statis dan `data` opsional berupa objek. Data mendukung objek literal, `$literal`, atau `$expr` untuk seluruh objek. URL dinamis, header buatan sendiri, dan kredensial tidak didukung.

## Memverifikasi permintaan

Bird menandatangani setiap permintaan, termasuk continuation, dengan secret callback workspace Anda menggunakan header [Standard Webhooks](https://www.standardwebhooks.com/): `webhook-id`, `webhook-timestamp`, dan `webhook-signature`. Tanda tangan mencakup ID, timestamp, dan body persis. Selama 24 jam setelah rotasi secret, `webhook-signature` berisi satu entri `v1,` untuk setiap secret aktif, sehingga kedua secret dapat memverifikasi. Panggilan dapat membutuhkan sekitar satu menit sebelum mulai menandatangani dengan secret baru, jadi tetap terima secret sebelumnya sampai masa tumpang tindih berakhir. Respons Anda tidak ditandatangani: Bird memvalidasi sertifikat TLS penerima Anda, dan koneksi HTTPS mengautentikasi data yang dikembalikannya.

Verifikasi header terhadap body mentah dengan fungsi verify dari library Standard Webhooks sebelum melakukan parsing. `webhooks.unwrap` di SDK mem-parsing event platform dan tidak ditujukan untuk permintaan ini. Rotasi secret terlebih dahulu di **Webhooks → Direct callbacks**, karena secret awal tidak pernah ditampilkan. Tanda tangan menunjukkan bahwa pemegang secret workspace Anda telah menandatangani permintaan. Penerima callback SMS Anda memegang secret yang sama dan juga dapat menandatangani, jadi simpan secret hanya di penerima yang Anda percayai. Siapa pun yang dapat mengedit sequence di workspace dapat memilih URL dan `data`. Periksa bahwa `target_url` adalah URL Anda sendiri untuk menolak permintaan yang ditandatangani Bird untuk penerima lain, dan lakukan otorisasi tindakan bisnis di penerima Anda. Otorisasi pelanggan tidak pernah diteruskan.

Permintaan webhook sequence tidak menyertakan sertifikat klien, meskipun workspace Anda mewajibkan mTLS. Konfigurasikan penerima agar menerima koneksi tanpa sertifikat klien, lalu autentikasi permintaan yang ditandatangani sebagai gantinya.

Periksa `webhook-timestamp` yang ditandatangani, yaitu timestamp Unix dalam detik, terhadap jam server yang tersinkronisasi. Tolak permintaan yang lebih dari lima menit di masa lalu atau masa depan. Lakukan deduplikasi berdasarkan `webhook-id` sebelum menjalankan efek samping. Jika memungkinkan, simpan ID dan hasilnya secara atomik bersama tindakan tersebut, lalu kembalikan hasil yang tersimpan untuk permintaan berulang. Setiap continuation memiliki ID sendiri. Gunakan kunci idempotensi bisnis terpisah jika permintaan yang berbeda tidak boleh mengulangi tindakan yang sama.

## Mengembalikan data

Penerima mengembalikan bentuk respons tertutup berikut:

```json
{
  "schema_version": 1,
  "type": "outcome",
  "output": { "status": "confirmed", "message": "Your appointment is confirmed." }
}
```

`output` harus sesuai dengan skema yang dikonfigurasi. Respons mengembalikan data, bukan port, ID node, atau perintah baru. Continuation berupa `{"schema_version":1,"type":"continue","url":"https://example.com/voice/next"}` pada origin kanonis dari URL asli.

Untuk merutekan berdasarkan data, hubungkan `success` ke langkah `logic.branch` dengan kasus seperti `steps.decision.output.status == 'confirmed'`. Deklarasikan field tersebut sebagai string `enum` dalam skema agar receiver hanya dapat mengembalikan nilai-nilai tersebut.

Permintaan berisi identitas run/sesi/panggilan, identitas sequence dan node yang diterima, `data` Anda yang telah dimaterialisasi, serta `results`. Simpan input bisnis di `data`. Hasil Gather privat langsung dapat muncul satu kali di `results`, dan rekaman yang sudah selesai membawa ID jika tersedia. Observasi ini tidak otomatis menjadi field ekspresi.

## Membaca permintaan receiver

Run uji draf mengirim bentuk berikut. ID dan timestamp di bawah ini fiktif; permintaan sebenarnya menggunakan run yang diterima dan kemunculan node saat ini:

```json
{
  "schema_version": 1,
  "method": "POST",
  "target_url": "https://example.com/decision",
  "run_id": "vsr_01k5yb7p000000000000000000",
  "sequence": {
    "id": "vsq_01k5yb7p000000000000000000",
    "version_id": null,
    "draft_revision": 1,
    "entry_node_id": "start",
    "mode": "test"
  },
  "session_id": "vcs_01k5yb7p000000000000000000",
  "call": { "id": "vcl_01k5yb7p000000000000000000", "orig": null, "dest": null },
  "node": { "id": "decision", "occurrence_id": "00000000-0000-4000-8000-000000000001" },
  "data": { "appointment_id": "appointment_123" },
  "results": []
}
```

Untuk run yang dipublikasikan, `version_id` mengidentifikasi versi yang dipilih dan `draft_revision` bernilai null. Untuk panggilan yang menjalankan [definisi inline](/docs/guides/voice/create-calls), `id`, `version_id`, dan `draft_revision` semuanya bernilai null. `mode` dapat berupa `test` atau `production`. Asal dan tujuan menggunakan objek pihak panggilan yang dapat bernilai null. Verifikasi tanda tangan sebelum memercayai field identitas ini.

Observasi Gather di `results` memiliki `type: "gather"`, `node_id`, UUID `command_id`, `digits`, dan `reason`. Observasi rekaman memiliki `type: "recording"`, UUID `command_id`, `state`, dan `recording_id` jika tersedia. Gunakan digit privat hanya untuk permintaan yang dituju; digit tersebut tidak menjadi output ekspresi yang disimpan. Permintaan lanjutan mempertahankan kemunculan node yang sama, dengan observasi yang sudah dikirim dihapus dari `results`.

## Menangani kegagalan

Sampel teknis menggunakan port `failure` dengan `{"code":"webhook_unavailable"}` atau `{"code":"webhook_invalid_response"}`. Kegagalan yang terhubung mengikuti jalur pemulihan; kegagalan yang tidak terhubung menggagalkan run. `success` yang tidak terhubung mengakhiri run secara normal. Permintaan webhook live memiliki batas waktu tiga detik dan tidak dicoba lagi secara otomatis. Preview tidak mengirim permintaan.

Batas waktu tiga detik tersebut mencakup waktu membaca kunci rahasia penandatanganan. Hubungkan `failure` ke jalur pemulihan agar panggilan dapat berlanjut saat receiver tidak tersedia atau mengembalikan respons yang tidak valid. Permintaan yang habis waktunya mungkin sudah memicu efek samping di receiver.

Perlakukan `data`, identitas panggilan, dan digit Gather sebagai data privat. Batasi akses ke receiver dan kunci rahasia, hindari mencatat body atau kunci rahasia ke log, dan simpan input penelepon hanya selama aplikasi Anda membutuhkannya. Jangan masukkan data privat atau kredensial ke URL, karena jejak panggilan menyimpan URL lengkap beserta query-nya. URL dan data yang Anda tulis serta output yang telah divalidasi disimpan dalam riwayat run; digit Gather sementara tidak disertakan dalam input langkah yang direkam. Kembalikan hanya data yang dibutuhkan sequence.

Baca data yang dikembalikan di `steps.decision.output`, dan baca kesalahan teknis di `steps.decision.error.code`. Jika kedua jalur bertemu, periksa wrapper dengan `has(steps.decision.output)` atau `has(steps.decision.error)` sebelum menggunakannya. Respons HTTP 200 dari receiver Anda belum berarti hasilnya valid sampai body-nya lolos kontrak.

## Related resources

- [Voice sequences](/docs/guides/voice/sequence-builder) (docs)
