---
title: "ツール連携（API）を構築"
description: "会話の途中でエージェントからAPIを呼び出せます。データベースの検索、チケットの作成、注文の照会が可能です。"
---

**ツール統合**は、エージェントが通話中に呼び出せる再利用可能な HTTP エンドポイントです。ThunderPhone にツールの JSON スキーマ記述とエンドポイント URL を渡すと、エージェントは会話に基づいて呼び出すタイミングを判断し、ThunderPhone がそのサーバーから送信 HTTP リクエストを実行して、応答をエージェントに返します。

<Note>
  この API を使わなくても、ダッシュボードでほとんどのツール要件に対応できます。**接続
  → アプリ**では、数回の OAuth クリックで Slack、HubSpot、Salesforce、Google Calendar、
  Google Sheets、Cal.com を接続できます。**接続 →
  API**では、任意の HTTP API をエージェントアクションに変換できます。cURL コマンドを貼り付けると、
  AI ウィザードがツールを下書きし、組み込みのリクエストテストも利用できます。**接続 → MCP**では
  MCP サーバーを追加できます。[接続](/ja/guides/concepts)を参照してください。このガイドでは、
  API 画面の基盤となる生の API を扱います。
</Note>

このガイドでは、天気検索ツールをエンドツーエンドで構築します。

## ツールの構成

2 つの要素があります。

1. **スキーマ** — ツールの機能と受け取る引数を LLM に伝える、OpenAI 形式の関数定義
   (`{type: "function", function: {name, description, parameters}}`)です。
2. **エンドポイント** — LLM がツールを使用すると判断したときに、ThunderPhone のサーバーが呼び出す
   URL です。リクエストは JSON POST で、本文には LLM が選択した引数が含まれます。

## 1. エディターを選択

<CardGroup cols={2}>
  <Card title="ダッシュボード" icon="window-maximize">
    **接続 → API**を開き、API 接続を作成または編集して、パラメーターエディターを **JSON** に切り替え、
    そこに形式を追加します。
  </Card>
  <Card title="統合 API" icon="plug">
    `POST /v1/integrations` で仕様を作成するか、
    `PATCH /v1/integrations/{id}` で更新します。
  </Card>
</CardGroup>

どちらの方法でも保存済みの統合が作成されます。保存後、その統合をエージェントに関連付けます。
Agents API には書き込み可能なインライン `tools` フィールドはありません。このガイドでは統合 API の方法を使用します。

## 2. 連携を作成する

```bash
curl -X POST https://api.thunderphone.com/v1/integrations \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "display_name": "Weather API",
    "spec": {
      "type": "function",
      "function": {
        "name": "get_weather",
        "description": "Return the current weather for a zip code.",
        "parameters": {
          "type": "object",
          "properties": {
            "zip": { "type": "string", "description": "5-digit US ZIP code" }
          },
          "required": ["zip"]
        }
      }
    },
    "endpoint_url":    "https://api.example.com/weather",
    "endpoint_method": "GET",
    "headers": [
      { "key": "X-Api-Key", "value": "your-provider-key" }
    ]
  }'
```

返された `id`（UUID）を保存します。ヘッダー値は書き込み専用です。レスポンスにはヘッダー名のみが表示され、後続の `PATCH` では `value` なしでヘッダーを送信することで、保存済みの値を維持できます。

<Tip>
  ツールおよび各パラメータの `description` は慎重に記述してください。LLM は実行時にこれらの文字列を使用して、ツールを呼び出すかどうかと呼び出し方法を判断します。曖昧な説明は、曖昧なツール呼び出しにつながります。
</Tip>

### アドレスパラメータに `format: "email"` を宣言する

メールアドレスを受け取るパラメータでは、スキーマにそのことを明記します。

```json
"email": { "type": "string", "format": "email", "description": "The caller's email address" }
```

