---
title: "Alat Fungsi"
description: "Berikan alat fungsi kepada agen AI Anda yang memanggil API eksternal di tengah percakapan — mengambil data pelanggan, menjadwalkan janji temu, memperbarui catatan — dengan parameter bertipe."
---

Alat fungsi memungkinkan agen AI Anda memanggil API eksternal selama panggilan telepon. Gunakan untuk mencari data pelanggan, memeriksa ketersediaan, menjadwalkan janji temu, atau menjalankan tindakan apa pun yang didukung backend Anda.

## Cara Kerjanya

1. Tentukan alat dengan skema (argumen yang diterima alat)
2. Sediakan konfigurasi `endpoint` (tempat ThunderPhone memanggil API Anda) — atau biarkan kosong untuk menerima panggilan alat pada webhook organisasi Anda
3. Selama panggilan, AI memutuskan kapan menggunakan alat berdasarkan percakapan
4. ThunderPhone memanggil endpoint Anda dengan argumen alat
5. Respons API Anda dikirim kembali ke AI untuk melanjutkan percakapan

| Kemampuan | Tempat dijalankan | Penyiapan |
| --- | --- | --- |
| [Alat bawaan](/id/guides/built-in-tools) | ThunderPhone | Instruksi Prompt; beberapa alat juga memerlukan pengaturan agen |
| [Koneksi aplikasi](/id/guides/connect-apps) | ThunderPhone dan penyedia yang terhubung | Hubungkan akun dan lampirkan tindakan yang disetujui |
| [Koneksi API](/id/guides/api-connections) dan alat fungsi | API HTTP Anda | Tentukan endpoint dan skema, atau terima panggilan fungsi melalui webhook |
| [Server MCP](/id/guides/mcp-servers) | Server MCP jarak jauh | Tambahkan server, temukan alatnya, lalu lampirkan ke agen |

---

## Skema Alat

Setiap alat mengikuti struktur ini:

```json
{
  "type": "function",
  "function": {
    "name": "search_appointments",
    "description": "Find available appointment slots for a given date",
    "parameters": {
      "type": "object",
      "properties": {
        "date": {
          "type": "string",
          "description": "Date in YYYY-MM-DD format"
        },
        "service": {
          "type": "string",
          "description": "Type of service (e.g., 'consultation', 'follow-up')"
        }
      },
      "required": ["date"]
    }
  },
  "endpoint": {
    "url": "https://api.example.com/appointments/search",
    "method": "POST",
    "headers": {
      "X-Api-Key": "your-api-key"
    }
  },
  "timeout": 120
}
```

### Konfigurasi Alat

| Kolom | Tipe | Wajib | Deskripsi |
|-------|------|----------|-------------|
| `timeout` | number | Tidak | Waktu eksekusi maksimum dalam detik (default: `20`, maksimum: `180`) |

### Definisi Fungsi

| Kolom | Tipe | Wajib | Deskripsi |
|-------|------|----------|-------------|
| `name` | string | Ya | Pengenal unik untuk alat |
| `description` | string | Ya | Menjelaskan kepada AI kapan alat ini digunakan |
| `parameters` | object | Ya | Skema JSON untuk argumen alat |

### Konfigurasi Endpoint

| Kolom | Tipe | Wajib | Deskripsi |
|-------|------|----------|-------------|
| `url` | string | Ya | URL endpoint API Anda |
| `method` | string | Tidak | Metode HTTP (default: `POST`) |
| `headers` | object | Tidak | Header khusus yang akan disertakan |

<Note>
  Konfigurasi `endpoint` **tidak** dikirim ke model AI—konfigurasi ini hanya digunakan oleh ThunderPhone untuk menjalankan panggilan alat.
</Note>

---

## Dua jalur pemanggilan

Permintaan yang diterima server Anda bergantung pada apakah alat memiliki
`endpoint`:

