API 和網頁工作台使用同一套 Agent、通話和點數。在工作台建立的一切都能從你自己的系統操作,反過來也一樣。
| 功能 | 端點 |
|---|---|
| 列出 Agent | GET /api/v1/voice/agents |
| 建立 Agent | POST /api/v1/voice/agents |
| 取得 / 更新 Agent | 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 |
| Agent 的測試 | GET · POST /api/v1/voice/agents/{id}/tests |
| 執行測試 / 查看結果 | POST /api/v1/voice/agents/{id}/test-runs · GET /api/v1/voice/test-runs/{id} |
| 號碼接到 Agent | PATCH /api/v1/voice/phone-numbers/{id} |
| 詞庫 | GET · POST /api/v1/voice/vocabularies |
在工作台的 整合 → API 金鑰 建立金鑰(也可以在概覽頁一鍵建立),以 Bearer 權杖傳送:
Authorization: Bearer sk-your-key
金鑰在擁有者所在的工作區內、以該成員的角色權限執行:唯讀成員的金鑰可以讀取,但不能修改任何東西。
從範本建立 Agent,也可以在同一個請求裡設定欄位。不帶 template 時從空白開始;GET /api/v1/voice/catalog 列出所有範本,以及 Agent 可用的語言和聲音。範本只有英文和中文,其他語言請用該語言寫好 system_prompt 和 first_message 一起傳。整個請求要麼全部儲存,要麼不建立 Agent,所以被拒絕的請求修正後可以直接重送。回應裡的 dashboard_url 是這個 Agent 在主控台的頁面,可以在瀏覽器裡直接和草稿通話。所有 Agent 都使用 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}];Agent 依 when 選擇 |
transfer_briefing | 布林值;接聽的人先聽到 Agent 對這通電話的摘要 |
pronunciations | 最多 100 筆 [{word, say, case_sensitive}];遇到 word 時聲音念成 say |
idle_messages | 最多 3 句提醒,依序說給沒出聲的來電者,最後一句之後結束通話 |
noise_reduction | 布林值;先過濾來電者的背景噪音再聽 |
recording_enabled | 布林值;以雙聲道錄音(左聲道來電者,右聲道 Agent) |
recording_consent | announce、ask(來電者同意才保留)或 none |
recording_notice | 最先說的告知或詢問;留空則用通話語言的內建說法 |
handoffs | 最多 5 筆 [{agent_id, when}]:這個 Agent 可以把通話交給的其他 Agent |
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(男聲)。不會說 Agent 語言的內建聲音,會換成會說這種語言的同性別聲音。舊欄位名稱 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 裡。
修改 Agent 的欄位只會改到草稿。用 POST /api/v1/voice/agents/{id}/publish(可附 note)發布草稿,或在同一個 PATCH 裡帶 status: "published" 之前,通話都繼續使用已發布的版本。每次發布都會保留一個編號版本;Agent 的 published_version 表示通話使用哪一版。通話開始時是哪一版就一直用到結束,所以通話中發布不會影響它。工作台的試打使用草稿。
GET /agents/{id}/versions 列出版本,POST /agents/{id}/versions/{version}/restore 回復:新通話改用該版本,草稿也變成該版本。Agent 的名稱、部署位置、掛件外觀和通知不做版本管理,修改後立即生效,知識庫裡的文件也一樣。
一個測試用文字模擬一通電話:模擬來電者照 scenario 扮演,Agent 用草稿(指令、開場白和知識庫)回答,再由評審依 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 執行 Agent 的全部測試(或 test_ids 裡的幾個),每個測試回傳一次執行(status: "running")。輪詢 GET /test-runs/{id},直到 status 變成 passed、failed 或 error,這時執行裡有 transcript 和評審的 reason(預設英文,reason_language: "zh" 為中文)。每次執行都會扣點數,金額顯示在主控台的測試頁;餘額不夠時從付不起的那個開始不再執行,not_started 列出沒執行的測試。沒跑完的執行會退回點數。測試檢查的是 Agent 說了什麼,不檢查聲音和延遲,也不會真的呼叫函式:函式請用 POST /tools/{id}/test 預覽。每個 Agent 最多 30 個測試,PATCH、DELETE /agents/{id}/tests/{test_id} 修改或刪除。
除了完整通話(type: "simulation",預設),測試也可以只檢查一句回覆。type: "next_reply" 需要 chat_history,也就是這句回覆之前、以來電者結尾的對話([{role, text}],role 為 caller 或 agent),以及判斷 Agent 下一句回覆的 success_criteria。type: "tool_call" 需要 chat_history 和 expected_tool {name, arguments, match}:Agent 應該呼叫 name(它的某個函式,或 end_call、transfer_call、handoff_to_agent),並傳入 arguments;參數依精確值比較(去掉首尾空白、不分大小寫),設為 match: "judge" 時依意思比較;Agent 多傳的參數不檢查。執行的 transcript 裡,tool_calls 記錄 Agent 呼叫了哪些函式。
POST /agents/{id}/tests/generate 根據 Agent 的草稿(指令、知識庫和函式)產生測試草稿。參數有 count(1–10,預設 5)、types、focus(要涵蓋的情況)和 language(en 或 zh,用於名稱和標準),依一次執行扣點數,回傳的草稿可以檢查後提交給 POST /agents/{id}/tests;帶 save: true 時直接儲存。
hooks 在通話的某個時刻做事,最多 10 條規則:on: "silence"(Agent 說完後來電者 seconds 秒都沒說話,3–600;再次開口前只觸發一次)、on: "time_elapsed"(通話進行到第 seconds 秒,10–3600)或 on: "call_ending"。每條規則最多 do 3 個動作:{type: "say", text}、{type: "transfer", destination?}、{type: "end_call", text?} 和 {type: "tool", name, args?};通話結束時的規則只執行函式。被規則結束的通話,ended_reason 是 rule_ended。
電話通話中,來電者按的鍵會作為回答傳給 Agent:以 # 或停頓兩秒結束輸入。外撥時,Agent 也能按鍵通過企業的語音選單。
transfer_number 是 Agent 把來電者轉給真人的主要號碼,也是工作區點數用完時撥打的號碼。transfer_destinations 可加上具名號碼,各附一句什麼時候用(「帳務:發票和退款問題」),由 Agent 選擇。開啟 transfer_briefing 時,Agent 轉接時會寫一段簡短摘要,接聽的人在來電者接進來之前,會先以 EvaSay 聲音、用 Agent 的摘要語言聽到它。這時通話的 ended_reason 是 transferred。
設定 handoffs 後,Agent 可以把通話交給工作區裡另一個已發布的 Agent,例如櫃檯把預約交給預約 Agent。Agent 依 when 選擇。另一個 Agent 會帶著目前為止的完整對話接手同一通電話,用自己的指令、聲音、知識庫、工具和交接設定說話。通話仍列在第一個 Agent 名下、由它分析和計費;逐字稿會標出每次交接。
號碼在主控台裡用你自己的電信商帳號連接。GET /api/v1/voice/phone-numbers 列出號碼,PATCH /api/v1/voice/phone-numbers/{id} 傳 { "agent_id": "AGENT_ID" } 會把這個號碼的來電交給某個 Agent(使用它已發布的版本),傳 "agent_id": null 則不接任何 Agent;label 修改號碼名稱。
詞庫放來電者會說、但辨識容易聽錯的名稱和術語,例如產品名、藥名、街道名。POST /api/v1/voice/vocabularies 傳 { "name": "Products", "terms": ["Lumora", "Vantix Pro"] }(最多 1,000 個詞,每個不超過 50 個字元,也可以用 "preset": "medical" 或 "legal" 起步),再把 Agent 的 vocabulary_id 設成它。GET、PATCH(name、terms)和 DELETE /vocabularies/{id};還有 Agent 在用的詞庫不能刪除。
每個號碼在電話號碼頁面都有來電規則,在 Agent 接聽前套用:封鎖的號碼或號碼開頭(+1555),以及隱藏號碼的來電者,都會不接聽直接拒絕、不計費;指定的來電者可以直接轉到另一個號碼;Agent 無法接聽時(未發布、沒有 Worker、點數用完且沒有轉接號碼),來電可以轉到一個號碼或播放你自己的訊息。開場查詢(run_at_start)也可以回傳 {"reject": true, "message": "..."} 來拒絕來電者:Agent 有訊息就先說出來再掛斷,ended_reason 為 rejected。
用工作區的一個電話號碼撥一通電話。每個請求都需要唯一的 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" }
}'
工作區有不止一個電話號碼時必須帶 from。variables 會填入 Agent 開場白和提示詞裡 {{customer_name}} 這類佔位符。
GET /api/v1/voice/calls/{id} 傳回通話的狀態、時長、扣除的點數、摘要、分析和逐字稿。用 GET /api/v1/voice/calls?agent_id=&status=&channel=&user_id=&page= 列出通話(user_id:你給這個人的 ID,來自網頁通話權杖)。其中的 turns 是通話的逐輪除錯紀錄:從來電者說完到聽到回答,時間花在哪裡,單位毫秒——waitMs(說話結束)、sttMs(辨識)、llmMs(模型第一個字,每輪函式呼叫多一個)、ttsMs(聲音第一個音)、firstAudioMs(聽到回答)——以及呼叫的函式、打斷和錯誤。通話頁面也會顯示。
開啟 recording_enabled 後,每通電話會錄成雙聲道 WAV:左聲道是來電者,右聲道是 Agent,取樣率與通話相同。recording_consent: "announce" 時 Agent 先告知正在錄音;"ask" 時先詢問,來電者同意才保留錄音(沒有回答視為不同意);"none" 時什麼都不說。請確認你和來電者所在地的法律要求。
錄音存放在 EvaSay 的儲存空間,或你在 整合 → Webhook 與通知 → 錄音儲存 設定的 S3 相容儲存貯體(AWS S3、Cloudflare R2、Google Cloud Storage、Supabase、MinIO)。執行通話的 Worker 直接把檔案上傳到儲存貯體。錄音存好後,GET /api/v1/voice/calls/{id} 會傳回一小時內有效的 recordingUrl,以及 recording.status(recording、stored、declined 或 failed)。
DELETE /api/v1/voice/calls/{id} 刪除一通已結束的通話,連同逐字稿、錄音(EvaSay 儲存空間或你自己的儲存桶中的)、分析和 Webhook 投遞紀錄。已用的 Credits 仍保留在點數紀錄中。暫時無法刪除的通話會回傳 409,code 為 call_not_deletable_yet:通話仍在進行或剛結束、仍在結算、錄音可能仍在上傳,或欠著餘額不足以支付的 Credits(請先儲值)。
有人要求刪除自己的資料時,使用 POST /api/v1/voice/calls/delete-by-caller:傳 { "phone_number": "+14155550123" } 會刪除這個號碼的所有通話(撥入看來電號碼,撥出看被叫號碼),傳 { "user_id": "..." } 會刪除權杖中帶這個 user_id 的所有網頁通話。回傳 { "deleted": 12, "kept_back": { "settling": 1 }, "more": false }:每次最多 200 通,more 為 true 時再呼叫一次;kept_back 中的通話稍後再刪。號碼若在拒撥名單中,會繼續保留在名單上。工作台的 通話記錄 → 刪除來電者的資料 做的是同一件事。
通話進行中傳送 POST /api/v1/voice/calls/{id}/control。執行通話的 Worker 約 2 秒內收到指令,API 回應 202。執行通話的 Worker 無法連線時,API 回應 503(code call_unreachable),指令不會執行。
type | 效果 |
|---|---|
say | Agent 說完目前這句後說出 text;帶 "interrupt": true 則立刻說 |
note | 把 text 告訴 Agent(不念出來),從下一次回答起納入考量;逐字稿會標記 |
end | 有 text 就先說再掛斷;沒有 text 的電話通話立即掛斷 |
這時通話的 ended_reason 是 api_ended。工作台的通話頁面也有同樣的控制。
POST /api/v1/voice/calls/{id}/listen(可帶 { "channels": 2 })傳回兩分鐘內可開啟的 listen_url。這是一個 WebSocket:先收到 JSON 訊息 {"type":"start","sampleRate":8000,"channels":1,"encoding":"pcm_s16le"},接著是 16 位元小端序 PCM 的二進位影格,內容是來電者聽到的聲音(兩邊混音;channels: 2 時左聲道來電者、右聲道 Agent)。通話結束時關閉。音訊直接來自執行通話的 Worker,不經過 EvaSay 的控制平面;工作台通話頁面的旁聽按鈕用的是同一個網址。
從你自己的程式收發通話音訊,例如你自己的電話系統或 App。先發起通話:
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(16 位元小端序 PCM,sample_rate 8000、16000 或 24000)或 mulaw(8 kHz μ-law)。音訊以該格式的二進位影格雙向傳送,每影格最好 20 ms。文字影格為 JSON:
| 方向 | 訊息 |
|---|---|
| 你 → EvaSay | {"type":"dtmf","digits":"12#"} 按鍵;{"type":"end"} 掛斷;{"type":"text","text":"…"} 來電者打的字(點按鈕回覆時帶 "input":"button");{"type":"context","text":"…","key":"cart"} 你的 App 回報的情況(Agent 不會回答);{"type":"tool_result","id":"…","result":{…}}(或 "error")頁面函式的結果 |
| EvaSay → 你 | start(帶 callId);clear(來電者打斷:丟掉還沒播放的 Agent 音訊);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,之後關閉連線) |
一連上 Agent 就會打招呼。請求裡可以加上 user_id 和 page_tools(你的 App 回答的頁面函式,見下文)。通話和其他通話一樣按分鐘計費,也有相同的通話紀錄、分析和 Webhook。
網頁通話(掛件、代管通話頁、你的 App)中,來電者在語音旁邊還會看到聊天介面,所以 Agent 能做的不只是說話。電話來電者沒有這些。
開啟 Agent → 知識庫與工具 → 傳給客戶(Agent API 中為 send_to_caller),Agent 就能在來電者畫面上一次顯示最多五張卡片:每張可有標題、標題下一行字、描述、圖片、連結和最多兩個回覆按鈕;也可以在問題下方提供最多三個快速回覆。連結永遠不會念出來。
Agent 只能傳送這通電話中給過它的連結和圖片:它的指令和開場白、知識庫(從網頁讀入的文件帶有網址)、你的函式回傳的結果,以及預設卡片。連結也必須在 allowed_hosts 之一(預設卡片自己的網站一律允許)。Agent 自己編的連結或來電者念出的連結都不會傳送。Agent 話語中這些網站的連結,在聊天裡可以點擊。
"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)。一通電話最多傳送 10 次。
通話中來電者也可以打字(電子郵件、訂單編號)或點回覆按鈕;Agent 像聽到說話一樣回答,正在說話時會先停下。每則最多 500 字,每秒 1 則,每通電話 100 則。通話紀錄會標出這一輪是打字還是點按鈕。
掛件會在你的網頁上提供一個 evasay 物件。全部可選;只有當網頁在掛件允許的網站中(Agent → 部署),通話視窗才會接收網頁的訊息:
<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}}。它們來自訪客的瀏覽器,所以會告訴 Agent 這些值未經驗證,而且永遠不會傳給你的函式或開場查詢。update 告訴 Agent 網頁上發生了什麼,但不要求它回答;同一個 key 的新更新會取代舊的。"kind": "page",沒有 url)的函式,並在網頁上註冊同名函式。Agent 只能在來自已註冊該函式的網頁的通話中呼叫它;除非 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 秒內可用一次,30–3600,預設 600)、agent(slug)和 web_call_url。在掛件的 script 上加 data-token-url="/your/endpoint" 把權杖交給網頁(它會帶著訪客的 Cookie POST 到那裡,讀取 { "token": "…" }),或使用 agent.setToken(async () => token)。權杖裡的 variables 是經過驗證的:指令、開場查詢和你的函式範本都可以使用,user_id 也一樣,並會保留在通話上(通話的 userId,篩選通話用 user_id)。API 金鑰請留在你的伺服器上。
在你自己的 App 裡,把 { "token": "…", "pageTools": ["add_to_cart"] } POST 到 https://evasay.com + web_call_url,得到這通電話的連線資訊(WebRTC offer 網址或 WebSocket 網址,帶通話權杖),用 RTVI 用戶端連線,例如 Pipecat 的 Web、iOS、Android、React Native 用戶端 SDK。卡片、頁面函式和提示以 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 中繼可以直接打給你的 Agent;通話費由你的 SIP 服務商收取:
sip:ev…@<EvaSay 的 SIP 網域>,只接受撥入,適用於 PBX(3CX、FreePBX、Asterisk…)和 SIP 電話。from);轉接真人時以 REFER 交給中繼。中繼要求驗證時,EvaSay 會用你填的中繼使用者名稱和密碼回應。撥入的電話以 EvaSay 提供的使用者名稱和密碼做 digest 驗證(realm 是 SIP 網域);也可以限制來源 IP,或在服務商無法驗證時只依 IP 接受(這時 IP 為必填)。每個 SIP 位址或中繼都有同時通話上限,超過時回應 486。密碼只在建立或重設時顯示一次。僅支援 UDP 和 G.711(PCMU、PCMA);按鍵使用 RFC 4733 或 SIP INFO;請關閉 PBX 中繼的註冊(REGISTER)。
外撥任務用一個 Agent 撥打一份聯絡人名單,在撥打時段和併發上限內進行(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" }
]
}'
聯絡人也可以是 CSV 文字,第一欄是電話號碼。必須帶 consent_confirmed: true:表示你確認名單上每個人都同意接聽來電(包括 AI 語音來電),EvaSay 會把這份確認和任務一起保存(consent_confirmed_at)。任務建立時是草稿;用 POST /api/v1/voice/campaigns/{id}/start 開始,pause 和 cancel 用法相同。開始任務前 Agent 必須已發布。
開啟 voicemail_detection(預設)時,外撥會先等對方說話,Agent 再打招呼;對方一直不出聲時,Agent 在兩三秒後打招呼。Agent 聽到語音信箱或電信業者的錄音時,會等問候語和嗶聲結束,留下 voicemail_message,沒有設定就掛斷。這類通話以 ended_reason voicemail 結束,按實際時長計費,不做分析也不發通知。
外撥任務中,進了語音信箱的號碼會像沒接的號碼一樣再撥,留言只在最後一次嘗試時留下,所以不會有人收到兩次同樣的留言。
每通外撥,無論來自外撥任務或 POST /api/v1/voice/calls,都會先檢查工作區的拒撥名單。名單上的號碼絕不撥打:API 回應 409,code 為 do_not_call;外撥任務則把該聯絡人標為 canceled、outcome 為 do_not_call,接著撥下一位。
Agent 撥出的電話中,對方要求不要再打來時,Agent 會自己把號碼加入名單、告知對方,並傳送 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 把一個號碼移出名單;只有在對方同意接聽時才這麼做。
POST /api/v1/voice/voices 用一位說話者的 WAV 錄音建立聲音(multipart audio、name、language、consent=true)。回應 202,狀態為 running;輪詢 GET /api/v1/voice/voices 直到變成 ready,再把 Agent 的 voice_id 設為它的 voice_id。language 是錄音所說的語言,為上述 Agent 語言之一;這個聲音會說中文以外的所有語言,中文由同性別的內建聲音來說,在錄音所用的語言裡最像本人。每個複製聲音都會扣點數,建立前會在工作台顯示。
在 整合 → Webhook 與通知 新增 https 網址來接收通話事件:
| 事件 | 時機 |
|---|---|
call.started | Agent 接起電話 |
call.ended | 通話結束,含時長和點數 |
call.analyzed | 摘要、結果、擷取欄位和逐字稿已產生 |
call.transcript | 通話中每隔幾秒傳送新增的逐字稿 |
call.tool_called | Agent 呼叫了你的函式:name、arguments、status、durationMs |
call.handed_off | 通話交給了另一個 Agent:fromAgentId、toAgentId |
call.transferred | 通話轉接給真人:destination |
call.voicemail | 外撥接到語音信箱 |
call.do_not_call | 外撥時對方要求不要再打來:phone |
call.recording_ready | 雙聲道錄音已儲存:recordingUrl(24 小時內有效)、durationMs |
call.message_sent | Agent 在網頁或 App 來電者畫面上顯示了卡片: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} 重新傳送一筆。
除了電子郵件和 alert.triggered / alert.resolved Webhook,警示也可以在 Webhook 與通知 → 警示 → 同時傳到 設定傳到 Slack 頻道(Incoming Webhook)、企業微信群組機器人或 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 | 金鑰是唯讀的,或 Agent 不可用 |
| 404 | 這個工作區沒有這個 Agent、通話或外撥任務 |
| 409 | 狀態不對(例如開始一個進行中的任務)、服務商帳號未連接,或號碼在拒撥名單上(do_not_call) |
| 429 | 請求太多;放慢速度後重試 |
工作區最近 30 天的語音 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 |
| 把工具綁定到 Agent | 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"] } 把它的定義綁定到 Agent。這會以快照取代整份工具清單;工具庫裡的工具變更後請重新綁定。刪除工具庫定義不影響已有的快照。也可以用 Agent API 的 tools 欄位讀取和取代內嵌定義。分析和掛件用 Agent API 的 analysis_config 和 widget_config 設定。
工具可以用 GET、POST、PUT、PATCH 或 DELETE。query 和 request_body 接受巢狀 JSON 範本,例如 { "customer": { "id": "{{customer_id}}" } }。整個值都是佔位符時保留原本的 JSON 型別。URL 佔位符會被編碼,而且只能出現在固定的公開 HTTPS 主機之後。沒有 request_body 時,非 GET 的工具會收到 { "arguments": {...}, "call": { "callId": "...", "agentId": "...", "toolCallId": "...", "idempotencyKey": "..." } }。response_path 用點號路徑選取欄位。簡單的 parameters 仍然接受;input_schema 另外支援巢狀物件、陣列、列舉和數值範圍。不接受外部 schema 參照和正規表示式。
static_args:不論模型寫什麼都每次帶上的值,例如門市編號;也會填入網址和請求內容裡的 {{name}}。extract:[{name, path}] 保留函式結果中的欄位(data.id),供後續函式的 {{name}} 使用,查詢得到的 customer_id 就能直接帶進接下來的下單,不用模型再說一次。開場查詢的欄位也以同樣方式保留。reject_when:最多 5 條規則 {on: caller|agent, pattern, negate};最後一句話全部符合時不執行函式,Agent 收到 reject_message。model_says:每次呼叫時由模型寫一句話,在函式執行期間說出,取代固定的 speak_before。在 整合 → Webhook 與通知 → 連接的應用程式 連接應用程式,再到 Agent 的知識庫與工具分頁使用「加入現成函式」;不需要你自己的端點:
check_availability(某天的空閒時段)和 book_appointment(預約來電者選的時間)。Google 日曆的時段依你設定的時區、營業日、營業時間和預約長度;Cal.com 使用活動類型本身的可預約時段,來電者沒給電子郵件時使用你設定的備用信箱。save_to_sheet,依你列出的欄位新增一列,再加上通話 ID 和時間。notify_team,把 Agent 寫的訊息發到 Slack 頻道(Incoming Webhook)。這些函式由 EvaSay 控制平面以你連接的帳號執行;Worker 和 Agent 定義都不持有應用程式的憑證。透過 API 讀取 Agent 時,它們顯示為 kind: "app"(帶 app、action、connection_id、app_config);取代 tools 時原樣傳回即可保留。應用程式只能在工作台連接。
在 Agent 的某個工具上設定 run_at_start: true,每通電話開始時、Agent 打招呼或回答之前就會呼叫它。名為 caller_number、called_number、direction、channel 或 call_id 的參數會自動帶入。回傳的欄位會填入 first_message、system_prompt 和 idle_messages 裡的 {{variables}}(下一層的欄位寫成 {{customer.name}}),完整結果也會作為背景交給 Agent。開場白有變數時最多等 3 秒;查不到的留空。每個 Agent 只能有一個工具在開場時執行。
憑證依你的工作區和目的地設定在伺服器環境中;請寫信到 support@evasay.com 申請 credential_ref。不要把 API 金鑰放進 Agent 定義、標頭、提示詞或工具參數。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,你的業務端點必須遵守它。同一通電話中相同的寫入會被去重,包括逾時後結果未知的操作。若要刻意做另一個操作,需要不同的業務參數,例如 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"
連接後會告訴 AI 工具怎樣建 Agent:先讀取可選的語言、聲音和範本,建草稿,加知識庫、函式和詞庫,寫測試並執行,改草稿直到通過,把 Agent 的 dashboard_url 給你試打,等你確認後才發布和接號碼。會讀文件的工具可以從 https://evasay.com/llms.txt 開始,裡面有本頁、OpenAPI 描述和 MCP 位址。
請使用用戶端的安全權杖或環境變數設定。這個端點使用 API 金鑰驗證,不是 OAuth 登入流程。它提供 Agent 建立 / 設定、工具庫、知識庫、Webhook 和通話紀錄。發布和撥打真實電話是寫入動作;外撥會消耗點數和電信費。每次呼叫工具都會重新檢查工作區權限。請為寫入動作開啟用戶端的確認機制。端點使用無狀態的 JSON 回應,不提供背景通知串流。
給程式設計工具的指令範例:「建立一個 EvaSay 草稿 Agent,註冊我的 CRM 查詢端點,用測試參數預覽,綁定到 Agent,再設定 call.analyzed Webhook。」發布和真實通話請先檢查並明確授權。
POST /mcp/discover 接受 { "url": "https://crm.example.com/mcp", "credential_ref": "crm" },傳回伺服器的工具和 schema。只註冊選定的工具:
{
"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"]
}
}
只有註冊過的工具 Agent 才能使用。遠端連線支援以 JSON 或 SSE 回應請求的 Streamable HTTP、公開 HTTPS 目的地和預先設定的憑證。不支援舊版 SSE 端點、OAuth 協商、伺服器發起的 sampling/elicitation 和二進位附件。探索不會自動授權存取伺服器的所有工具。遠端工具的 schema 必須是內嵌的,並使用支援的 JSON Schema 子集。
用 POST /webhooks 傳送 { "url": "https://crm.example.com/evasay-events", "events": ["call.analyzed"] } 建立 Webhook。回應包含訂閱和產生的簽章密鑰,密鑰只在建立時傳回。驗證 EvaSay-Signature、依 EvaSay-Delivery 去重,再更新你的 CRM。事件 Webhook 是非同步通知;通話中的工具會直接把結果傳回給 Agent。