`format` は単なるヒントではありません。解決済みのメールスキーマでは、エンドポイントを呼び出す前に ThunderPhone が値の前後の空白を削除し、ドメインを小文字に変換し、独立した英単語 `at`、`dot`、`underscore`、`dash`、`hyphen` を対応する文字に変換します。また、`@`、`.`、`_`、`-` の直前と直後の空白を削除します。文字起こしにリテラルの `@` がすでに含まれているかどうかにかかわらず、これらの単語の意味は同じです。`"john dot smith at gmail dot com"` は `john.smith@gmail.com` になります。

その他の内部空白は、暗黙的に結合されるのではなく拒否されます。音声による区切り単語は英語にのみ対応しており、英語以外または認識されない空白区切りの形式は安全側に倒して失敗します。有効な国際化ドメインおよび SMTPUTF8 ローカルパートは受け入れられます。パーサーによる正規化後も、Punycode 入力は Punycode のまま、Unicode ドメイン入力は Unicode のまま維持されるため、API は発信者が指定した一般的な表現を受け取ります。最終的な値が無効な場合、ツールは**呼び出されません**。エージェントは `invalid_email_argument` を受け取り、発信者にスペルを確認してリテラルのアドレスを再送するよう指示されます。

省略された任意のメールは変更されません。プロパティが任意または nullable の場合、`null`、空文字列、空白のみの文字列も変更されません。同じ値でも、必須かつ非 nullable のメールでは拒否されます。

`#/$defs/email` や `#/definitions/email` などのローカルスキーマ参照に加え、`anyOf`、`oneOf`、`allOf` も、循環および深さの制限付きで検査されます。非ローカルまたは解決不能な `$ref` は既知の適用制限であり、使用可能なスキーマがツールスナップショットにない呼び出しと同様に、変更されずに渡されます。ゲートを適用する必要がある場合は、メールスキーマをローカルに保持してください。

強制されるメール形式を持たないパラメータは、モデルが生成したとおりにそのまま渡されます。

対応する形式は `date-time`、`time`、`date`、`duration`、`email`、`hostname`、`ipv4`、`ipv6`、`uuid` です。現時点で正規化および強制されるのは `email` のみです。

## 3. エンドポイントをサンドボックスでテストする

連携をエージェントにリンクする前に、ThunderPhone のサーバーから署名付きリクエストを送信して接続性を確認します。

```bash
curl -X POST https://api.thunderphone.com/v1/integrations/test-request \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url":    "https://api.example.com/weather?zip=94110",
    "method": "GET",
    "headers": { "X-Api-Key": "your-provider-key" }
  }'
```

```json Response
{
  "ok": true,
  "status": 200,
  "elapsed_ms": 187,
  "response_headers": { "content-type": "application/json" },
  "response_preview": "{\"temperature_f\": 64, ...}"
}
```

このテストは ThunderPhone の SSRF 保護も強化します。localhost またはプライベート IP 範囲へのリクエストは `400 code=url_not_allowed` を返します。

## 4. エージェントに連携をリンクする

エージェントを作成または更新するときに、`integration_ids` を使用して紐付けます。

```bash
curl -X PATCH https://api.thunderphone.com/v1/agents/12 \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "integration_ids": ["f9b5a1a4-..."]
  }'
```

1つのエージェントに複数の連携をリンクできます。エージェントのプロンプトから名前で参照できます。たとえば、「発信者が天候について尋ねたときは `get_weather` を使用する」のように指定します。スキーマの説明から暗黙的に検出させることもできます。

## 5. エンドポイントを実装する

エージェントがツールを呼び出すと、ThunderPhone は設定した `endpoint_url` に署名付き POST リクエストを送信します。

```
POST /weather HTTP/1.1
Host: api.example.com
X-Api-Key: your-provider-key
X-ThunderPhone-Signature-V2: t=<unix seconds>,v1=<HMAC-SHA256 hex>
X-ThunderPhone-Signature: <HMAC-SHA256 hex>
X-ThunderPhone-Call-ID: 987654321
Content-Type: application/json

{"zip": "94110"}
```

サーバーは、LLM に返される JSON を応答として返します。