| | Tool **dengan** `endpoint` | Tool **tanpa** `endpoint` |
|---|---|---|
| Tujuan permintaan | Langsung ke `endpoint.url` | [URL webhook lama](/api-reference/organizations#legacy-single-url-webhook) organisasi Anda |
| Isi | Format permintaan yang dipilih (argumen tanpa pembungkus secara default) | Pembungkus `telephony.tool` / `web.tool` |
| Header | `endpoint.headers` Anda + `X-ThunderPhone-Call-ID` + `X-ThunderPhone-Signature-V2` + `X-ThunderPhone-Signature` | `Content-Type` + `X-ThunderPhone-Signature-V2` + `X-ThunderPhone-Signature` |
| Kunci penandatanganan | Rahasia webhook organisasi | Rahasia webhook organisasi |

Kedua jalur bersifat **memblokir** — AI menunggu hasilnya di tengah
kalimat. Batas waktu default adalah **20 d**; atur `timeout` tingkat atas
pada alat untuk mengizinkan eksekusi yang lebih lama, hingga batas maksimum
Platform **180 d**. Pastikan handler tetap cepat. Kombinasi keduanya dapat digunakan:
pada panggilan yang organisasinya memiliki URL webhook, alat dengan `endpoint`
dipanggil secara langsung dan sisanya dialihkan ke webhook.

## Panggilan endpoint langsung

Saat AI memanggil alat yang memiliki `endpoint`, ThunderPhone mengirim
permintaan ke URL Anda:

### Header Permintaan

```http
POST /appointments/search HTTP/1.1
Host: api.example.com
Content-Type: application/json
X-ThunderPhone-Signature-V2: t=1767225600,v1=5f2c...
X-ThunderPhone-Signature: abc123...
X-ThunderPhone-Call-ID: 987654321
X-Api-Key: your-api-key
```

Header kustom dari `endpoint.headers` Anda selalu disertakan
apa adanya, ditambah header dengan namespace ThunderPhone berikut:

- `X-ThunderPhone-Signature-V2` — HMAC-SHA256 bertanda waktu atas
  metode, path, kueri, dan body, yang menggunakan **secret webhook organisasi** Anda.
  Verifikasi header ini dan tolak permintaan yang berusia lebih dari 5 menit; lihat
  [Verifikasi signature webhook](/id/guides/verify-webhook-signatures)
- `X-ThunderPhone-Signature` — HMAC-SHA256 lama dari byte body
  permintaan yang persis sama, dengan kunci yang sama, masih dikirim untuk verifier yang ada
- `X-ThunderPhone-Call-ID` — ID panggilan saat ini

`Content-Type: application/json` ditetapkan kecuali `endpoint.headers`
Anda menggantinya — `Content-Type` kustom akan digunakan.

<Warning>
  Tanda tangan menggunakan rahasia webhook tingkat organisasi dari
  [`GET /v1/webhook`](/api-reference/organizations#legacy-single-url-webhook).
  Jika organisasi Anda belum pernah mengonfigurasi webhook lama, tidak ada
  rahasia dan panggilan alat hanya membawa `X-ThunderPhone-Call-ID` — sebuah
  handler yang gagal keras saat tanda tangan tidak ada akan menolaknya.
  Konfigurasikan webhook lama untuk mendapatkan rahasia, atau tempatkan rahasia
  bersama Anda sendiri di `endpoint.headers`.
</Warning>

### Format permintaan

Setiap koneksi API memiliki pengaturan `request_format`. Pilih `vapi` atau
`retell` ketika tool yang diimpor harus tetap memanggil endpoint yang dibuat untuk
platform tersebut. Koneksi baru dan yang sudah ada secara default menggunakan `standard`.

Untuk `POST` / `PUT` / `PATCH`, ThunderPhone membuat serialisasi
envelope yang dipilih secara kanonis (kunci diurutkan, pemisah ringkas). Kedua tanda tangan
mencakup byte yang tepat ini.

#### Standard

`standard` hanya mengirim argumen alat, tanpa wrapper. Ini adalah default dan mempertahankan isi permintaan asli ThunderPhone byte demi byte:

```json
{"date":"2025-01-02","service":"consultation"}
```

#### Vapi

`vapi` mengirim [pesan server `tool-calls`](https://docs.vapi.ai/tools/custom-tools) Vapi
dalam bentuk yang didokumentasikan Vapi. `call.id` adalah ID panggilan ThunderPhone. `id`
tool-call bersifat unik untuk pemanggilan tersebut dan digunakan kembali untuk mencocokkan
hasil endpoint. Panggilan muncul di `toolCallList` dan
`toolWithToolCallList`, dengan `function.arguments` sebagai objek JSON:

```json
{
  "message": {
    "type": "tool-calls",
    "timestamp": 1725000000123,
    "call": { "id": "987654321" },
    "toolCallList": [
      {
        "id": "call_00112233445566778899aabbccddeeff",
        "type": "function",
        "function": {
          "name": "search_appointments",
          "arguments": { "date": "2025-01-02", "service": "consultation" }
        }
      }
    ],
    "toolWithToolCallList": [
      {
        "type": "function",
        "function": { "name": "search_appointments" },
        "toolCall": {
          "id": "call_00112233445566778899aabbccddeeff",
          "type": "function",
          "function": {
            "name": "search_appointments",
            "arguments": { "date": "2025-01-02", "service": "consultation" }
          }
        }
      }
    ]
  }
}
```

Kolom khusus Vapi seperti `assistant`, `artifact`, dan objek `call` lengkap
tidak dikirim.

Kembalikan envelope respons Vapi dengan ID tool-call yang sesuai:

```json
{
  "results": [
    {
      "toolCallId": "call_00112233445566778899aabbccddeeff",
      "result": "{\"available_slots\":[\"9:00 AM\",\"2:00 PM\"]}"
    }
  ]
}
```

ThunderPhone memberikan string `result` tanpa pembungkus kepada AI. ThunderPhone juga menerima
nilai JSON untuk kompatibilitas dengan endpoint yang sudah mengembalikannya. `error`
sebagai pengganti `result` dikembalikan kepada AI sebagai error tool. Jika
endpoint mengembalikan envelope Vapi yang tidak valid atau tidak cocok, ThunderPhone mencatat
peringatan dan beralih ke parsing respons standarnya alih-alih menggagalkan
panggilan.

#### Retell

`retell` mengirim permintaan yang didokumentasikan untuk [fungsi kustom
Retell](https://docs.retellai.com/build/single-multi-prompt/custom-function):

```json
{
  "name": "search_appointments",
  "call": { "call_id": "987654321" },
  "args": { "date": "2025-01-02", "service": "consultation" }
}
```

Objek `call` hanya menyertakan `call_id`. ThunderPhone tidak menyertakan
kolom khusus Retell yang tidak dapat disediakannya secara akurat saat pengiriman, seperti
`agent_id`, `transcript`, `transcript_object`, `metadata`, atau variabel
dinamis. Retell menerima isi respons apa pun; ThunderPhone meneruskannya
menggunakan penanganan respons standar.

Untuk `GET` / `DELETE`, argumen dikirim sebagai **parameter kueri**
dan isi permintaan kosong terlepas dari `request_format`. Tanda tangan V2
mencakup string kueri sebagaimana dikirim; tanda tangan lama dihitung berdasarkan
string byte kosong. Lihat
[Verifikasi tanda tangan webhook](/id/guides/verify-webhook-signatures).

### Respons

Kembalikan respons JSON dengan hasil alat:

```json
{
  "available_slots": ["9:00 AM", "2:00 PM", "4:30 PM"],
  "timezone": "America/Los_Angeles"
}
```

Respons diformat dan diberikan kepada AI untuk melanjutkan
percakapan. Respons non-JSON dibungkus sebagai `{"data": "<text>"}`;
waktu habis dan kegagalan koneksi dilaporkan kepada AI sebagai kesalahan, sehingga
agen dapat meminta maaf dan melanjutkan alih-alih terhenti.

## Pengiriman mode webhook

Alat **tanpa** `endpoint` dikirim ke URL webhook lama organisasi Anda
sebagai permintaan `telephony.tool` (panggilan telepon) atau `web.tool`
(panggilan web) yang ditandatangani. Berbeda dengan [notifikasi audit](/id/webhooks/events)
yang dikirimkan ke endpoint webhook setelah eksekusi, permintaan ini **adalah**
eksekusinya — respons HTTP Anda adalah hasil alat.

```json
{
  "type": "telephony.tool",
  "data": {
    "call_id": 987654321,
    "tool_name": "search_appointments",
    "arguments": { "date": "2026-04-21" },
    "from_number": "+14155550199",
    "to_number": "+15551234567"
  }
}
```

`web.tool` membawa `origin_domain` alih-alih `from_number` /
`to_number`. Tanggapi dengan hasil tool sebagai JSON — kontrak respons yang sama
seperti panggilan endpoint langsung. Permintaan ditandatangani dengan rahasia webhook organisasi, menggunakan kedua header tanda tangan, seperti setiap webhook lainnya.

<Note>
  [Endpoint webhook](/id/webhooks/endpoints) yang berlangganan juga
  menerima notifikasi `telephony.tool` / `web.tool` yang tidak memblokir
  **setelah** setiap alat dieksekusi (jalur mana pun yang menjalankannya), termasuk
  respons alat — berguna untuk jejak audit. Lihat
  [katalog peristiwa](/id/webhooks/events).
</Note>

---

## Verifikasi Tanda Tangan

Panggilan tool langsung ditandatangani dengan cara yang sama seperti webhook, menggunakan
rahasia webhook organisasi Anda:

- `X-ThunderPhone-Signature-V2` menandatangani stempel waktu, metode, path, dan
  query sebagaimana dikirim, serta byte body yang tepat. Tolak permintaan yang
  stempel waktunya berjarak lebih dari 5 menit dari jam Anda.
- `X-ThunderPhone-Signature` (legacy) hanya menandatangani byte body, yaitu
  string byte kosong untuk `GET` / `DELETE`. Header ini tetap dikirim, tanpa perubahan.

Helper SDK memeriksa header V2, termasuk jangka waktu 5 menit:

<CodeGroup>
```python Python
import json

from fastapi import FastAPI, HTTPException, Request
from thunderphone import verify_signature

app = FastAPI()

@app.post("/appointments/search")
async def search_appointments(request: Request):
    body = await request.body()
    query = request.scope.get("query_string", b"").decode("latin-1")
    path = request.url.path + (f"?{query}" if query else "")

    if not verify_signature(
        request.headers.get("X-ThunderPhone-Signature-V2"),
        secret=WEBHOOK_SECRET,
        method=request.method,
        path=path,
        body=body,
    ):
        raise HTTPException(status_code=401)

    data = json.loads(body)
    date = data["date"]

    # Look up availability
    slots = await get_available_slots(date)

    return {"available_slots": slots}
```

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

app.post('/appointments/search', express.raw({type: 'application/json'}), (req, res) => {
  const ok = verifySignature({
    header: req.header('X-ThunderPhone-Signature-V2'),
    secret: WEBHOOK_SECRET,
    method: req.method,
    path: req.originalUrl,
    body: req.body,
  });
  if (!ok) return res.status(401).send('Invalid signature');

  const { date, service } = JSON.parse(req.body);

  // Look up availability
  const slots = getAvailableSlots(date, service);

  res.json({ available_slots: slots });
});
```
</CodeGroup>

Panduan lengkap — string yang ditandatangani, contoh yang dikerjakan, kode tanpa
SDK, kasus `GET` / `DELETE`, dan catatan tanpa rahasia — tersedia di [Verifikasi tanda tangan webhook](/id/guides/verify-webhook-signatures).

---

## Contoh: Alur Pemesanan Lengkap

Berikut adalah serangkaian alat untuk sistem pemesanan janji temu lengkap:

```json
{
  "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": "key" }
      }
    },
    {
      "type": "function",
      "function": {
        "name": "book_appointment",
        "description": "Book an appointment at a specific time",
        "parameters": {
          "type": "object",
          "properties": {
            "date": { "type": "string", "description": "YYYY-MM-DD" },
            "time": { "type": "string", "description": "HH:MM format" },
            "customer_name": { "type": "string" },
            "customer_phone": { "type": "string" }
          },
          "required": ["date", "time", "customer_name"]
        }
      },
      "endpoint": {
        "url": "https://api.example.com/appointments/book",
        "method": "POST",
        "headers": { "X-Api-Key": "key" }
      }
    },
    {
      "type": "function",
      "function": {
        "name": "cancel_appointment",
        "description": "Cancel an existing appointment",
        "parameters": {
          "type": "object",
          "properties": {
            "confirmation_number": { "type": "string" }
          },
          "required": ["confirmation_number"]
        }
      },
      "endpoint": {
        "url": "https://api.example.com/appointments/cancel",
        "method": "POST",
        "headers": { "X-Api-Key": "key" }
      }
    }
  ]
}
```

---

## Praktik Terbaik

<AccordionGroup>
  <Accordion title="Tulis deskripsi yang jelas">
    Kolom `description` membantu AI memahami **kapan** harus menggunakan alat tersebut. Jelaskan secara spesifik apa fungsinya dan kapan alat tersebut sesuai digunakan.
  </Accordion>

  <Accordion title="Tangani error dengan baik">
    Kembalikan pesan error yang dapat dipahami AI: `{"error": "No slots available for that date"}` alih-alih error 500 generik.
  </Accordion>

  <Accordion title="Jaga respons tetap ringkas">
    Kembalikan hanya informasi yang diperlukan AI untuk melanjutkan percakapan. Payload besar memperlambat waktu respons.
  </Accordion>

  <Accordion title="Gunakan kolom wajib dengan bijak">
    Tandai kolom sebagai `required` hanya jika benar-benar diperlukan. AI akan meminta informasi wajib dari pengguna sebelum memanggil alat.
  </Accordion>
</AccordionGroup>

---

## Terkait

<CardGroup cols={2}>
  <Card title="Alat bawaan" icon="wrench" href="/id/guides/built-in-tools">
    Arahkan tindakan panggilan yang dikelola Platform tanpa menentukan endpoint.
  </Card>
  <Card title="Koneksi aplikasi" icon="plug" href="/id/guides/connect-apps">
    Alat yang dikelola Platform untuk HubSpot, Salesforce, Slack, Google
    Calendar, Google Sheets, dan Cal.com — tanpa endpoint.
  </Card>
  <Card title="Server MCP" icon="server" href="/id/guides/mcp-servers">
    Lampirkan server MCP dan biarkan agen memanggil alatnya.
  </Card>
  <Card title="Koneksi API" icon="code" href="/id/guides/api-connections">
    Integrasi REST yang dapat digunakan kembali dan dapat Anda lampirkan ke agen.
  </Card>
  <Card title="Verifikasi tanda tangan webhook" icon="shield-check" href="/id/guides/verify-webhook-signatures">
    Satu helper verifikasi untuk webhook dan panggilan alat.
  </Card>
</CardGroup>
