API працює з тими самими агентами, дзвінками та кредитами, що й вебпанель. Усе, що ви створюєте в панелі, можна вести зі своїх систем, і навпаки.
| Що | Endpoint |
|---|---|
| Список агентів | 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; перед першим повідомленням агент каже мовою дзвінка, що абонент розмовляє зі штучним інтелектом (пропускається, якщо перше повідомлення вже про це говорить) |
voicemail_detection | Логічне значення; під час вихідних дзвінків агент спершу слухає й завершує дзвінок, потрапивши на автовідповідач |
voicemail_message | Залишається на автовідповідачі, може містити {{variables}}; якщо не задано — агент кладе слухавку |
transfer_number | Номер для переведення дзвінка, у міжнародному форматі |
transfer_destinations | До 9 додаткових номерів, [{name, number, when}]; агент обирає один за when |
transfer_briefing | Логічне значення; той, хто відповів першим, чує стислий переказ дзвінка від агента |
pronunciations | До 100 [{word, say, case_sensitive}]; голос вимовляє say замість word |
idle_messages | До 3 нагадувань, які по черзі вимовляються мовчазному абонентові, перш ніж дзвінок завершиться |
noise_reduction | Логічне значення; фоновий шум абонента фільтрується, перш ніж його почують |
recording_enabled | Логічне значення; дзвінки записуються у два канали (абонент ліворуч, агент праворуч) |
recording_consent | announce, ask (запис зберігається, лише якщо абонент погоджується) або none |
recording_notice | Повідомлення або запитання, що вимовляється першим; порожньо — вбудований текст мовою дзвінка |
handoffs | До 5 [{agent_id, when}]: інші агенти, яким цей може передати дзвінок |
knowledge_base_id | Одна з ваших баз знань |
vocabulary_id | Один з ваших словників; його терміни розпізнаються надійніше, коли їх вимовляє абонент |
voice_id | Ідентифікатор вбудованого голосу EvaSay або один із ваших клонованих голосів (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.
Зміна полів агента змінює його чернетку. Дзвінки й далі використовують опубліковану версію, доки ви не опублікуєте чернетку через POST /api/v1/voice/agents/{id}/publish (за бажання з note) або з status: "published" у тому самому PATCH. Кожна публікація зберігає пронумеровану версію; published_version в агента показує, яку версію використовують дзвінки. Дзвінок лишається на версії, що була активною на момент його початку, тож публікація під час дзвінка його не змінює. Тестові дзвінки з панелі використовують чернетку.
GET /agents/{id}/versions виводить список версій, а POST /agents/{id}/versions/{version}/restore відкочує: нові дзвінки використовують цю версію, і чернетка стає нею. Назва агента, розгортання, вигляд віджета та сповіщення не версіонуються й змінюються одразу, як і документи його бази знань.
Тест розігрує один дзвінок у вигляді тексту: імітований абонент грає 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, і відповідає одним прогоном на тест (status: "running"). Опитуйте GET /test-runs/{id}, доки status не стане passed, failed або error; тоді прогон містить transcript і reason оцінювача (англійською або китайською з reason_language: "zh"). Кожен прогон коштує Credits — сума показана біля тестів у панелі; прогони зупиняються на першому, який не покриває баланс, а not_started перелічує решту. Прогон, який не вдалося провести, повертається. Тести перевіряють, що каже агент, а не голос чи темп, і не викликають його функцій: їх перевірте заздалегідь через POST /tools/{id}/test. Агент має до 30 тестів; PATCH і DELETE /agents/{id}/tests/{test_id} змінюють або видаляють тест.
Окрім цілих дзвінків (type: "simulation", типово), тест може перевіряти одну відповідь. 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, для назв і критеріїв), коштує Credits як один прогін і повертає чернетки, які треба переглянути й надіслати в POST /agents/{id}/tests; з save: true вони зберігаються.
hooks щось робить у певний момент дзвінка, до 10 правил: on: "silence" (абонент мовчить 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.
Під час телефонних дзвінків натиснуті абонентом клавіші доходять до агента як відповідь: уведення закінчується на # або після двосекундної паузи. Під час вихідних дзвінків агент може й сам натискати клавіші, щоб пройти телефонне меню компанії.
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"] } (до 1000 термінів по 50 символів, або "preset": "medical" чи "legal" для початку), потім задайте vocabulary_id агента. GET, PATCH (name, terms) і DELETE /vocabularies/{id}; словник, яким користується агент, видалити не можна.
Кожен номер має правила дзвінків на сторінці «Номери телефонів», які застосовуються до того, як відповість агент: заблоковані номери або початки номерів (+1555) і абоненти, що приховують номер, отримують відмову без відповіді й нічого не коштують; абонентів зі списку можна одразу з'єднати з іншим номером; а коли агент не може відповісти (не опублікований, немає воркера, закінчилися кредити й немає номера для переведення), дзвінок може піти на номер або програти ваше власне повідомлення. Початковий запит (run_at_start) також може відхилити абонента, відповівши {"reject": true, "message": "..."}: агент вимовляє повідомлення, якщо воно є, і кладе слухавку, а 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 підставляються в заповнювачі на кшталт {{customer_name}} у першому повідомленні та промпті агента.
GET /api/v1/voice/calls/{id} повертає статус дзвінка, тривалість, списані кредити, короткий зміст, аналіз і розшифровку. Список дзвінків — GET /api/v1/voice/calls?agent_id=&status=&channel=&user_id=&page= (user_id: ваш власний ідентифікатор людини з токена вебдзвінка). Його turns — покроковий налагоджувальний журнал дзвінка: куди пішов час від кінця репліки абонента до моменту, коли він почув відповідь, у мілісекундах — waitMs (кінець мовлення), sttMs (розпізнавання), llmMs (перше слово моделі, плюс по одному на кожен раунд функцій), ttsMs (перший звук голосу), firstAudioMs (відповідь почуто) — з викликаними функціями, перебиваннями та помилками. Його показує й сторінка дзвінка.
З recording_enabled кожен дзвінок записується як двоканальний WAV: абонент ліворуч, агент праворуч, із частотою дискретизації дзвінка. За recording_consent: "announce" агент спершу повідомляє, що дзвінок записується; за "ask" — запитує, і запис зберігається, лише якщо абонент погоджується (відсутність відповіді вважається відмовою); за "none" нічого не кажеться. З'ясуйте, що вимагає закон там, де перебуваєте ви та ваші абоненти.
Записи зберігаються у сховищі EvaSay або у вашому власному бакеті, сумісному з S3 (AWS S3, Cloudflare R2, Google Cloud Storage, Supabase, MinIO), що задається в Інтеграції → Вебхуки та сповіщення → Сховище записів. Воркер, який веде дзвінок, завантажує файл просто в бакет. GET /api/v1/voice/calls/{id} повертає recordingUrl, чинний одну годину після збереження запису, та recording.status (recording, stored, declined або failed).
DELETE /api/v1/voice/calls/{id} видаляє завершений дзвінок разом із розшифровкою, записом (у сховищі EvaSay або у вашому бакеті), аналізом і надсиланнями вебхуків. Витрачені Credits лишаються в історії кредитів. Дзвінок, який поки не можна видалити, відповідає 409 з code call_not_deletable_yet: він ще триває або щойно завершився, ще оплачується, запис, можливо, ще завантажується, або за ним є борг у Credits, який не покрив баланс (спершу поповніть 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, поки дзвінок триває. Воркер, який веде дзвінок, підхоплює команду приблизно за 2 секунди; API відповідає 202. Якщо воркер, який веде дзвінок, недоступний, 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 }) повертає listen_url, яку можна відкрити протягом двох хвилин. Це WebSocket: спершу JSON-повідомлення {"type":"start","sampleRate":8000,"channels":1,"encoding":"pcm_s16le"}, потім бінарні кадри 16-бітного little-endian PCM із тим, що чує абонент (обидва голоси змішані; з channels: 2 абонент ліворуч, агент праворуч). Він закривається, коли дзвінок завершується. Звук іде прямо від воркера, що веде дзвінок, оминаючи площину керування EvaSay; кнопка «Слухати» на сторінці дзвінка в панелі використовує ту саму адресу.
Надсилайте й приймайте аудіо дзвінка зі своєї програми, наприклад власної телефонної системи чи застосунку. Розпочніть дзвінок:
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-бітний little-endian PCM із sample_rate 8000, 16000 або 24000) або mulaw (μ-law, 8 кГц). Аудіо йде в обидва боки бінарними кадрами в цьому форматі, в ідеалі по 20 мс. Текстові кадри — 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 (функції сторінки, на які відповідає ваш застосунок, див. нижче). Дзвінки тарифікуються поминутно, як і будь-які інші, з тими самими записами дзвінків, аналізом і вебхуками.
Під час вебдзвінка (віджет, розміщена сторінка, ваш застосунок) абонент бачить чат поруч із голосом, тож агент може не лише говорити. В абонентів, що телефонують, цього немає.
Увімкніть Агент → Інструменти → Надіслати абоненту (send_to_caller в API агента), і агент зможе виводити на екран абонента до п'яти карток одночасно: заголовок, рядок під ним, опис, зображення, посилання та до двох кнопок відповіді в кожній; він також може запропонувати до трьох швидких відповідей під запитанням. Посилання ніколи не зачитуються вголос.
Агент може надсилати лише ті посилання й зображення, що були йому надані в цьому дзвінку: його інструкції та привітання, база знань (документ, прочитаний з вебсторінки, має її адресу), відповіді ваших функцій і готові картки. Крім того, посилання має вести на один з 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 (channel, items із title та url, quickReplies, blocked). За дзвінок надсилання відбувається не більше ніж 10 разів.
Під час дзвінка абонент може також вводити текст (адресу електронної пошти, номер замовлення) або натискати кнопку відповіді; агент відповідає на це як на мовлення, попередньо замовкнувши, якщо говорив. До 500 символів у повідомленні, одне на секунду, 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 }); // for the next call
agent.update('Cart: 2 items, $120', { key: 'cart' }); // during it
agent.registerTool('add_to_cart', async function (args) {
// your code, in the visitor's browser
return { ok: true, checkout: 'https://shop.example.com/checkout' };
});
agent.on('cards', function (cards) {}); // also 'live', 'ended'
});
</script>
setVariables підставляються в {{name}} в інструкціях і привітанні. Вони надходять із браузера відвідувача, тому агентові повідомляється, що вони не перевірені, і вони ніколи не потрапляють до ваших функцій і початкового запиту.update повідомляє агентові, що відбувається на сторінці, не змушуючи його відповідати; новіше оновлення з тим самим key замінює попереднє."kind": "page", без url) і зареєструйте на своїй сторінці функцію з такою самою назвою. Агент може викликати її лише в дзвінках зі сторінки, що її зареєструвала; він чекає на відповідь до timeout_sec (1–10, за замовчуванням 5), якщо тільки wait_for_result не дорівнює false. Вона виконується від імені відвідувача, як його власний клік: запитуйте підтвердження на своїй сторінці перед усім, що коштує грошей або є незворотним. Посилання з її відповіді можуть надсилатися як картки.Установіть Хто може почати дзвінок на Лише з токеном і нехай ваш сервер отримує одноразовий токен для користувача, що увійшов:
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 (слаг) і web_call_url. Передайте токен сторінці через data-token-url="/your/endpoint" у скрипті віджета (він надсилає туди POST із куками відвідувача й читає { "token": "…" }) або через agent.setToken(async () => token). variables токена перевірені: їх можуть використовувати інструкції, початковий запит і шаблони ваших функцій, як і user_id, який дзвінок зберігає (userId у дзвінку, user_id для фільтрації дзвінків). Зберігайте свій API-ключ на сервері.
У власному застосунку надішліть POST { "token": "…", "pageTools": ["add_to_cart"] } на https://evasay.com + web_call_url. У відповіді — з'єднання дзвінка (адреса WebRTC-пропозиції або адреса WebSocket із токеном дзвінка) для клієнта RTVI, наприклад клієнтських SDK Pipecat для вебу, iOS, Android і React Native. Картки, функції сторінки та сповіщення приходять як RTVI server-message (evasay.cards, evasay.tool, evasay.notice); набрані слова надсилайте через send-text, а повідомлення та відповіді сторінки — як client-message evasay.context та evasay.tool_result. Або передавайте аудіо самі через Аудіо через WebSocket, указавши user_id та page_tools.
У розділі Номери телефонів → SIP ваша власна АТС, SIP-телефони або SIP-транк можуть телефонувати вашим агентам напряму; дзвінки тарифікує ваш SIP-провайдер:
sip:ev…@<EvaSay's SIP domain>, лише вхідні дзвінки, для АТС (3CX, FreePBX, Asterisk…) і SIP-телефонів.from); переведення на людину передаються транку через REFER. Коли транк запитує облікові дані, EvaSay відповідає іменем користувача та паролем транка, які ви вказали.Вхідні дзвінки автентифікуються за digest з іменем користувача та паролем, які надає EvaSay (realm — SIP-домен); можна також обмежити IP-адреси джерел або приймати лише за IP, коли ваш провайдер не може авторизуватися (тоді IP обов'язкові). Кожна SIP-адреса чи транк має ліміт одночасних дзвінків; понад нього дзвінки отримують 486. Паролі показуються один раз — під час створення чи скидання. Лише UDP і G.711 (PCMU, PCMA); натискання клавіш за RFC 4733 або SIP INFO; вимкніть реєстрацію (REGISTER) на транку вашої АТС.
Кампанія обдзвонює список контактів одним агентом у межах годин обдзвону та ліміту паралельності (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 обов'язковий: ним ви підтверджуєте, що всі зі списку погодилися на дзвінки, зокрема голосом ШІ, і EvaSay зберігає це підтвердження разом із кампанією (consent_confirmed_at). Кампанію створюють як чернетку; запустіть її через POST /api/v1/voice/campaigns/{id}/start, а pause і cancel використовуйте так само. Перед запуском кампанії агент має бути опублікований.
З увімкненим voicemail_detection (за замовчуванням) вихідний дзвінок чекає, поки інша сторона заговорить, перш ніж агент привітається; якщо вони мовчать, агент вітається за дві–три секунди. Коли агент чує автовідповідач або запис оператора зв'язку, він чекає на кінець привітання та сигналу, залишає voicemail_message або кладе слухавку, якщо повідомлення немає. Такі дзвінки завершуються з ended_reason voicemail, тарифікуються за фактичною тривалістю й не аналізуються та не розсилаються в сповіщеннях.
У кампанії номер, що потрапив на автовідповідач, набирається знову, як той, що не відповів, а повідомлення залишається лише під час останньої спроби, щоб ніхто не отримав те саме повідомлення двічі.
Кожен вихідний дзвінок — із кампанії чи через POST /api/v1/voice/calls — спершу перевіряє список «Не дзвонити» робочого простору. Номер зі списку ніколи не набирається: API відповідає 409 із code do_not_call, а кампанія позначає контакт як canceled з outcome do_not_call і переходить до наступного.
Коли людина в дзвінку, зробленому агентом, просить більше їй не телефонувати, агент сам вносить її номер до списку, повідомляє їй про це, і надсилається вебхук call.do_not_call. Номери можна також додавати й видаляти в Кампанії → Список «Не дзвонити» або через 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" }'
Номери вказуються в міжнародному форматі; до 1000 за раз. 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, потім задайте voice_id агента рівним voice_id голосу. language — мова, якою говорять у записі, будь-яка з вищеперелічених мов агентів; голос говорить усіма, окрім китайської, де відповідає вбудований голос тієї самої статі, і найбільше нагадує людину із запису мовою запису. Кожен клонований голос коштує кредитів, які панель показує до його створення.
Додайте 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 | Двоканальний запис збережено: recordingUrl (чинний 24 години), durationMs |
call.message_sent | Агент показав картки на екрані веб- або застосункового абонента: items, quickReplies |
alert.triggered | Одне з ваших оповіщень (Вебхуки та сповіщення → Оповіщення) перевищило поріг |
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>
Перевіряйте підпис секретом вебхука, показаним під час його створення, і відповідайте статусом 2xx. Невдалі доставки повторюються через 1 хвилину, 5 хвилин, 30 хвилин, 2 години та 12 годин з тим самим тілом і тим самим ідентифікатором EvaSay-Delivery, тож повтори можна ігнорувати. Останні доставки вебхука з точним надісланим тілом і статусом, який відповів ваш endpoint, виводяться через GET /webhooks/{id}/deliveries і в панелі; POST /webhooks/{id}/deliveries/{delivery_id} надсилає одну з них знову.
Окрім електронної пошти та вебхуків alert.triggered / alert.resolved, оповіщення можна надсилати в канал 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 | Забагато запитів; сповільніться й повторіть |
Запити робочого простору до голосового API за останні 30 днів (час, endpoint, статус, тривалість, ключ і код помилки; без тіл) перелічено в API-ключі → Журнал запитів API.
Читання обмежено 120 запитами на хвилину на ключ, запис і дзвінки — 30.
Дзвінки тарифікуються поминутно за рахунок кредитів робочого простору — того самого балансу, що використовує панель. Актуальні тарифи — на сторінці цін.
Використовуйте контракт OpenAPI, щоб згенерувати клієнт для інтеграції. Публічний API, панель і керівний endpoint MCP працюють з одними й тими самими ресурсами та правами робочого простору.
| Можливість | Endpoint |
|---|---|
| Інструменти для повторного використання | 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 |
| Керівний endpoint MCP | /api/v1/voice/mcp |
Зареєструйте HTTP-інструмент, що звертається до вашого CRM-адаптера:
{
"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"] }. Це замінює весь список інструментів знімками; прив'яжіть знову, коли інструмент бібліотеки зміниться. Видалення визначення з бібліотеки зберігає наявні знімки. Вбудовані визначення можна також читати й замінювати через поле tools API агента. Через 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 додатково підтримує вкладені об'єкти, масиви, переліки та числові межі. Зовнішні посилання на схеми й регулярні вирази не приймаються.
static_args: значення, що надсилаються з кожним викликом незалежно від того, що напише модель, наприклад ідентифікатор магазину; вони також підставляються в {{name}} в адресі й тілі.extract: [{name, path}] зберігає поля з відповіді функції (data.id) для {{name}} у наступних функціях, тож customer_id з пошуку можна передати в наступне замовлення, не змушуючи модель повторювати його. Поля початкового запиту зберігаються так само.reject_when: до 5 правил {on: caller|agent, pattern, negate}; доки всі вони виконуються для останніх слів, функція не запускається, а агент отримує reject_message.model_says: під час кожного виклику модель пише одне речення, яке вимовляється, поки виконується функція, замість фіксованого speak_before.Підключіть застосунок у Інтеграції → Вебхуки та сповіщення → Підключені застосунки, потім скористайтеся «Додати готову функцію» на вкладці «Знання та інструменти» агента; власний endpoint не потрібен:
check_availability (вільний час на дату) і book_appointment (записує на час, обраний абонентом). Час у Google Calendar відповідає заданим вами часовому поясу, робочим дням, годинам і тривалості прийому; Cal.com використовує власну доступність типу події та запасну адресу електронної пошти, яку ви зададите, якщо абонент її не назвав.save_to_sheet, яка дописує рядок із перерахованими вами стовпцями, а також ID і часом дзвінка.notify_team, яка публікує в каналі Slack (incoming webhook) повідомлення, написане агентом.Площина керування EvaSay виконує ці функції з підключеним вами обліковим записом; ані воркер, ані визначення агента не зберігають облікові дані застосунку. Під час читання агента через API вони відображаються як kind: "app" (з app, action, connection_id, app_config); надсилайте їх назад без змін під час заміни tools, щоб зберегти. Застосунки підключаються лише в панелі.
Задайте run_at_start: true одному з інструментів агента, і він буде викликаний на початку кожного дзвінка, до того як агент привітається чи відповість. Параметри з іменами caller_number, called_number, direction, channel або call_id заповнюються для нього автоматично. Поля, які він повертає, підставляються в {{variables}} у first_message, system_prompt та idle_messages (поля на рівень глибше — як {{customer.name}}), а вся відповідь передається агентові як довідкова інформація. Привітання зі змінними чекає на відповідь до 3 секунд; усе, що не знайдено, лишається порожнім. На старті може запускатися один інструмент на агента.
Облікові дані налаштовуються в оточенні сервера для вашого робочого простору й адреси призначення; попросіть support@evasay.com виділити credential_ref. Не вставляйте 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. Ваш бізнес-endpoint має його дотримуватися. Однакові операції запису в межах дзвінка дедуплікуються, зокрема операції з невідомим результатом після тайм-ауту. Інша навмисна операція потребує окремого бізнес-аргумента, наприклад operation_id. EvaSay не повторює операції запису автоматично. Події виконання інструментів містять статус і тривалість; функція перевірки розмови лишається текстовою симуляцією, а не перевіркою виконання інструментів.
Налаштуйте підключення MCP Streamable HTTP з параметрами:
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.
Використовуйте безпечне налаштування клієнта для токена чи змінної оточення. Endpoint використовує автентифікацію за API-ключем, а не вхід через OAuth. Він надає створення й налаштування агентів, інструменти бібліотеки, знання, вебхуки та записи дзвінків. Публікація та справжні дзвінки — операції запису; вихідні дзвінки витрачають кредити й оплату оператора зв'язку. Кожен виклик інструмента заново перевіряє права в робочому просторі. Увімкніть у клієнті підтвердження для операцій запису. Endpoint використовує JSON-відповіді без стану; потоку фонових сповіщень він не надає.
Приклад інструкції для інструмента розробки: «Створи чернетку агента EvaSay, зареєструй мій endpoint пошуку в CRM, перегляньте його з тестовими аргументами, прив'яжи до агента й налаштуй вебхук call.analyzed». Перевірте та явно дозвольте публікацію й справжні дзвінки.
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"]
}
}
Агентові доступні лише зареєстровані інструменти. Віддалені підключення підтримують Streamable HTTP з відповідями JSON або SSE на запити, публічні HTTPS-адреси та заздалегідь налаштовані облікові дані. Застарілі SSE-endpoint, узгодження OAuth, ініційовані сервером sampling і elicitation та бінарні вкладення не підтримуються. Виявлення не надає автоматично доступу до всіх інструментів сервера. Схема віддаленого інструмента має бути вбудованою й використовувати підтримуване підмножину JSON Schema.
Створіть вебхук через POST /webhooks з { "url": "https://crm.example.com/evasay-events", "events": ["call.analyzed"] }. У відповіді — підписка та згенерований секрет підпису, що повертається лише під час створення. Перевіряйте EvaSay-Signature, дедуплікуйте за EvaSay-Delivery і оновлюйте свій CRM. Вебхуки подій — це асинхронні сповіщення; інструменти під час дзвінка повертають результати безпосередньо агентові.