# 連携

> 通話中の AI に予定表や顧客の台帳を読み書きさせ、通話のあとに Slack などへ知らせる連携（コネクター）の使い方。プリセットの一覧、カスタムの連携、事業者向けの埋め込みの連携の画面。

Source: https://voicast.jp/docs/connectors

連携（コネクター）を使うと、通話中の AI が Google カレンダーの空きを調べて予約を入れたり、kintone で電話番号から顧客を探したりできます。通話のあとには、要約と聞き取った項目を Slack や Teams に知らせます。[通話中の道具](https://voicast.jp/docs/tools.md)と違い、自社で API を作らずに使えます。

## 連携の持ち方

連携は 2 通りの持ち方ができます。

| 持ち方 | 使いどころ | つなぎ方 |
| --- | --- | --- |
| テナントの連携 | 事業者（テナント）ごとに、自分の予定表や Slack をつなぐ | [埋め込みの連携の画面](#埋め込みの連携の画面)を事業者が開いてつなぐ |
| 環境の連携 | サービス全体で 1 つの予約システムや通知先を使う | 管理画面の「連携」でつなぐ |

通話のときは、テナントの連携を先に使います。テナントの連携があれば（もう一度連携が要る状態でも）、環境の連携には切り替えません。

鍵（OAuth のトークン・API キー・Webhook の URL）は voicast が暗号化して持ちます。API の応答や音声サーバーには出しません。

## プリセット

| コネクター | 名前 | 種類 | つなぎ方 | AI が使える操作 |
| --- | --- | --- | --- | --- |
| `google_calendar` | Google カレンダー | 予約・予定 | Google のログイン | `find_free_slots`・`create_event`・`cancel_event`・`find_events_by_phone_or_name` |
| `m365_calendar` | Microsoft 365 のカレンダー | 予約・予定 | Microsoft のログイン | Google カレンダーと同じ 4 つ |
| `google_sheets` | Google スプレッドシート | 顧客の台帳 | Google のログイン（台帳の表を作ります） | `find_row_by_phone`・`append_row` |
| `kintone` | kintone | 顧客の台帳 | kintone の OAuth（お客さまのドメインで登録したクライアント） | `find_record_by_phone`・`create_record`・`update_record` |
| `hubspot` | HubSpot | 顧客の台帳 | HubSpot のログイン | `find_contact_by_phone`・`create_contact`・`log_call` |
| `slack` | Slack | 通知 | Slack のログイン（チャンネルを選ぶ） | 通知だけ |
| `chatwork` | Chatwork | 通知 | Chatwork のログイン（ルームを選ぶ） | 通知だけ |
| `lineworks` | LINE WORKS | 通知 | LINE WORKS の OAuth（お客さまの Developer Console で作ったアプリと Bot） | 通知だけ |
| `teams` | Microsoft Teams | 通知 | Teams のワークフローの Webhook の URL を貼る | 通知だけ |
| `shannon_booking` | シャノンの予約システム | 予約・予定 | 呼び先の URL と API キー（環境の連携だけ） | `get_clinic_info`・`find_free_slots`・`find_patient`・`list_reservations`・`create_reservation`・`change_reservation`・`cancel_reservation` |

`GET /v1/connectors`（MCP は `list_connectors`）で、操作の説明・つなぐ前に聞く項目・AI に見せる道具の名前（`gcal_find_free_slots` など）を確かめられます。`available: false` の連携先は、まだ使えません。

### 操作の中身

- **Google カレンダー・Microsoft 365**: `find_free_slots { date_from, date_to?, duration_minutes? }` は、受付の時間・前後の余白・埋まっている時間を考えて空きを 8 件まで返します（14 日先まで）。`create_event { start, name, phone?, note?, duration_minutes? }`・`cancel_event { event_id }`・`find_events_by_phone_or_name { phone?, name? }`（電話番号を省くと、かけてきた番号）。つないだあとに、使うカレンダー・予約の長さ（5〜480 分、既定 30）・前後の余白・曜日ごとの受付の時間・予定の題名を決めます。予定の題名には病名などを入れず、用件は予定のメモにだけ入れます。
- **Google スプレッドシート**: `find_row_by_phone { phone? }`・`append_row { name?, phone?, note? }`。使う表と、電話番号・名前・メモ・日時の列を決めます。
- **kintone**: `find_record_by_phone { phone? }`・`create_record { name?, phone?, note? }`・`update_record { record_id, name?, note? }`。使うアプリと、電話番号・名前・メモのフィールドを決めます。
- **HubSpot**: `find_contact_by_phone { phone? }`・`create_contact { name, phone? }`・`log_call { summary, contact_id? }`（受けた電話として記録します）。
- **通知（Slack・Chatwork・LINE WORKS・Teams）**: 通話のあと（要約ができたあと）に、要約・用件の分類・聞き取った項目・管理画面へのリンクを送ります。失敗した通知は送り直し、ほかの通知は止めません。

## 会話の設定に書く

連携を AI に使わせるには、会話の設定の `connectors` に並べます。

```json
"connectors": [
  { "connector": "google_calendar", "actions": ["find_free_slots", "create_event", "cancel_event"] },
  { "connector": "slack" },
  { "connection": "con_…", "actions": ["*"] }
]
```

| 項目 | 内容 |
| --- | --- |
| `connector` | プリセットの名前。テナントの連携を使い、無ければ環境の連携を使います |
| `connection` | 決まった連携の ID（`con_…`）。カスタム（MCP サーバー・URL と API キー）はこちらで指定します |
| `actions` | 使う操作の名前か `"*"`（すべて）。通知だけの連携先は書きません |
| `confirm` | 確認してから使う操作を足す（読み取りの操作に足すためのもの） |

- 予約を作る・取り消すなど書き換える操作は、いつも内容を読み上げて相手の同意を得てから使います（外せません）。
- 使える状態（`active`）の連携の操作だけが、通話のときに AI に渡ります。
- 同じ連携先は 1 回だけ、`connectors` は 10 個までです。AI に見せる道具の名前（`gcal_create_event` など）が `tools` の名前と重なると 422 です。

## 埋め込みの連携の画面

事業者（テナント）が自分の Google カレンダーや Slack をつなぐ画面です。voicast の名前は出さないので、サービスの画面の一部として見せられます。

### リンクの作り方

```bash
curl https://api.voicast.jp/v1/tenants/ten_…/connect-links \
  -H "Authorization: Bearer $VOICAST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "connectors": ["google_calendar", "slack"], "frame_origin": "https://app.example.com" }'
```

```json
{ "url": "https://app.voicast.jp/connect/vcl_…", "expires_at": 1790000900000 }
```

| 入力 | 内容 |
| --- | --- |
| `connectors` | 画面に出す連携先（省くとすべて）。`shannon_booking` とカスタムは出せません |
| `frame_origin` | iframe で埋め込む親のオリジン（https）。新しいタブで開くなら要りません |

- リンクは 15 分で切れます。事業者が設定の画面を開くたびに、サーバーで作り直します。
- 言語は `url` に `?lang=ja` か `?lang=en` を付けて選べます（無ければブラウザの言語）。
- ログインは要りません。リンクのトークンで、そのテナントの連携だけを扱えます。

### 埋め込み

```html
<iframe src="https://app.voicast.jp/connect/vcl_…?lang=ja" title="連携" style="width: 100%; height: 720px; border: 0"></iframe>
```

「連携する」を押すと、連携先のログインの画面が小さな別の窓で開きます（Google などは iframe の中にログインの画面を出せないため）。終わると、元の画面に `postMessage` で知らせます。

```js
window.addEventListener('message', (e) => {
  if (e.origin !== 'https://app.voicast.jp' || e.data?.type !== 'voicast.connect') return;
  // { type: 'voicast.connect', result: 'ok' | 'error', error, connector, connection }
  if (e.data.result === 'ok') refreshConnections();
});
```

Google カレンダーなど設定の要る連携先は、つないだあとに同じ画面で、使うカレンダーや受付の時間を選びます。設定が済むまでは `needs_setup` で、AI には渡りません。

## API キーや URL でつなぐ連携先

Teams・シャノンの予約システム・カスタムの「URL と API キー」は、`POST /v1/connections` でつなげます。保存の前に 1 回試しに呼び、失敗すると 422 `verify_failed` です。

```bash
curl https://api.voicast.jp/v1/connections \
  -H "Authorization: Bearer $VOICAST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "connector": "teams", "tenant_id": "ten_…", "secret": "https://….logic.azure.com/workflows/…" }'
```

## カスタムの連携

プリセットに無い相手は、管理画面の「連携」→「＋ API でつなぐ」でつなぎます。環境の連携だけで、いくつでもつなげます（シャノンの予約システムも環境の連携だけです。`tenant_id` を付けてつなごうとすると 422 `invalid_request` になります。テナントごとの振り分けは、呼び出しに付く `tenant`（`external_id`）で行います）。会話の設定では `{ "connection": "con_…", "actions": [...] }` で指定し、AI に見せる道具の名前には連携ごとの頭（`mcp1_…`・`api2_…`）が付きます。

### URL と API キー（http）

自社や他社の REST API を、URL と API キーで呼びます。

```bash
curl https://api.voicast.jp/v1/connections \
  -H "Authorization: Bearer $VOICAST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "connector": "http", "secret": "<API キー>", "input": { "base_url": "https://api.example.com", "header": "Authorization" } }'
```

- `header` が `Authorization` なら `Bearer <キー>` を、ほかの見出し（`X-API-Key` など）ならキーをそのまま付けます。
- 保存すると設定待ちになり、道具（10 個まで）を `PATCH /v1/connections/{id}` で保存すると使えます。

```json
{
  "config": {
    "tools": [
      {
        "name": "find_customer",
        "description": "電話番号で顧客を探す",
        "method": "GET",
        "path": "/customers/search",
        "params": [{ "name": "phone", "description": "電話番号（数字だけ）", "required": true }],
        "confirm": false
      }
    ]
  }
}
```

- 引数はいつも文字列で、GET はクエリ、POST は JSON の本文で送ります（10 個まで）。`path` はつないだ URL の下だけです。
- 呼び出しには `idempotency-key`（通話と呼び出しごとに決まる値）が付きます。
- 応答は JSON ならそのまま、ほかは文字で AI に渡します（長い文字は切ります）。

### MCP サーバー（mcp）

リモートの MCP サーバー（Streamable HTTP）で、MCP の認可（OAuth）に対応したものをつなげます。管理画面で URL を入れると、相手のログインの画面が開きます。つないだあと、AI に使わせる道具と「確認してから使う」を選びます（読むだけと宣言された道具のほかは、既定で確認します）。

- ログインなしで使えるサーバー・手元（ローカル）の MCP サーバーはつなげません。
- 引数に文字列・数・真偽のほかの形が必須の道具は選べません。

### 呼んでよい URL

連携から呼ぶ URL（MCP サーバー・HTTP・予約システム・Teams）は https だけです。IP アドレスの直書き・`localhost`・`.local`・`.internal`・点の無い名前・利用者名とパスワード入りの URL は受けません。別の URL へ移す応答（3xx）には付いて行きません。

## 連携の状態

| `status` | 内容 |
| --- | --- |
| `active` | 使える |
| `needs_setup` | つないだが、設定（カレンダーの選択など）がまだ。AI には渡らない |
| `needs_reauth` | トークンが失効した。もう一度連携が要る |

連携が切れると、Webhook の `connection.needs_reauth` が届きます。テナントの連携なら `tenant` が付くので、その事業者に新しい連携の画面のリンクを出して、つなぎ直してもらいます。

テナントを消すと、そのテナントの連携も取り消します。連携を解除する（`DELETE /v1/connections/{id}`）と、連携先のトークンも取り消します。
