> ## Documentation Index
> Fetch the complete documentation index at: https://docs.thunderphone.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Alat Fungsi

> Izinkan agen AI Anda memanggil API eksternal selama percakapan

Alat fungsi memungkinkan agen suara AI Anda memanggil API eksternal selama panggilan telepon. Gunakan alat ini untuk mencari data pelanggan, memeriksa ketersediaan, menjadwalkan janji temu, atau melakukan 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

<Note>
  Alat fungsi adalah jalur bawa-API-Anda-sendiri. ThunderPhone juga
  menyediakan alat yang dikelola Platform dan tidak memerlukan endpoint:
  [koneksi aplikasi](/id/guides/connect-apps) (HubSpot, Salesforce, Slack,
  Google Calendar, Google Sheets, Cal.com),
  [koneksi API](/id/guides/api-connections), dan
  [server MCP](/id/guides/mcp-servers).
</Note>

***

## Skema Alat

Setiap alat mengikuti struktur ini:

```json theme={null}
{
  "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"
    }
  }
}
```

### Definisi Fungsi

| Kolom         | Jenis  | Wajib | Deskripsi                                              |
| ------------- | ------ | ----- | ------------------------------------------------------ |
| `name`        | string | Ya    | Pengidentifikasi unik untuk alat                       |
| `description` | string | Ya    | Menjelaskan kepada AI kapan harus menggunakan alat ini |
| `parameters`  | object | Ya    | Skema JSON untuk argumen alat                          |

### Konfigurasi Endpoint

| Kolom     | Jenis  | Wajib | Deskripsi                          |
| --------- | ------ | ----- | ---------------------------------- |
| `url`     | string | Ya    | URL endpoint API Anda              |
| `method`  | string | Tidak | Metode HTTP (default: `POST`)      |
| `headers` | object | Tidak | Header kustom 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`:

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

Kedua jalur bersifat **memblokir** — AI menunggu di tengah kalimat untuk
hasilnya — dengan batas waktu **20 d**. Pastikan handler tetap cepat.
Kombinasi keduanya tidak masalah: pada panggilan dengan organisasi yang memiliki URL webhook,
alat dengan `endpoint` dipanggil langsung dan sisanya kembali menggunakan webhook.

## Panggilan endpoint langsung

Saat AI memanggil tool yang memiliki `endpoint`, ThunderPhone mengirimkan
permintaan ke URL Anda:

### Header Permintaan

```http theme={null}
POST /appointments/search HTTP/1.1
Host: api.example.com
Content-Type: application/json
X-ThunderPhone-Signature: abc123...
X-ThunderPhone-Call-ID: 987654321
X-Api-Key: your-api-key
```

Header kustom dari `endpoint.headers` Anda selalu disertakan
secara verbatim, ditambah dua header dengan namespace ThunderPhone:

* `X-ThunderPhone-Signature` — HMAC-SHA256 dari byte body permintaan
  yang tepat, menggunakan **secret webhook organisasi** Anda sebagai kunci
* `X-ThunderPhone-Call-ID` — ID panggilan saat ini

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

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

### Body Permintaan

Untuk `POST` / `PUT` / `PATCH`, body hanya berisi argumen tool
(tanpa wrapper), yang diserialisasi secara kanonis (kunci diurutkan, pemisah
ringkas):

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

Untuk `GET` / `DELETE`, argumen dikirim sebagai **parameter kueri**
dan body kosong — tanda tangan kemudian dihitung atas string byte kosong.
Lihat
[Verifikasi tanda tangan webhook](/id/guides/verify-webhook-signatures).

### Respons

Kembalikan respons JSON dengan hasil tool:

```json theme={null}
{
  "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>"}`;
timeout dan kegagalan koneksi dilaporkan kepada AI sebagai error, sehingga
agen dapat meminta maaf dan melanjutkan alih-alih terhenti.

## Pengiriman mode webhook

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

```json theme={null}
{
  "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`. Respons dengan hasil tool sebagai JSON — kontrak responsnya
sama seperti panggilan endpoint langsung. Permintaan ditandatangani dengan
secret webhook organisasi atas body mentah, seperti setiap webhook lainnya.

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

***

## Verifikasi Tanda Tangan

Panggilan alat langsung ditandatangani dengan cara yang sama seperti webhook:

* HMAC-SHA256 atas byte isi permintaan yang persis sama (JSON kanonis —
  kunci diurutkan, tanpa spasi tambahan)
* Menggunakan secret webhook organisasi Anda sebagai kunci
* Alat `GET` / `DELETE` menandatangani string byte kosong

<CodeGroup>
  ```python Python theme={null}
  import hmac
  import hashlib

  def verify_tool_call(body: bytes, signature: str, secret: str) -> bool:
      expected = hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
      return hmac.compare_digest(expected, signature)

  @app.post("/appointments/search")
  async def search_appointments(request: Request):
      body = await request.body()
      signature = request.headers.get("X-ThunderPhone-Signature", "")

      if not verify_tool_call(body, signature, WEBHOOK_SECRET):
          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 theme={null}
  app.post('/appointments/search', express.raw({type: 'application/json'}), (req, res) => {
    const signature = req.headers['x-thunderphone-signature'] || '';
    const expected = crypto
      .createHmac('sha256', WEBHOOK_SECRET)
      .update(req.body)
      .digest('hex');

    if (!signature ||
        signature.length !== expected.length ||
        !crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature))) {
      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>

Resep lengkap — termasuk kasus isi kosong dan catatan tanpa secret —
tersedia di [Verifikasi tanda tangan webhook](/id/guides/verify-webhook-signatures).

***

## Contoh: Alur Pemesanan Lengkap

Berikut adalah sekumpulan alat untuk sistem pemesanan janji temu lengkap:

```json theme={null}
{
  "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 tool. Jelaskan secara spesifik fungsi tool tersebut dan kapan tool 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 kepada pengguna sebelum memanggil tool.
  </Accordion>
</AccordionGroup>

***

## Terkait

<CardGroup cols={2}>
  <Card title="Koneksi aplikasi" icon="plug" href="/id/guides/connect-apps">
    Tool yang dikelola Platform untuk HubSpot, Salesforce, Slack, Google
    Calendar, Google Sheets, dan Cal.com — tidak memerlukan endpoint.
  </Card>

  <Card title="Server MCP" icon="server" href="/id/guides/mcp-servers">
    Hubungkan server MCP dan biarkan agen memanggil tool-nya.
  </Card>

  <Card title="Koneksi API" icon="code" href="/id/guides/api-connections">
    Integrasi REST yang dapat digunakan kembali dan dapat Anda hubungkan ke agen.
  </Card>

  <Card title="Verifikasi signature webhook" icon="shield-check" href="/id/guides/verify-webhook-signatures">
    Satu helper verifikasi untuk webhook dan pemanggilan tool.
  </Card>
</CardGroup>