```json
{"temperature_f": 64, "condition": "Partly cloudy", "wind_mph": 8}
```

LLM はその応答を取り込み、発信者に自然な要約を読み上げます。

<Warning>
  署名は、Webhook エンドポイントと同じ `secret` を使用して、生のリクエスト本文に対して計算されます。**必ず検証してください**。ツールエンドポイントはインターネットに公開されており、Webhook と同じなりすましリスクの対象です。詳細は
  [Webhook 署名を検証する](/ja/guides/verify-webhook-signatures)を参照してください。
</Warning>

## 6. ループをテストする

エージェントに対して[マイクセッション](/api-reference/mic-sessions)を実行し、ツールが処理する質問（「94110の天気は？」）をします。通話の文字起こしには、往復のすべての処理が表示されます。

```json
{
  "call_id": 987654321,
  "transcripts": [
    { "role": "user",
      "content": "What's the weather in 94110?" },
    { "role": "tool_call",
      "content": "{\"tool_call\": \"get_weather\", \"arguments\": {\"zip\": \"94110\"}}" },
    { "role": "tool_response",
      "content": "{\"tool_name\": \"get_weather\", \"response\": {\"temperature_f\": 64, \"condition\": \"Partly cloudy\"}}" },
    { "role": "agent",
      "content": "It's 64 degrees and partly cloudy." }
  ]
}
```

これは[`GET /v1/calls/{call_id}/transcript`](/api-reference/calls#get-transcript)から取得できます。エントリごとのタイミングと音声オフセットを含む生イベントストリームは、[`GET /v1/calls/{call_id}/history`](/api-reference/calls#get-history)で確認できます。

通話一覧および分析画面では、ThunderPhoneはテスト通話を実際の通話トラフィックと分けて報告します。Browser Talkのテストとシミュレーションでは実際の電話番号を再利用でき、着信のように見える場合がありますが、本番通話数、評価、または問題指標には含まれません。

## よくある注意点

<AccordionGroup>
  <Accordion title="エージェントがツールを呼び出さない">
    LLM はツールの説明に基づいて判断します。発信者の質問が説明と一致しない場合、モデルはツールを呼び出しません。説明を具体化して一般的な同義語や表現を追加するか、エージェントのプロンプトで明示してください（「発信者が天気について尋ねたら、`get_weather` を使用する。」）。
  </Accordion>

  <Accordion title="ツールが返すデータ量が多すぎる">
    6 kB を超える応答は、文字起こしプレビューで切り詰められます。レコード全体ではなく、LLM が必要とするフィールドだけを返してください。
  </Accordion>

  <Accordion title="タイムアウト">
    ツールエンドポイントのデフォルトのタイムアウトは10秒です。より長い時間が必要な場合は、非同期で処理してください。`{"status": "pending", "request_id": "..."}` を返し、別のツール呼び出しで結果を取得します。
  </Accordion>

  <Accordion title="バージョニング">
    連携に対するすべての `PATCH` は新しいリビジョンを作成します。[`GET /v1/integrations/{id}/versions`](/api-reference/integrations#version-history)を確認すると、誰が何を変更したかを確認できます。ツールのスキーマを破損した場合は、古いスナップショットを手動で PATCH し直してロールバックできます。
  </Accordion>
</AccordionGroup>

---

## 次のステップ

<CardGroup cols={2}>
  <Card title="連携リファレンス" icon="plug" href="/api-reference/integrations">
    CRUD、転送、バージョン履歴。
  </Card>
  <Card title="Function Tools 仕様" icon="screwdriver-wrench" href="/ja/tools/overview">
    完全な JSON スキーマ文法と署名付きエンドポイントの契約。
  </Card>
  <Card title="署名を検証" icon="shield-check" href="/ja/guides/verify-webhook-signatures">
    Webhook 署名パターンをツールエンドポイントに適用。
  </Card>
  <Card title="文字起こし + 履歴 API" icon="phone" href="/api-reference/calls">
    ツール呼び出しの完全な往復を確認。
  </Card>
</CardGroup>
