API は Web ダッシュボードと同じエージェント、通話、クレジットを扱います。ダッシュボードで作ったものはすべて自社システムから操作でき、その逆も可能です。
| 内容 | エンドポイント |
|---|---|
| エージェント一覧 | GET /api/v1/voice/agents |
| エージェントを作成 | POST /api/v1/voice/agents |
| エージェントの取得・更新 | GET · PATCH /api/v1/voice/agents/{id} |
| 発信する | POST /api/v1/voice/calls |
| 通話一覧 | GET /api/v1/voice/calls |
| 通話の取得(文字起こし) | GET /api/v1/voice/calls/{id} |
| 通話の削除 | DELETE /api/v1/voice/calls/{id} |
| 発信者の通話の削除 | POST /api/v1/voice/calls/delete-by-caller |
| キャンペーン | GET · POST /api/v1/voice/campaigns |
| キャンペーンの取得 | GET /api/v1/voice/campaigns/{id} |
| 開始・一時停止・中止 | POST /api/v1/voice/campaigns/{id}/{action} |
| クローン音声 | GET · POST /api/v1/voice/voices |
| クローン音声の削除 | DELETE /api/v1/voice/voices/{id} |
| 発信停止リスト | GET · POST /api/v1/voice/do-not-call |
| リストから削除 | DELETE /api/v1/voice/do-not-call/{phone} |
| 言語・音声・テンプレート | GET /api/v1/voice/catalog |
| エージェントのテスト | GET · POST /api/v1/voice/agents/{id}/tests |
| テスト実行 / 実行結果の取得 | POST /api/v1/voice/agents/{id}/test-runs · GET /api/v1/voice/test-runs/{id} |
| 番号をエージェントに割り当て | PATCH /api/v1/voice/phone-numbers/{id} |
| 単語リスト | GET · POST /api/v1/voice/vocabularies |
ダッシュボードの 連携 → API キー でキーを作成し(概要ページからワンクリックでも作れます)、Bearer トークンとして送ります。
Authorization: Bearer sk-your-key
キーは所有者が作業しているワークスペースで、その人のロールの権限で動作します。閲覧専用メンバーのキーは読み取りはできても変更はできません。
テンプレートからエージェントを作成し、同じリクエストでフィールドを設定することもできます。template を省くと空の状態から始まります。GET /api/v1/voice/catalog はテンプレートと、エージェントに指定できる言語と音声を返します。テンプレートは英語と中国語で書かれているため、ほかの言語では system_prompt と first_message をその言語で送ってください。リクエスト全体が保存されるか、エージェントがまったく作られないかのどちらかなので、拒否されたリクエストは直してそのまま送り直せます。レスポンスの dashboard_url はダッシュボード上のエージェントのページで、ブラウザから下書きと通話できます。すべてのエージェントは EvaSay ASR、EvaSay LLM、EvaSay TTS で動き、通話ごとに言語と負荷に応じて EvaSay が選び、通話料金に含まれます。ほかのサービスのアカウントを設定する必要はありません。
curl https://evasay.com/api/v1/voice/agents \
-X POST \
-H "Authorization: Bearer sk-your-key" \
-H "Content-Type: application/json" \
-d '{
"name": "Front desk",
"language": "en",
"first_message": "Hi, thanks for calling Sunrise Salon. How can I help?",
"system_prompt": "You are the front desk of Sunrise Salon..."
}'
これらのフィールドは PATCH /api/v1/voice/agents/{id} で更新できます。
| フィールド | 説明 |
|---|---|
name, first_message, system_prompt | テキスト |
status | published で下書きを新しいバージョンとして公開、draft で通話の受付を停止 |
language | 27 言語のいずれか(例:en、zh、es、de)、または発信者に合わせる multi |
temperature | 0-100 |
max_duration_sec, silence_timeout_sec | 通話の上限 |
interruptible, end_call_enabled | 真偽値 |
confirm_details | 真偽値。番号や名前を復唱して確認を取ります |
ai_disclosure | 真偽値、既定は true。最初のメッセージの前に、通話の言語で相手が AI と話していることを伝えます(最初のメッセージですでに伝えている場合は省略) |
voicemail_detection | 真偽値。発信時はまず相手の声を聞き、留守番電話なら終了します |
voicemail_message | 留守番電話に残すメッセージ。{{variables}} を使えます。空なら切断します |
transfer_number | 転送先の番号(国際形式) |
transfer_destinations | 追加で最大 9 件の番号 [{name, number, when}]。エージェントが when に基づいて選びます |
transfer_briefing | 真偽値。電話に出た人が最初にエージェントによる通話の要約を聞きます |
pronunciations | 最大 100 件の [{word, say, case_sensitive}]。音声は word を say のとおりに読みます |
idle_messages | 黙っている発信者に順に伝える最大 3 件のリマインダー。最後のあとに通話が終了します |
noise_reduction | 真偽値。発信者の背景雑音を除去してから聞き取ります |
recording_enabled | 真偽値。通話を 2 チャンネル(左が発信者、右がエージェント)で録音します |
recording_consent | announce、ask(発信者が同意した場合のみ保存)、none |
recording_notice | 最初に伝える案内または質問。空なら通話の言語の標準文 |
handoffs | 最大 5 件の [{agent_id, when}]。通話を引き継げるほかのエージェント |
knowledge_base_id | ナレッジベースのいずれか |
vocabulary_id | 用語集のいずれか。発信者がその用語を話したとき、より確実に聞き取れます |
voice_id | EvaSay の内蔵音声 ID、またはクローン音声(clone:…) |
内蔵音声は言語によって異なります。中国語は ming-designed-female-clear、ming-designed-female-warm(女性)、ming-designed-male-clear、ming-designed-male-friendly(男性)、ほかの言語は F1–F5(女性)と M1–M5(男性)です。エージェントの言語を話せない内蔵音声は、その言語を話す同じ性別の音声に置き換わります。旧フィールド名 tts_voice_id も引き続き使えます。それ以外の voice_id や、まだ準備ができていないクローン音声は拒否されます。
analysis_config は通話終了後に何を分析するかを決めます。summary(既定 true)、summaryLanguage、successCriteria(成功した通話が達成すること)、fields(抽出する最大 20 個の {name, type, description}。type は string、number、boolean のいずれか)、scorecard(最大 10 個の {name, description, weight}、重みは 1〜5)です。結果は通話の analysis と call.analyzed Webhook に入ります。
エージェントのフィールドを変更すると下書きが変わります。POST /api/v1/voice/agents/{id}/publish(任意で note 付き)で下書きを公開するか、同じ PATCH で status: "published" を指定するまで、通話は公開中のバージョンを使い続けます。公開のたびに番号付きのバージョンが保存され、エージェントの published_version が通話で使われるバージョンを示します。通話は開始時に公開されていたバージョンのまま進むため、通話中に公開しても影響しません。ダッシュボードからのテスト通話は下書きを使います。
GET /agents/{id}/versions でバージョン一覧を取得し、POST /agents/{id}/versions/{version}/restore でロールバックします。新しい通話はそのバージョンを使い、下書きもその内容になります。エージェントの名前、デプロイ先、ウィジェットの見た目、通知はバージョン管理されず即座に変わります。ナレッジベースのドキュメントも同様です。
テストは 1 本の通話をテキストで再現します。模擬の発信者が scenario を演じ、エージェントは下書き(指示、最初のメッセージ、ナレッジ)から答え、審査役が success_criteria に照らして会話を判定します。大事な通話ごとにテストを書き、変更のたびに実行し、すべて合格したら公開してください。
curl https://evasay.com/api/v1/voice/agents/AGENT_ID/tests \
-X POST \
-H "Authorization: Bearer sk-your-key" \
-H "Content-Type: application/json" \
-d '{
"name": "Move a booking",
"scenario": "A regular customer wants to move Friday 3 pm to Saturday morning.",
"success_criteria": "Offers Saturday times and confirms the new time and the customer'"'"'s name.",
"max_turns": 8
}'
POST /agents/{id}/test-runs はエージェントのすべてのテスト、または test_ids のテストを実行し、テストごとに 1 件の実行(status: "running")を返します。status が passed、failed、error のいずれかになるまで GET /test-runs/{id} を問い合わせると、実行に transcript と審査役の reason(英語。reason_language: "zh" で中国語)が入ります。各実行には Credits がかかり、金額はダッシュボードのテスト画面に表示されます。残高で払えない最初の実行で止まり、残りは not_started に並びます。実行できなかった分は返金されます。テストが確かめるのはエージェントの発言で、声やタイミングは対象外です。関数も呼びません。関数は POST /tools/{id}/test で事前に確認してください。テストはエージェントごとに最大 30 件で、PATCH と DELETE /agents/{id}/tests/{test_id} で変更・削除できます。
通話全体(type: "simulation"、既定)のほかに、1 回の返答だけを確認するテストもあります。type: "next_reply" には、その返答より前の会話で発信者の発言で終わる chat_history([{role, text}]。role は caller か agent)と、エージェントの次の返答を判定する success_criteria を指定します。type: "tool_call" には chat_history と expected_tool {name, arguments, match} を指定します。エージェントは name(自分の関数、または end_call、transfer_call、handoff_to_agent)を arguments 付きで呼び出す必要があり、値は完全一致(前後の空白を除き、大文字小文字は区別しない)で、match: "judge" なら意味で比較します。これ以外に渡された引数は無視されます。実行の transcript では、エージェントが呼び出した関数が tool_calls に入ります。
POST /agents/{id}/tests/generate はエージェントの下書き(指示、ナレッジ、関数)からテスト案を作ります。count(1〜10、既定 5)、types、focus(扱ってほしい内容)、language(名前と条件の言語、en または zh)を指定でき、1 回の実行と同じ Credits がかかります。返ってきた案は確認してから POST /agents/{id}/tests に送ります。save: true を付けるとそのまま保存されます。
hooks は通話の特定の時点で動作を行います(最大 10 ルール)。on: "silence"(エージェントが話し終えてから発信者が seconds 秒(3–600)何も言わない。再び話すまで 1 回)、on: "time_elapsed"(通話開始から seconds 秒、10–3600)、on: "call_ending"。各ルールは {type: "say", text}、{type: "transfer", destination?}、{type: "end_call", text?}、{type: "tool", name, args?} を最大 3 つ do します。通話終了時のルールは関数の実行のみです。ルールで終了した通話の ended_reason は rule_ended です。
電話では、発信者が押したキーが回答としてエージェントに届きます。入力は # または 2 秒の間で確定します。発信時には、エージェントがキーを押して企業の自動音声メニューを進むこともできます。
transfer_number は発信者を人に引き継ぐときのエージェントの主な番号で、ワークスペースのクレジットが尽きたときに呼び出す番号でもあります。transfer_destinations は名前付きの番号を追加し、それぞれにいつ使うかの説明(「請求:請求書と返金の質問」)を付けると、エージェントが選びます。transfer_briefing を有効にすると、エージェントは転送時に短い要約を作成し、電話に出た人は発信者がつながる前に、EvaSay の音声とエージェントの要約言語でそれを聞きます。その通話の ended_reason は transferred になります。
handoffs を使うと、受付が予約担当のエージェントに予約を渡すように、エージェントがワークスペースの別の公開中エージェントに通話を渡せます。エージェントは when で選びます。引き継いだエージェントはそれまでの会話をすべて受け取って同じ通話を続け、自身の指示、音声、ナレッジ、ツール、引き継ぎ設定で話します。通話は最初のエージェントの下で一覧表示、分析、課金され、文字起こしに各引き継ぎが記録されます。
番号はダッシュボードでご自身の通信事業者アカウントを使って接続します。GET /api/v1/voice/phone-numbers で一覧を取得し、PATCH /api/v1/voice/phone-numbers/{id} に { "agent_id": "AGENT_ID" } を送るとその番号の着信がエージェント(公開中のバージョン)に届き、"agent_id": null でどのエージェントにも届かなくなります。label で名前を変えられます。
単語リストには、発信者が口にするのに音声認識が聞き間違えやすい名前や用語(製品名、薬の名前、通りの名前など)を入れます。POST /api/v1/voice/vocabularies に { "name": "Products", "terms": ["Lumora", "Vantix Pro"] } を送り(50 文字以内の用語を最大 1,000 個。"preset": "medical" または "legal" から始めることもできます)、エージェントの vocabulary_id に設定します。GET、PATCH(name、terms)、DELETE /vocabularies/{id} が使えます。エージェントが使用中のリストは削除できません。
各番号には電話番号ページに通話ルールがあり、エージェントが応答する前に適用されます。ブロックした番号や番号の先頭部分(+1555)、番号を非通知にした発信者は応答せずに拒否され、料金はかかりません。指定した発信者を別の番号に直接つなぐこともできます。エージェントが応答できないとき(未公開、Worker なし、転送先番号なしでクレジット切れ)は、番号を呼び出すか独自のメッセージを流せます。開始時の照会(run_at_start)で {"reject": true, "message": "..."} と返して発信者を断ることもできます。エージェントはメッセージがあれば伝えてから切断し、ended_reason は rejected になります。
ワークスペースの電話番号から 1 件発信します。すべてのリクエストに一意の Idempotency-Key が必要です。同じキーを再送すると、二重に発信せず同じ通話を返します。
curl https://evasay.com/api/v1/voice/calls \
-X POST \
-H "Authorization: Bearer sk-your-key" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"agent_id": "AGENT_ID",
"to": "+14155550100",
"variables": { "customer_name": "Alex", "order_id": "1234" }
}'
ワークスペースに電話番号が 2 つ以上ある場合は from が必須です。variables はエージェントの最初のメッセージとプロンプトの {{customer_name}} のようなプレースホルダーを埋めます。
GET /api/v1/voice/calls/{id} は通話のステータス、通話時間、課金されたクレジット、要約、分析、文字起こしを返します。通話一覧は GET /api/v1/voice/calls?agent_id=&status=&channel=&user_id=&page= で取得します(user_id:Web 通話トークンから得た、その人に対するあなた側の ID)。turns は通話のターンごとのデバッグログで、発信者が話し終えてから回答が聞こえるまでの時間の内訳をミリ秒で示します。waitMs(発話終了)、sttMs(音声認識)、llmMs(モデルの最初の単語。関数を呼ぶたびに 1 つ追加)、ttsMs(音声の最初の音)、firstAudioMs(回答が聞こえた時点)に加え、呼び出された関数、割り込み、エラーが含まれます。通話ページにも表示されます。
recording_enabled を有効にすると、各通話は 2 チャンネルの WAV として録音されます。左が発信者、右がエージェントで、サンプリングレートは通話と同じです。recording_consent: "announce" ではエージェントが最初に録音することを伝え、"ask" では許可を尋ね、発信者が同意した場合のみ録音を保存します(回答なしは不同意とみなします)。"none" では何も伝えません。あなたと発信者がいる地域の法律で何が求められるかを確認してください。
録音は EvaSay のストレージ、または 連携 → Webhook と通知 → 録音の保存先 で設定した独自の S3 互換バケット(AWS S3、Cloudflare R2、Google Cloud Storage、Supabase、MinIO)に保存されます。通話を処理する Worker がファイルを直接バケットにアップロードします。録音が保存されると、GET /api/v1/voice/calls/{id} は 1 時間有効な recordingUrl と recording.status(recording、stored、declined、failed)を返します。
DELETE /api/v1/voice/calls/{id} は終了した通話を、文字起こし、録音(EvaSay のストレージまたはお客様のバケット内)、分析、Webhook の送信記録とともに削除します。使用した Credits はクレジット履歴に残ります。まだ削除できない通話には 409(code は call_not_deletable_yet)が返ります。通話中か終了直後、精算中、録音のアップロード中、または残高で支払えなかった Credits がある場合です(先に Credits を追加してください)。
本人からデータ削除を求められたときは POST /api/v1/voice/calls/delete-by-caller を使います。{ "phone_number": "+14155550123" } を送るとこの番号の通話(着信は発信元の番号、発信は発信先の番号)がすべて、{ "user_id": "..." } を送るとトークンにこの user_id を含む Web 通話がすべて削除されます。応答は { "deleted": 12, "kept_back": { "settling": 1 }, "more": false } です。1 回あたり最大 200 件なので、more が true の間は繰り返し呼び出し、kept_back の通話は後で再度削除してください。番号が発信停止リストにある場合は、リストに残ります。ダッシュボードの 通話 → 発信者のデータを削除 でも同じことができます。
通話中に POST /api/v1/voice/calls/{id}/control を送ります。通話を処理する Worker が約 2 秒以内にコマンドを受け取り、API は 202 を返します。通話を処理する Worker に接続できない場合、API は 503(code call_unreachable)を返し、コマンドは実行されません。
type | 動作 |
|---|---|
say | エージェントが今の文のあとに text を話します。"interrupt": true ならすぐに話します |
note | エージェントに text を伝え(話しません)、次の返答から考慮させます。文字起こしに記録されます |
end | text があればエージェントが話してから切断します。text のない電話は即座に切断します |
その通話の ended_reason は api_ended になります。ダッシュボードの通話ページにも同じ操作があります。
POST /api/v1/voice/calls/{id}/listen(任意で { "channels": 2 })は、2 分以内に開ける listen_url を返します。これは WebSocket で、まず JSON メッセージ {"type":"start","sampleRate":8000,"channels":1,"encoding":"pcm_s16le"} が届き、続いて発信者が聞いている音声(両者の声のミックス。channels: 2 なら左が発信者、右がエージェント)が 16 ビット リトルエンディアン PCM のバイナリフレームで届きます。通話が終わると閉じます。音声は EvaSay のコントロールプレーンを経由せず、通話を処理する Worker から直接届きます。ダッシュボードの通話ページの「モニタリング」ボタンも同じアドレスを使います。
自社の電話システムやアプリなど、独自のプログラムから通話の音声を送受信します。通話を開始します。
curl https://evasay.com/api/v1/voice/calls/websocket \
-X POST \
-H "Authorization: Bearer sk-your-key" \
-H "Content-Type: application/json" \
-d '{ "agent_id": "AGENT_ID", "encoding": "pcm_s16le", "sample_rate": 16000 }'
レスポンスの websocket_url にはワンタイムトークンが含まれ、15 分以内に開けます。encoding は pcm_s16le(sample_rate 8000、16000、24000 の 16 ビット リトルエンディアン PCM)または mulaw(8 kHz μ-law)です。音声はその形式のバイナリフレームで双方向に流れ、1 フレーム 20 ms が理想です。テキストフレームは JSON です。
| 方向 | メッセージ |
|---|---|
| あなた → EvaSay | {"type":"dtmf","digits":"12#"} キー入力。{"type":"end"} 切断。{"type":"text","text":"…"} 発信者が入力した言葉(ボタンでの返答は "input":"button")。{"type":"context","text":"…","key":"cart"} アプリからの状況報告(エージェントは返答しません)。{"type":"tool_result","id":"…","result":{…}}(または "error")ページ関数の回答 |
| EvaSay → あなた | start(callId 付き)。clear(発信者が割り込み:未再生のエージェント音声を破棄)。transcript(role、text、final、入力された言葉には input)。user_started_speaking、user_stopped_speaking、agent_started_speaking、agent_stopped_speaking。cards(cards、quick_replies)。tool(id、name、arguments:ページ関数を実行)。notice(code:入力された言葉の too_fast または limit)。ended(reason 付き。そのあとソケットが閉じます) |
接続するとすぐにエージェントが挨拶します。リクエストに user_id と page_tools(アプリが応答するページ関数。後述)を追加できます。通話はほかの通話と同じく分単位で課金され、通話記録、分析、Webhook も同じです。
Web 通話(ウィジェット、ホストされた通話ページ、あなたのアプリ)では、発信者には音声の横にチャットが表示されるため、エージェントは話す以外のこともできます。電話の発信者にはこれらはありません。
エージェント → ナレッジとツール → 発信者に送る(エージェント API では send_to_caller)を有効にすると、エージェントは発信者の画面に一度に最大 5 枚のカードを表示できます。各カードにはタイトル、その下の一行、説明、画像、リンク、最大 2 つの返答ボタンを付けられます。質問の下に最大 3 つのクイック返信を出すこともできます。リンクが読み上げられることはありません。
エージェントが送れるのは、その通話で与えられたリンクと画像だけです。指示と挨拶、ナレッジベース(Web ページから読み込んだドキュメントにはそのアドレスが付きます)、関数の回答、既製のカードです。さらにリンクは allowed_hosts のいずれかになければなりません(既製カード自身のサイトは常に許可)。エージェントが作り出したリンクや発信者が読み上げたリンクは送られません。エージェントの発言中のそれらのサイトのリンクは、チャットでクリックできます。
"send_to_caller": {
"enabled": true,
"allowed_hosts": ["shop.example.com", "*.cdn.example.com"],
"presets": [
{ "name": "hours", "when": "asked when we open", "title": "Opening hours",
"description": "Mon–Sat 9:00–18:00", "url": "https://shop.example.com/stores", "image_url": "" }
]
}
送信のたびに call.message_sent Webhook(channel、title と url を持つ items、quickReplies、blocked)が届きます。1 通話につき最大 10 回送信できます。
通話中、発信者は文字を入力したり(メールアドレス、注文番号)、返答ボタンをタップしたりもできます。エージェントは話した言葉と同じように答え、話している最中ならまず止まります。1 メッセージ最大 500 文字、1 秒に 1 件、1 通話 100 件までです。通話記録では、これらのターンは入力またはタップとして示されます。
ウィジェットはページに evasay オブジェクトを提供します。すべて任意で、通話フレームがページの声を聞くのは、そのページがウィジェットの許可されたサイト(エージェント → デプロイ)にある場合だけです。
<script src="https://evasay.com/voice-widget.js" data-agent="AGENT_SLUG" async></script>
<script>
window.evasay = window.evasay || [];
evasay.push(function (agent) {
agent.setVariables({ plan: 'pro', page: location.pathname }); // 次の通話用
agent.update('Cart: 2 items, $120', { key: 'cart' }); // 通話中
agent.registerTool('add_to_cart', async function (args) {
// あなたのコード(訪問者のブラウザで実行)
return { ok: true, checkout: 'https://shop.example.com/checkout' };
});
agent.on('cards', function (cards) {}); // ほかに 'live'、'ended'
});
</script>
setVariables の値は指示と挨拶の {{name}} を埋めます。訪問者のブラウザから来るため、エージェントには未検証であることが伝えられ、関数や開始時の照会には決して渡されません。update はページで起きていることをエージェントに伝えますが、返答はさせません。同じ key の新しい更新は古いものを置き換えます。"kind": "page"、url なし)の関数をエージェントに追加し、ページで同名の関数を登録します。エージェントがそれを呼べるのは、登録したページからの通話だけです。wait_for_result が false でない限り、timeout_sec(1–10、既定 5)まで回答を待ちます。訪問者としてサインインした状態で、訪問者のクリックと同じように動作します。費用が発生する操作や取り消せない操作の前には、ページ側で確認を求めてください。回答内のリンクはカードとして送れます。通話を開始できる人 を トークンを持つ人のみ に設定し、サーバーでサインイン中のユーザー向けにワンタイムトークンを取得します。
curl https://evasay.com/api/v1/voice/web-calls \
-X POST \
-H "Authorization: Bearer sk-your-key" \
-H "Content-Type: application/json" \
-d '{ "agent_id": "AGENT_ID", "user_id": "u_42", "variables": { "name": "Ann" }, "expires_in": 600 }'
レスポンスには token(expires_in 秒以内に 1 回の通話。30–3600、既定 600)、agent(スラッグ)、web_call_url が含まれます。ウィジェットのスクリプトに data-token-url="/your/endpoint" を付けてトークンをページに渡すか(訪問者の Cookie 付きでそこに POST し、{ "token": "…" } を読み取ります)、agent.setToken(async () => token) を使います。トークンの variables は検証済みです。指示、開始時の照会、関数のテンプレートで使え、user_id も同様です。user_id は通話に保持されます(通話の userId、通話の絞り込みには user_id)。API キーはサーバーに置いてください。
自社アプリでは、{ "token": "…", "pageTools": ["add_to_cart"] } を https://evasay.com + web_call_url に POST します。レスポンスは通話の接続情報(WebRTC offer のアドレスまたは WebSocket のアドレスと通話トークン)で、Web、iOS、Android、React Native 向けの Pipecat クライアント SDK などの RTVI クライアントで使います。カード、ページ関数、通知は RTVI の server-message(evasay.cards、evasay.tool、evasay.notice)として届きます。入力した言葉は send-text で、ページの報告と回答は client-message の evasay.context と evasay.tool_result で送ります。または WebSocket による音声 で user_id と page_tools を渡して音声を自分で扱うこともできます。
電話番号 → SIP では、自社の PBX、SIP 電話、SIP トランクからエージェントに直接発信できます。通話料金は SIP プロバイダーから請求されます。
sip:ev…@<EvaSay の SIP ドメイン>。着信のみで、PBX(3CX、FreePBX、Asterisk など)や SIP 電話向けです。from に指定)。担当者への転送は REFER でトランクに引き渡されます。トランクが認証を求めた場合、EvaSay は入力されたトランクのユーザー名とパスワードで応答します。着信は EvaSay が発行するユーザー名とパスワードによるダイジェスト認証でサインインします(realm は SIP ドメイン)。送信元 IP を制限することも、プロバイダーがサインインできない場合は IP だけで受け付けることもできます(その場合 IP の登録は必須)。各 SIP アドレスまたはトランクには同時通話数の上限があり、超えると 486 が返ります。パスワードは作成時とリセット時に一度だけ表示されます。UDP と G.711(PCMU、PCMA)のみ対応です。キー入力は RFC 4733 または SIP INFO。PBX のトランクでは登録(REGISTER)をオフにしてください。
キャンペーンは 1 つのエージェントで連絡先リストに電話をかけます。発信時間帯と同時通話数の上限(max_concurrency、最大 100 件、ワークスペースの同時通話の上限を超えることはありません)の範囲内で発信し、つながらなかった相手には再試行します。
curl https://evasay.com/api/v1/voice/campaigns \
-X POST \
-H "Authorization: Bearer sk-your-key" \
-H "Content-Type: application/json" \
-d '{
"name": "Appointment reminders",
"agent_id": "AGENT_ID",
"phone_number_id": "PHONE_NUMBER_ID",
"timezone": "America/New_York",
"window_start": "09:00",
"window_end": "18:00",
"max_concurrency": 2,
"max_attempts": 2,
"retry_delay_min": 60,
"consent_confirmed": true,
"contacts": [
{ "phone": "+14155550100", "name": "Alex", "appointment": "Tuesday 3 pm" }
]
}'
連絡先は、1 列目に電話番号を置いた CSV テキストでも指定できます。consent_confirmed: true は必須です。これにより、リストの全員が AI 音声を含む電話を受けることに同意していることを確認したことになり、EvaSay はその確認をキャンペーンに保存します(consent_confirmed_at)。キャンペーンは下書きとして作成されます。POST /api/v1/voice/campaigns/{id}/start で開始し、pause と cancel も同様に使います。キャンペーンを開始する前にエージェントを公開しておく必要があります。
voicemail_detection が有効(既定)の場合、発信時はエージェントが挨拶する前に相手が話すのを待ちます。相手が黙っていれば、2〜3 秒後にエージェントが挨拶します。留守番電話や電話会社の録音が聞こえたら、エージェントは応答メッセージと発信音が終わるのを待って voicemail_message を残し、なければ切断します。こうした通話は ended_reason が voicemail で終わり、通話時間分が課金され、分析や通知は行われません。
キャンペーンでは、留守番電話につながった番号は応答がなかった番号と同じく再度発信され、メッセージは最後の試行でのみ残されるため、同じメッセージが 2 回残ることはありません。
キャンペーンからでも POST /api/v1/voice/calls からでも、すべての発信はまずワークスペースの発信停止リストを確認します。リストにある番号には決して発信しません。API は code が do_not_call の 409 を返し、キャンペーンはその連絡先を outcome が do_not_call の canceled として次に進みます。
エージェントが発信した通話で相手がもう電話しないでほしいと言うと、エージェント自身がその番号をリストに追加して相手に伝え、call.do_not_call Webhook が送信されます。番号は キャンペーン → 発信停止リスト または API でも追加・削除できます。
curl https://evasay.com/api/v1/voice/do-not-call \
-X POST \
-H "Authorization: Bearer sk-your-key" \
-H "Content-Type: application/json" \
-d '{ "phones": ["+14155550100"], "note": "Asked by email" }'
番号は国際形式で、一度に最大 1,000 件です。GET /api/v1/voice/do-not-call は新しい順に一覧を返し(search、limit、page)、それぞれに source(call、dashboard、api)が付きます。DELETE /api/v1/voice/do-not-call/%2B14155550100 で 1 件削除できますが、相手が電話を受けることに同意した場合にのみ行ってください。
POST /api/v1/voice/voices は、1 人の話者の WAV 録音から音声を作成します(multipart の audio、name、language、consent=true)。ステータス running で 202 を返します。ready になるまで GET /api/v1/voice/voices をポーリングし、エージェントの voice_id にその voice_id を設定します。language は録音で話されている言語で、上記のエージェント言語のいずれかです。音声は中国語以外のすべての言語を話し、中国語では同じ性別の内蔵音声が応答します。録音の言語で最も話者に近く聞こえます。クローン音声の作成にはクレジットがかかり、作成前にダッシュボードに表示されます。
連携 → Webhook と通知 で https のアドレスを追加すると、通話イベントを受け取れます。
| イベント | タイミング |
|---|---|
call.started | エージェントが応答した |
call.ended | 通話が終了した(通話時間とクレジット付き) |
call.analyzed | 要約、結果、抽出フィールド、文字起こしの準備ができた |
call.transcript | 通話中、数秒ごとに文字起こしの新しい行 |
call.tool_called | エージェントがあなたの関数を呼び出した:name、arguments、status、durationMs |
call.handed_off | 通話が別のエージェントに引き継がれた:fromAgentId、toAgentId |
call.transferred | 通話が担当者に転送された:destination |
call.voicemail | 発信した通話が留守番電話につながった |
call.do_not_call | 発信先の相手がもう電話しないでほしいと言った:phone |
call.recording_ready | 2 チャンネルの録音が保存された:recordingUrl(24 時間有効)、durationMs |
call.message_sent | エージェントが Web またはアプリの発信者の画面にカードを表示した:items、quickReplies |
alert.triggered | アラート(Webhook と通知 → アラート)のいずれかがしきい値を超えた |
alert.resolved | そのアラートが正常に戻った |
通話中に送られるイベントには data.call(id、agentId、channel、direction、番号)が含まれます。各リクエストは署名されています。
EvaSay-Signature: t=<unix seconds>,v1=<hex HMAC-SHA256 of "<t>.<raw body>">
EvaSay-Delivery: <delivery id>
Webhook 作成時に表示された Webhook シークレットで署名を検証し、2xx ステータスで応答してください。失敗した配信は 1 分後、5 分後、30 分後、2 時間後、12 時間後に、同じ本文と同じ EvaSay-Delivery ID で再試行されるため、重複は無視できます。Webhook の最近の配信(送信した本文そのものと、エンドポイントが返したステータス)は GET /webhooks/{id}/deliveries とダッシュボードで確認でき、POST /webhooks/{id}/deliveries/{delivery_id} で 1 件を再送できます。
メールと alert.triggered / alert.resolved Webhook のほか、アラートは Webhook と通知 → アラート → ほかの送信先 で Slack チャンネル(Incoming Webhook)、WeCom のグループロボット、Telegram にも送れます。
エラーは共通の形式です。
{
"error": {
"type": "invalid_request_error",
"code": "invalid_agent_field",
"message": "status must be draft or published",
"param": "status"
}
}
| ステータス | 意味 |
|---|---|
| 400 | 入力が不正(code と param が箇所を示します) |
| 401 | API キーがない、または無効 |
| 402 | 通話を開始するためのクレジットが不足 |
| 403 | キーが閲覧専用、またはエージェントが利用不可 |
| 404 | このワークスペースにそのエージェント、通話、キャンペーンがない |
| 409 | 状態が不正(例:実行中のキャンペーンを開始)、プロバイダーのアカウントが未接続、または番号が発信停止リストにある(do_not_call) |
| 429 | リクエストが多すぎる。間隔を空けて再試行してください |
ワークスペースの過去 30 日間の Voice API リクエスト(時刻、エンドポイント、ステータス、所要時間、キー、エラーコード。本文は含まない)は API キー → API リクエストログ に表示されます。
読み取りはキーごとに毎分 120 リクエスト、書き込みと発信は 30 リクエストまでです。
通話はワークスペースのクレジットから分単位で課金され、ダッシュボードと同じ残高を使います。現在の料金は 料金ページ をご覧ください。
OpenAPI コントラクト を使って連携用クライアントを生成できます。公開 API、ダッシュボード、MCP 管理エンドポイントは、同じワークスペースのリソースと権限を扱います。
| 機能 | エンドポイント |
|---|---|
| 再利用できるツール | GET / POST /api/v1/voice/tools |
| ツールの取得・更新・削除 | GET / PATCH / DELETE /api/v1/voice/tools/{id} |
| ツールのプレビュー・テスト | POST /api/v1/voice/tools/{id}/test |
| ツールをエージェントに紐づけ | PUT /api/v1/voice/agents/{id}/tools |
| ナレッジベース | GET / POST /api/v1/voice/knowledge-bases |
| ナレッジベースの取得・更新・削除 | GET / PATCH / DELETE /api/v1/voice/knowledge-bases/{id} |
| ドキュメントの追加・一覧 | POST / GET /api/v1/voice/knowledge-bases/{id}/documents |
| ドキュメントの取得・削除 | GET / DELETE /api/v1/voice/knowledge-bases/{id}/documents/{doc_id} |
| 通話イベントの購読 | GET / POST /api/v1/voice/webhooks |
| 購読の取得・更新・削除 | GET / PATCH / DELETE /api/v1/voice/webhooks/{id} |
| 署名付きテストイベントを送信 | POST /api/v1/voice/webhooks/{id}/test |
| リモート MCP ツールの検出 | POST /api/v1/voice/mcp/discover |
| MCP 管理エンドポイント | /api/v1/voice/mcp |
CRM アダプターに照会する HTTP ツールを登録します。
{
"name": "lookup_customer",
"description": "Find a customer after confirming their customer ID.",
"kind": "http",
"url": "https://crm.example.com/customers/{{customer_id}}",
"method": "GET",
"read_only": true,
"credential_ref": "crm",
"input_schema": {
"type": "object",
"properties": { "customer_id": { "type": "string" } },
"required": ["customer_id"],
"additionalProperties": false
},
"response_path": "data"
}
POST /tools は { "data": { "id": "TOOL_ID", ... } } を返します。PUT /agents/AGENT_ID/tools と { "tool_ids": ["TOOL_ID"] } でその定義をエージェントに紐づけます。これはツール一覧全体をスナップショットで置き換えます。ライブラリのツールが変わったら紐づけ直してください。ライブラリの定義を削除しても既存のスナップショットは残ります。エージェント API の tools フィールドでインラインの定義を読み取り・置き換えることもできます。分析とウィジェットはエージェント API の analysis_config と widget_config で設定します。
ツールは GET、POST、PUT、PATCH、DELETE を使えます。query と request_body は { "customer": { "id": "{{customer_id}}" } } のような入れ子の JSON テンプレートを受け付けます。値全体がプレースホルダーの場合は元の JSON の型が保たれます。URL のプレースホルダーはエンコードされ、固定の公開 HTTPS ホストのあとにのみ置けます。request_body がない場合、GET 以外のツールは { "arguments": {...}, "call": { "callId": "...", "agentId": "...", "toolCallId": "...", "idempotencyKey": "..." } } を受け取ります。response_path はドット記法でフィールドを選びます。単純な parameters も引き続き使えます。input_schema はさらに入れ子のオブジェクト、配列、列挙、数値の範囲に対応します。外部スキーマの参照と正規表現は使えません。
static_args:モデルが何を書いても毎回送られる値(店舗 ID など)。アドレスと本文の {{name}} も埋めます。extract:[{name, path}] で関数の回答のフィールド(data.id)を保持し、後の関数の {{name}} に使います。照会で得た customer_id をモデルに繰り返させずに続く注文に渡せます。開始時の照会のフィールドも同じように保持されます。reject_when:最大 5 件のルール {on: caller|agent, pattern, negate}。直近の発言がすべてを満たす間は関数を実行せず、エージェントに reject_message を返します。model_says:呼び出しのたびにモデルが 1 文を書き、関数の実行中にそれを話します。固定の speak_before の代わりです。連携 → Webhook と通知 → 連携アプリ でアプリを接続し、エージェントの「ナレッジとツール」タブで「既製の関数を追加」を使います。自前のエンドポイントは不要です。
check_availability(指定日の空き時間)と book_appointment(発信者が選んだ時間を予約)を追加します。Google カレンダーの時間は、設定したタイムゾーン、営業日、営業時間、予約の長さに従います。Cal.com はイベントタイプ自体の空き状況を使い、発信者がメールアドレスを伝えない場合は設定した代替のメールアドレスを使います。save_to_sheet を追加し、指定した列に通話の ID と時刻を加えて 1 行追記します。notify_team を追加し、エージェントが書いたメッセージを Slack チャンネル(Incoming Webhook)に投稿します。これらの関数は、接続したアカウントで EvaSay のコントロールプレーンが実行します。Worker もエージェント定義もアプリの認証情報を持ちません。API でエージェントを読み取ると、これらは kind: "app"(app、action、connection_id、app_config 付き)として表示されます。tools を置き換えるときに残すには、そのまま送り返してください。アプリの接続はダッシュボードでのみ行えます。
エージェントのツールの 1 つに run_at_start: true を設定すると、各通話の開始時、エージェントが挨拶や応答をする前に呼び出されます。caller_number、called_number、direction、channel、call_id という名前のパラメーターには値が入ります。返されたフィールドは first_message、system_prompt、idle_messages の {{variables}} を埋め(1 階層下のフィールドは {{customer.name}})、回答全体が背景情報としてエージェントに渡されます。変数を含む挨拶は回答を最大 3 秒待ち、見つからないものは空欄のままになります。開始時に実行できるツールはエージェントごとに 1 つです。
認証情報はワークスペースと接続先ごとにサーバー環境で設定されます。credential_ref の発行は support@evasay.com にご依頼ください。API キーをエージェント定義、ヘッダー、プロンプト、ツールのパラメーターに入れないでください。CRM の認証情報はアダプター自身の環境に置けます。
業務システムにリクエストを送る前にツールをプレビューします。
{
"arguments": { "customer_id": "customer-123" },
"dry_run": true,
"mock_result": { "name": "Alex", "order_status": "shipped" }
}
プレビューはパラメーターを検証し、変換後のリクエストを表示します。会話の実行や業務システムへの接続は行いません。実際に実行するには dry_run=false を設定します。書き込みの本番テストには、さらに confirmed=true と idempotency_key(8–128 文字の英数字、ドット、アンダースコア、コロン、ハイフン)が必要です。テストのレスポンスには ok、status、duration_ms と上限付きの結果が含まれ、認証情報は返しません。読み取り・書き込みのテストにはワークスペースの書き込み権限が必要です。
通話中、業務リクエストには固定の Idempotency-Key が付きます。業務エンドポイントはこれを尊重する必要があります。1 通話内の同一の書き込みは、タイムアウトで結果が不明になった操作も含めて重複排除されます。意図的に別の操作を行うには、operation_id のような別の業務引数が必要です。EvaSay は書き込みを自動で再実行しません。ツール実行イベントにはステータスと所要時間が含まれます。会話テスト機能はテキストのシミュレーションであり、ツール実行のテストではありません。
Streamable HTTP の MCP 接続を次のように設定します。
URL: https://evasay.com/api/v1/voice/mcp
Authorization: Bearer <your EvaSay API key>
たとえば Claude Code では:
claude mcp add --transport http evasay https://evasay.com/api/v1/voice/mcp --header "Authorization: Bearer $EVASAY_API_KEY"
接続するとツールにエージェントの作り方が伝わります。カタログを読み、下書きを作り、ナレッジ・関数・単語リストを加え、テストを書いて実行し、合格するまで下書きを直し、エージェントの dashboard_url を渡して試してもらい、公開と番号の割り当てはあなたの承認後にだけ行います。ドキュメントを読むツールは https://evasay.com/llms.txt から始められます。このページ、OpenAPI 定義、MCP のアドレスへのリンクがあります。
クライアントの安全なトークン設定または環境変数の設定を使ってください。エンドポイントは API キー認証を使い、OAuth のログインフローではありません。エージェントの作成・設定、ライブラリのツール、ナレッジ、Webhook、通話記録を公開します。公開と実際の発信は書き込み操作で、発信はクレジットと通信料金を消費します。ツールを呼び出すたびにワークスペースの権限を再確認します。書き込み操作にはクライアントの確認機能を有効にしてください。エンドポイントはステートレスな JSON レスポンスを使い、バックグラウンドの通知ストリームはありません。
コーディングツールへの指示の例:「EvaSay の下書きエージェントを作成し、CRM 照会エンドポイントを登録し、テスト引数でプレビューしてからエージェントに紐づけ、call.analyzed Webhook を設定して。」公開と実際の発信は、内容を確認してから明示的に許可してください。
POST /mcp/discover は { "url": "https://crm.example.com/mcp", "credential_ref": "crm" } を受け取り、サーバーのツールとスキーマを返します。選んだツールだけを登録します。
{
"name": "lookup_customer",
"description": "Find a CRM customer by confirmed ID.",
"kind": "mcp",
"url": "https://crm.example.com/mcp",
"mcp_tool": "get_customer",
"credential_ref": "crm",
"read_only": true,
"input_schema": {
"type": "object",
"properties": { "customer_id": { "type": "string" } },
"required": ["customer_id"]
}
}
エージェントが使えるのは登録したツールだけです。リモート接続は、リクエストへの JSON または SSE レスポンスを返す Streamable HTTP、公開 HTTPS の接続先、事前設定された認証情報に対応します。旧来の SSE エンドポイント、OAuth のネゴシエーション、サーバー起点の sampling/elicitation、バイナリ添付には対応しません。検出しても、サーバーのすべてのツールへのアクセスが自動で許可されるわけではありません。リモートツールのスキーマはインラインで、対応する JSON Schema のサブセットを使う必要があります。
POST /webhooks に { "url": "https://crm.example.com/evasay-events", "events": ["call.analyzed"] } を送って Webhook を作成します。レスポンスには購読と生成された署名シークレットが含まれ、シークレットは作成時にのみ返されます。EvaSay-Signature を検証し、EvaSay-Delivery で重複を排除して、CRM を更新します。イベント Webhook は非同期の通知で、通話中のツールは結果を直接エージェントに返します。