# API リファレンス

> voicast の API（/v1）と音声の接続の一覧。認証・形式・各エンドポイントの入力と返り・エラー。

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

## 基本

- **URL**: `https://api.voicast.jp/v1`
- **認証**: `Authorization: Bearer vk_live_…` か `vk_test_…`。キーの環境（本番かテスト）の中だけを読み書きします
- **形式**: JSON。時刻は Unix ミリ秒、ID は接頭辞付きの文字列（`agt_`・`ten_`・`call_`・`sess_`・`con_`）
- **エラー**: `{ error, message, details?, request_id }`。種類は[エラーと制限](https://voicast.jp/docs/errors.md)
- **呼び出しの ID**: すべての応答に `x-request-id`（`req_…`）が付きます。エラーの本文の `request_id` と同じです。問い合わせのときに伝えてください
- **回数の上限**: API キーごとに 1 分 600 回。応答の `x-ratelimit-limit`・`x-ratelimit-remaining`・`x-ratelimit-reset`（枠の終わり、Unix 秒）で残りが分かり、超えると 429 と `Retry-After`（秒）

API キーは管理画面の「API キー」で作ります（管理者以上）。サービスのサーバーだけに置き、ブラウザやアプリには渡しません。

## エンドポイント

| メソッド | パス | 内容 |
| --- | --- | --- |
| POST | [`/v1/agents`](#会話の設定の作成) | 会話の設定の作成 |
| GET | [`/v1/agents`](#会話の設定の一覧) | 会話の設定の一覧 |
| GET | [`/v1/agents/{id}`](#会話の設定の取得) | 会話の設定の取得 |
| PUT | [`/v1/agents/{id}`](#会話の設定の変更) | 会話の設定の変更（新しい版） |
| GET | [`/v1/agents/{id}/versions`](#会話の設定の版の一覧) | 版の一覧 |
| DELETE | [`/v1/agents/{id}`](#会話の設定の削除) | 会話の設定の削除 |
| POST | [`/v1/tenants`](#テナントの作成) | テナントの作成 |
| GET | [`/v1/tenants`](#テナントの一覧) | テナントの一覧 |
| GET | [`/v1/tenants/{id}`](#テナントの取得) | テナントの取得 |
| PATCH | [`/v1/tenants/{id}`](#テナントの変更) | テナントの変更 |
| POST | [`/v1/tenants/{id}/secret`](#接続の鍵の作り直し) | 接続の鍵の作り直し |
| DELETE | [`/v1/tenants/{id}`](#テナントの削除) | テナントの削除 |
| GET | [`/v1/calls`](#通話の一覧) | 通話の一覧 |
| GET | [`/v1/calls/{id}`](#通話の取得) | 通話の取得 |
| GET | [`/v1/calls/by-provider/{providerCallId}`](#サービスの側の-id-で通話を引く) | サービスの側の ID で通話を引く |
| GET | [`/v1/calls/{id}/transcript`](#文字起こし) | 文字起こし |
| POST | [`/v1/sessions`](#セッションの作成) | Web・アプリのセッションの作成 |
| GET | [`/v1/usage`](#利用の内訳) | 利用の内訳 |
| POST | [`/v1/webhooks/portal`](#webhook-の設定画面のリンク) | Webhook の設定画面のリンク |
| GET | [`/v1/billing`](#請求の状態) | 請求の状態（残高・オートチャージ・1 分の値段・支払い方法） |
| POST | [`/v1/billing/topups`](#チャージ) | チャージの URL |
| GET | [`/v1/tools/secret`](#道具の署名の鍵) | 通話中の道具の署名の鍵 |
| POST | [`/v1/tools/secret/rotate`](#道具の署名の鍵の作り直し) | 署名の鍵の作り直し |
| GET | [`/v1/connectors`](#連携先の一覧) | 連携先の一覧 |
| GET | [`/v1/connections`](#連携の一覧) | 連携の一覧 |
| POST | [`/v1/connections`](#連携の作成api-キーurl) | 連携の作成（API キー・URL） |
| GET | [`/v1/connections/{id}`](#連携の取得) | 連携の取得 |
| PATCH | [`/v1/connections/{id}`](#連携の設定) | 連携の設定 |
| GET | [`/v1/connections/{id}/options`](#連携の設定の選択肢) | 連携の設定の選択肢 |
| DELETE | [`/v1/connections/{id}`](#連携の解除) | 連携の解除 |
| GET | [`/v1/tenants/{id}/connections`](#テナントの連携の一覧) | テナントの連携の一覧 |
| POST | [`/v1/tenants/{id}/connect-links`](#連携の画面のリンク) | 事業者がつなぐ画面のリンク |

音声の接続（API キーは使いません）:

| 方式 | URL | 内容 |
| --- | --- | --- |
| WebSocket | [`wss://voice.voicast.jp/ws`](#電話twilio-media-streams) | 電話（Twilio の Media Streams） |
| POST | [`https://voice.voicast.jp/v1/webrtc/offer`](#webrtc-の-offer) | Web・アプリ（WebRTC の offer） |
| PATCH | [`https://voice.voicast.jp/v1/webrtc/offer`](#ice-の候補) | ICE の候補を足す |

MCP（`https://api.voicast.jp/mcp`）は [MCP](https://voicast.jp/docs/mcp.md) を見てください。

---

## 会話の設定の作成

```
POST /v1/agents
```

| 入力 | 型 | 内容 |
| --- | --- | --- |
| `name` | string | 名前（1〜100 文字） |
| `config` | AgentConfig | 会話の設定（[会話の設定](https://voicast.jp/docs/agents.md)） |

返り: 201 `Agent`（`config` 付き、`version: 1`）。誤りは 422 `invalid_config`（`details` に項目ごとの誤り）。

```ts
type Agent = { id: string; name: string; version: number; config?: AgentConfig; created_at: number; updated_at: number };

type AgentConfig = {
  greeting: string;            // 最初のあいさつ（300 文字まで）
  persona: string;             // 役割と話し方（1,000 文字まで）
  fields: { key: string; label: string; required?: boolean; type?: 'text' | 'phone' }[]; // 1〜20 個
  handoff_when: string[];      // 人に引き継ぐ条件（20 個まで）
  closing: string;             // 聞き取りが終わったときの締め（300 文字まで）
  closing_answered?: string;   // よくある質問で用が済んだときの締め
  faq?: { q: string; a: string }[];  // 50 個まで
  domain?: string;             // 業種（音声認識の手がかり）
  vocabulary?: string[];       // 用語（音声認識の手がかり、200 個まで）
  voice?: string;              // 声の名前（既定 Aoede）
  speaking_rate?: number;      // 話す速さ（既定 1.15）
  tools?: AgentTool[];         // 通話中の道具（10 個まで）
  connectors?: ConnectorEntry[]; // 連携（10 個まで）
};

type AgentTool = {
  name: string; description: string; url: string; confirm?: boolean;
  parameters?: { properties: Record<string, { type: 'string' | 'number' | 'integer' | 'boolean'; description?: string; enum?: string[] }>; required?: string[] };
};

type ConnectorEntry =
  | { connector: string; actions: string[]; confirm?: string[] }   // プリセット
  | { connection: string; actions: string[]; confirm?: string[] }; // 決まった連携（con_…）
```

## 会話の設定の一覧

```
GET /v1/agents
```

返り: `{ data: Agent[] }`（`config` なし）。

## 会話の設定の取得

```
GET /v1/agents/{id}?version=
```

`version`（1 以上の整数）を省くと、いまの版です。返り: `Agent`（`config` 付き）。無ければ 404。

## 会話の設定の変更

```
PUT /v1/agents/{id}
```

| 入力 | 型 | 内容 |
| --- | --- | --- |
| `name` | string | 任意。省くといまのまま |
| `config` | AgentConfig | 全体を渡す |

返り: `Agent`（新しい版）。進行中の通話は始まったときの版のまま続きます。

## 会話の設定の版の一覧

```
GET /v1/agents/{id}/versions
```

返り: `{ data: [{ version, created_at }] }`（新しい順）。

## 会話の設定の削除

```
DELETE /v1/agents/{id}
```

返り: 204。使っているテナントがあれば 409 `in_use`。通話の記録は残ります。

## テナントの作成

```
POST /v1/tenants
```

| 入力 | 型 | 内容 |
| --- | --- | --- |
| `external_id` | string | サービスの側の顧客の ID。英数字と `. _ : -` の 1〜100 文字。環境の中で重ならないこと |
| `name` | string | 任意。表示名（100 文字まで） |
| `agent_id` | string | 任意。既定の会話の設定 |

返り: 201 `Tenant` と `secret`（接続の鍵 `vts_…`。このときだけ）。同じ `external_id` があれば 409 `conflict`。

```ts
type Tenant = { id: string; external_id: string; name: string; agent_id: string | null; status: 'active' | 'disabled'; created_at: number };
```

## テナントの一覧

```
GET /v1/tenants
```

返り: `{ data: Tenant[] }`。

## テナントの取得

```
GET /v1/tenants/{id}
```

返り: `Tenant`（接続の鍵は返しません）。

## テナントの変更

```
PATCH /v1/tenants/{id}
```

| 入力 | 型 | 内容 |
| --- | --- | --- |
| `name` | string | 任意 |
| `agent_id` | string \| null | 任意。`null` で外す |
| `status` | `active`・`disabled` | 任意。`disabled` で電話と Web・アプリの接続を止める |

返り: `Tenant`。渡した項目だけが変わります。

## 接続の鍵の作り直し

```
POST /v1/tenants/{id}/secret
```

返り: `{ secret, previous_secret_expires_at }`。古い鍵も 24 時間は通ります。

## テナントの削除

```
DELETE /v1/tenants/{id}
```

返り: 204。接続できなくなり、テナントの連携も取り消します。通話の記録は残ります。同じ `external_id` でまた作れます。

## 通話の一覧

```
GET /v1/calls?tenant_id=&cursor=&limit=
```

| クエリ | 内容 |
| --- | --- |
| `tenant_id` | 任意。テナントで絞る |
| `cursor` | 任意。続きの位置（前の応答の `next_cursor` をそのまま渡す） |
| `limit` | 任意。1〜100（既定 50） |
| `before` | 任意。以前の形のページ送り（この `started_at` より前に始まったもの）。同じミリ秒に始まった通話を飛ばすことがあるので `cursor` を使います。`cursor` と一緒には使えません（422） |

返り: `{ data: Call[], has_more, next_cursor }`（始まった時刻の新しい順）。`has_more` が `true` のあいだ、`next_cursor` を `cursor` に渡して続きを読みます（`has_more` が `false` なら `next_cursor` は `null`）。

```ts
type Call = {
  id: string; tenant_id: string; agent: { id: string; version: number };
  provider_call_id: string | null;   // Twilio の CallSid、Web・アプリはセッションの ID
  from: string | null;               // かけてきた番号（電話で from を渡したとき）
  started_at: number; ended_at: number | null; duration_ms: number | null;
  outcome: 'completed' | 'answered' | 'handoff' | 'hangup' | 'error' | null; reason: string | null;
  fields: Record<string, string> | null;
  summary: string | null; category: string | null; has_transcript: boolean;
  channel: 'phone' | 'web'; output: 'audio' | 'text';
  billed_yen: number | null;         // この通話の利用料（税別の円、小数 3 桁まで）。通話中は null
};
```

`billed_yen` は、チャージ式なら残高から引いた額（秒数 × 1 分の値段 ÷ 60）、請求書払いなら秒数 × 契約の 1 分の値段 ÷ 60 です。請求書は月の合計秒数を分に切り上げるので、通話ごとの額の合計とは少しずれます（[料金と請求](https://voicast.jp/docs/pricing.md)）。

## 通話の取得

```
GET /v1/calls/{id}
```

返り: `Call`。

## サービスの側の ID で通話を引く

```
GET /v1/calls/by-provider/{providerCallId}
```

Twilio の `CallSid`（`CA…`）か、セッションの ID（`sess_…`）で引きます。Twilio の `<Connect>` の `action` で結果を見るのに使います。返り: `Call`。記録が無ければ 404。

## 文字起こし

```
GET /v1/calls/{id}/transcript
```

返り: `{ call_id, turns: [{ role: 'assistant' | 'user', text }] }`。通話が終わってから作られます。無ければ 404。

## セッションの作成

```
POST /v1/sessions
```

| 入力 | 型 | 内容 |
| --- | --- | --- |
| `tenant_id` | string | テナントの ID |
| `agent_id` | string | 任意。省くとテナントの既定 |
| `output` | `audio`・`text` | 任意。既定 `audio` |

返り: 201 `{ id, tenant_id, agent_id, output, client_token, expires_at, webrtc_url, ice_servers }`。`client_token`（`vct_…`）は 2 分・1 回だけ。`ice_servers`（`[{ urls, username?, credential? }]`）は `RTCPeerConnection` の `iceServers` にそのまま渡します（STUN と、使えるときは期限の短い TURN）。`client_token`・`webrtc_url`・`ice_servers` だけをブラウザやアプリに渡します。

エラー: 402（`balance_depleted`・`payment_overdue`・`spending_cap_reached`）、409 `tenant_disabled`、422 `no_agent`・`invalid_request`。

## 利用の内訳

```
GET /v1/usage?from=&to=&group_by=
```

| クエリ | 内容 |
| --- | --- |
| `from`・`to` | 日付（`YYYY-MM-DD`、日本時間。`to` はその日を含む）か Unix ミリ秒（`to` はその時刻を含まない）。省くと今月の始まりから今まで。366 日まで |
| `group_by` | 任意。`tenant`・`day`・`channel` |

返り:

```ts
type Usage = {
  environment: 'live' | 'test'; from: number; to: number;
  group_by: 'tenant' | 'day' | 'channel' | null; unit_price_yen: number;
  total: { calls: number; seconds: number; minutes: number; amount_yen: number };
  data: (({ tenant: { id: string; external_id: string; name: string } } | { day: string } | { channel: 'phone' | 'web' })
        & { calls: number; seconds: number; minutes: number; amount_yen: number })[];  // group_by が無ければ []
  note: string;
};
```

数えるのは期間内に終わった通話です。`amount_yen` は税別の目安です（[料金と請求](https://voicast.jp/docs/pricing.md#利用の内訳)）。

## Webhook の設定画面のリンク

```
POST /v1/webhooks/portal
```

| 入力 | 型 | 内容 |
| --- | --- | --- |
| `frame_origin` | string | 任意。iframe で埋め込む親のオリジン |
| `locale` | `ja`・`en` | 任意。画面の言葉（既定 `ja`） |

返り: 201 `{ url, expires_at }`（15 分有効）。送り先の URL・受け取るイベント・署名の鍵を登録する画面です（[結果の受け取り](https://voicast.jp/docs/results.md#webhook)）。Webhook の送信が使えないときは 503 `unavailable`。

## 請求の状態

```
GET /v1/billing
```

API キーの組織の請求の状態を返します（本番とテストの環境で共通）。管理画面の「請求」と同じ内容です。

```ts
type Billing = {
  organization_id: string; month: string;            // 今月（日本時間、YYYY-MM）
  contract: { payment_method: 'card' | 'invoice'; per_minute_yen: number; invoice_days_until_due: number };
  has_card: boolean;                                   // オートチャージに使うカードを保存したか
  usage: { calls: number; seconds: number; minutes: number };  // 今月の通話（本番とテストの合計）
  estimate_yen: number;                                // チャージ式は今月に引いた額、請求書払いは見込み
  balance: { yen: number; milli_yen: number; depleted: boolean; next_expiry: { yen: number; at: number } | null } | null;  // チャージ式だけ
  auto_recharge: { enabled: boolean; threshold_yen: number; amount_yen: number; monthly_cap_yen: number | null;
                   recharged_this_month_yen: number; failures: number; max_failures: number } | null;  // チャージ式だけ
  topup: { min_yen: number; max_yen: number; quick_yen: number[]; credit_months: number };
  spending_cap_yen: number | null; cap_reached: boolean;
  payment: { failed_at: number; grace_ends_at: number; suspended: boolean } | null;  // 請求書払いの支払いの遅れ
  tax: 'excluded'; tax_rate: number;
  // ほかに price・stripe_enabled・subscription_status・threshold
};
```

## チャージ

```
POST /v1/billing/topups
```

| 入力 | 型 | 内容 |
| --- | --- | --- |
| `amount_yen` | integer | チャージの金額（税別の円、500〜300,000） |

返り: 201 `{ url, amount_yen, tax_yen }`。`url` は Stripe の支払いの画面です。利用者がブラウザで開いて払うと、残高に足されます。範囲の外は 422、請求書払いの組織は 409 `invoice_billing`。オートチャージ・月の上限・カードの変更は管理画面で行います（[料金と請求](https://voicast.jp/docs/pricing.md)）。

## 道具の署名の鍵

```
GET /v1/tools/secret
```

返り: `{ secret, previous_secret_expires_at }`。`secret`（`whsec_…`）は環境の[通話中の道具](https://voicast.jp/docs/tools.md)の呼び出しに付く署名の鍵です。`previous_secret_expires_at` は作り直したあと古い鍵も並べる期限（無ければ `null`）。

## 道具の署名の鍵の作り直し

```
POST /v1/tools/secret/rotate
```

返り: `{ secret, previous_secret_expires_at }`。作り直してから 24 時間は、古い鍵の署名も並べて送ります。そのあいだに自社の側の鍵を差し替えます。

## 連携先の一覧

```
GET /v1/connectors
```

返り: `{ data: Connector[] }`。

```ts
type Connector = {
  id: string; name: string; description: string;
  category: 'scheduling' | 'crm' | 'notify' | 'custom'; auth: 'oauth2' | 'api_key' | 'webhook_url';
  available: boolean;      // この voicast で使える
  needs_setup: boolean;    // つないだあとに設定が要る
  notify: boolean;         // 通話のあとに通知する
  env_only: boolean;       // 環境の連携だけ（埋め込みの連携の画面に出ない）
  inputs: { name: string; type: 'text' | 'password' | 'url' | 'number' }[];
  actions: { name: string; tool: string; description: string; confirm: boolean }[]; // tool は AI に見せる名前
};
```

## 連携の一覧

```
GET /v1/connections?tenant_id=
```

`tenant_id` を省くとすべて、空（`?tenant_id=`）なら環境の連携だけ、ID ならそのテナントの連携だけ。解除したものは出しません。返り: `{ data: Connection[] }`。

```ts
type Connection = {
  id: string; connector: string; tenant_id: string | null;
  label: string;           // 相手のアカウント名（clinic@example.com・ワークスペース #チャンネル など）
  status: 'active' | 'needs_setup' | 'needs_reauth';
  config: object; created_by: string | null; created_at: number; updated_at: number;
};
```

## 連携の作成（API キー・URL）

```
POST /v1/connections
```

| 入力 | 型 | 内容 |
| --- | --- | --- |
| `connector` | `teams`・`shannon_booking`・`http` | API キーか URL を貼る連携先だけ |
| `secret` | string | API キー・Webhook の URL（1〜2,000 文字） |
| `input` | object | 任意。`shannon_booking` は `{ base_url }`、`http` は `{ base_url, header }` |
| `tenant_id` | string | 任意。テナントの連携にする。`shannon_booking`・`http` は環境の連携だけで、付けると 422 `invalid_request` |

返り: 201 `Connection`。保存の前に 1 回試しに呼び、失敗は 422 `verify_failed`。ログインでつなぐ連携先（OAuth）は、[連携の画面のリンク](#連携の画面のリンク)か管理画面でつなぎます。

## 連携の取得

```
GET /v1/connections/{id}
```

返り: `Connection`。

## 連携の設定

```
PATCH /v1/connections/{id}
```

| 入力 | 型 | 内容 |
| --- | --- | --- |
| `config` | object | 連携先ごとの設定。渡した項目だけ変わります |

返り: `Connection`。設定待ち（`needs_setup`）の連携は `active` になります。誤りは 422 `invalid_config`。設定の形は[連携](https://voicast.jp/docs/connectors.md)を見てください。

## 連携の設定の選択肢

```
GET /v1/connections/{id}/options
```

連携先に問い合わせて、設定で選べるものを返します。

| 連携先 | クエリ | 返り |
| --- | --- | --- |
| Google カレンダー・Microsoft 365 | | `{ calendars: [{ id, name, primary }] }` |
| Google スプレッドシート | `spreadsheet`・`sheet` | `{ spreadsheets, sheets, headers }` |
| kintone | `app` | `{ apps, fields: [{ code, label }] }` |
| Chatwork | | `{ rooms }` |
| LINE WORKS | | `{ users }` |
| MCP サーバー | | `{ tools }` |

連携が切れていれば 409 `needs_reauth`。

## 連携の解除

```
DELETE /v1/connections/{id}
```

返り: 204。連携先のトークンも取り消します。

## テナントの連携の一覧

```
GET /v1/tenants/{id}/connections
```

返り: `{ data: Connection[] }`。

## 連携の画面のリンク

```
POST /v1/tenants/{id}/connect-links
```

| 入力 | 型 | 内容 |
| --- | --- | --- |
| `connectors` | string[] | 任意。画面に出す連携先（`google_calendar`・`m365_calendar`・`google_sheets`・`kintone`・`hubspot`・`slack`・`chatwork`・`lineworks`・`teams`） |
| `frame_origin` | string | 任意。iframe で埋め込む親のオリジン（https） |

返り: 201 `{ url, expires_at }`（15 分有効）。使い方は[連携](https://voicast.jp/docs/connectors.md#埋め込みの連携の画面)。

---

## 電話（Twilio Media Streams）

```
wss://voice.voicast.jp/ws
```

Twilio の `<Connect><Stream>` の接続先です。`<Parameter>` で次を渡します。

| Parameter | 内容 |
| --- | --- |
| `tenant` | テナントの ID |
| `secret` | テナントの接続の鍵（`vts_…`） |
| `agent` | 任意。会話の設定の ID |
| `from` | 任意。かけてきた番号 |

鍵が違う・テナントが止まっている・料金の都合で断るときは、すぐに閉じます。手順は[電話でつなぐ](https://voicast.jp/docs/phone.md)。

## WebRTC の offer

```
POST https://voice.voicast.jp/v1/webrtc/offer
```

| 入力 | 型 | 内容 |
| --- | --- | --- |
| `sdp`・`type` | string | WebRTC の offer |
| `token` | string | セッションの `client_token`（Pipecat のクライアントは `requestData.token` でも可） |
| `pc_id`・`restart_pc` | string・boolean | つなぎ直しのときだけ（`token` は要らない） |

返り: `{ sdp, type, pc_id }`。トークンが違う・期限切れ・使用済み・料金の都合で断るときは 403（料金の都合はいずれ `POST /v1/sessions` と同じ 402 にそろえる予定です）。

つないだら、データチャンネルで RTVI の `client-ready` を送り、そのあと 1 秒ごとに `ping` の文字列を送ります（[Web・アプリでつなぐ](https://voicast.jp/docs/web.md)）。

## ICE の候補

```
PATCH https://voice.voicast.jp/v1/webrtc/offer
```

| 入力 | 型 | 内容 |
| --- | --- | --- |
| `pc_id` | string | offer の返りの `pc_id` |
| `candidates` | array | `[{ candidate, sdp_mid, sdp_mline_index }]` |

返り: `{ ok: true }`。知らない `pc_id` は 404。
