API działa na tych samych agentach, połączeniach i kredytach co panel w przeglądarce. Wszystko, co zbudujesz w panelu, możesz obsługiwać z własnych systemów, i odwrotnie.
| Co | Endpoint |
|---|---|
| Lista agentów | GET /api/v1/voice/agents |
| Utworzenie agenta | POST /api/v1/voice/agents |
| Pobranie / aktualizacja agenta | GET · PATCH /api/v1/voice/agents/{id} |
| Wykonanie połączenia wychodzącego | POST /api/v1/voice/calls |
| Lista połączeń | GET /api/v1/voice/calls |
| Pobranie połączenia (transkrypcja) | GET /api/v1/voice/calls/{id} |
| Usuń połączenie | DELETE /api/v1/voice/calls/{id} |
| Usuń połączenia rozmówcy | POST /api/v1/voice/calls/delete-by-caller |
| Kampanie | GET · POST /api/v1/voice/campaigns |
| Pobranie kampanii | GET /api/v1/voice/campaigns/{id} |
| Start / wstrzymanie / anulowanie | POST /api/v1/voice/campaigns/{id}/{action} |
| Sklonowane głosy | GET · POST /api/v1/voice/voices |
| Usunięcie sklonowanego głosu | DELETE /api/v1/voice/voices/{id} |
| Lista „Nie dzwonić” | GET · POST /api/v1/voice/do-not-call |
| Usunięcie z listy | DELETE /api/v1/voice/do-not-call/{phone} |
| Języki, głosy, szablony | GET /api/v1/voice/catalog |
| Testy agenta | GET · POST /api/v1/voice/agents/{id}/tests |
| Uruchom testy / odczytaj przebieg | POST /api/v1/voice/agents/{id}/test-runs · GET /api/v1/voice/test-runs/{id} |
| Przypisz numer do agenta | PATCH /api/v1/voice/phone-numbers/{id} |
| Listy słów | GET · POST /api/v1/voice/vocabularies |
Utwórz klucz w panelu w Integracje → Klucze API (lub jednym kliknięciem na stronie przeglądu) i wysyłaj go jako token Bearer:
Authorization: Bearer sk-your-key
Klucz działa w przestrzeni roboczej, w której pracuje jego właściciel, z rolą tej osoby: klucz członka tylko do odczytu może czytać, ale niczego nie zmienia.
Utwórz agenta z szablonu, opcjonalnie ustawiając jego pola w tym samym żądaniu. Bez template zaczyna od zera; GET /api/v1/voice/catalog wymienia szablony oraz języki i głosy, które może mieć agent. Szablony są napisane po angielsku i chińsku, więc dla innego języka wyślij system_prompt i first_message w tym języku. Zapisywane jest całe żądanie albo nie powstaje żaden agent, więc odrzucone żądanie można po poprawieniu wysłać ponownie. dashboard_url w odpowiedzi to strona agenta w panelu, gdzie możesz porozmawiać z wersją roboczą w przeglądarce. Każdy agent działa na EvaSay ASR, EvaSay LLM i EvaSay TTS, które EvaSay dobiera do każdego połączenia według języka i obciążenia i które są wliczone w cenę połączenia; nie ma kont innych dostawców do skonfigurowania.
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..."
}'
Każde z tych pól zaktualizujesz przez PATCH /api/v1/voice/agents/{id}:
| Pole | Uwagi |
|---|---|
name, first_message, system_prompt | Tekst |
status | published publikuje wersję roboczą jako nową wersję; draft przestaje przyjmować połączenia |
language | Jeden z 27 języków, np. en, zh, es, de, albo multi, aby podążać za językiem rozmówcy |
temperature | 0-100 |
max_duration_sec, silence_timeout_sec | Limity połączenia |
interruptible, end_call_enabled | Wartości logiczne |
confirm_details | Wartość logiczna; agent powtarza na głos liczby i imiona i prosi o potwierdzenie |
ai_disclosure | Wartość logiczna, domyślnie true; przed pierwszą wiadomością agent mówi w języku połączenia, że rozmówca rozmawia z SI (pomijane, gdy pierwsza wiadomość już to mówi) |
voicemail_detection | Wartość logiczna; w połączeniach wychodzących agent najpierw słucha i kończy rozmowę, gdy trafi na pocztę głosową |
voicemail_message | Zostawiana na poczcie głosowej, może zawierać {{variables}}; brak oznacza rozłączenie |
transfer_number | Numer, na który przekierować, w formacie międzynarodowym |
transfer_destinations | Do 9 dodatkowych numerów, [{name, number, when}]; agent wybiera jeden według when |
transfer_briefing | Wartość logiczna; osoba, która odbierze pierwsza, słyszy streszczenie rozmowy przygotowane przez agenta |
pronunciations | Do 100 pozycji [{word, say, case_sensitive}]; głos mówi say zamiast word |
idle_messages | Do 3 przypomnień wypowiadanych po kolei milczącemu rozmówcy, zanim połączenie się zakończy |
noise_reduction | Wartość logiczna; szum tła po stronie rozmówcy jest filtrowany, zanim zostanie usłyszany |
recording_enabled | Wartość logiczna; połączenia są nagrywane w dwóch kanałach (rozmówca po lewej, agent po prawej) |
recording_consent | announce, ask (nagranie jest zachowywane tylko, jeśli rozmówca się zgodzi) lub none |
recording_notice | Komunikat lub pytanie wypowiadane na początku; puste oznacza wbudowany tekst w języku połączenia |
handoffs | Do 5 pozycji [{agent_id, when}]: inni agenci, którym ten może przekazać połączenie |
knowledge_base_id | Jedna z Twoich baz wiedzy |
vocabulary_id | Jeden z Twoich słowników; jego terminy są trafniej rozpoznawane, gdy rozmówca je wypowiada |
voice_id | Identyfikator wbudowanego głosu EvaSay albo jeden z Twoich sklonowanych głosów (clone:…) |
Wbudowane głosy zależą od języka: chiński ma ming-designed-female-clear i ming-designed-female-warm (żeńskie) oraz ming-designed-male-clear i ming-designed-male-friendly (męskie), pozostałe języki mają F1–F5 (żeńskie) i M1–M5 (męskie). Wbudowany głos, który nie mówi językiem agenta, jest zastępowany głosem tej samej płci, który nim mówi. Starsza nazwa pola tts_voice_id nadal jest akceptowana. Każda inna wartość voice_id albo sklonowany głos, który nie jest jeszcze gotowy, zostaje odrzucona.
analysis_config określa, co jest ustalane z każdej rozmowy po jej zakończeniu: summary (domyślnie true), summaryLanguage, successCriteria (co osiąga udana rozmowa), fields (do 20 {name, type, description} do wyodrębnienia, gdzie type to string, number lub boolean) i scorecard (do 10 {name, description, weight}, waga 1–5). Wyniki są w polu analysis rozmowy i w webhooku call.analyzed.
Zmiana pól agenta zmienia jego wersję roboczą. Połączenia nadal używają wersji opublikowanej, dopóki nie opublikujesz wersji roboczej przez POST /api/v1/voice/agents/{id}/publish (opcjonalnie z note) albo przez status: "published" w tym samym PATCH. Każda publikacja zachowuje numerowaną wersję; published_version w agencie wskazuje, której używają połączenia. Połączenie pozostaje przy wersji, która obowiązywała w chwili jego rozpoczęcia, więc publikacja w trakcie rozmowy jej nie zmienia. Połączenia testowe z panelu używają wersji roboczej.
GET /agents/{id}/versions wyświetla wersje, a POST /agents/{id}/versions/{version}/restore cofa zmiany: nowe połączenia używają tej wersji, a wersja robocza się nią staje. Nazwa agenta, wdrożenie, wygląd widżetu i powiadomienia nie mają wersji i zmieniają się od razu, podobnie jak dokumenty jego bazy wiedzy.
Test odgrywa jedną rozmowę w formie tekstu: symulowany dzwoniący odgrywa scenario, agent odpowiada na podstawie wersji roboczej (instrukcje, pierwsza wiadomość i wiedza), a oceniający sprawdza rozmowę względem success_criteria. Napisz testy dla ważnych rozmów, uruchamiaj je po każdej zmianie i publikuj, gdy przechodzą.
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 uruchamia wszystkie testy agenta albo te z test_ids i odpowiada jednym przebiegiem na test (status: "running"). Odpytuj GET /test-runs/{id}, aż status będzie passed, failed lub error; wtedy przebieg zawiera transcript i reason oceniającego (po angielsku albo po chińsku z reason_language: "zh"). Każdy przebieg kosztuje Credits, widoczne przy testach w panelu; przebiegi zatrzymują się na pierwszym, za który saldo nie wystarcza, a not_started wymienia pozostałe. Przebieg, którego nie udało się odegrać, jest zwracany. Testy sprawdzają, co mówi agent, a nie jego głos czy tempo, i nie wywołują jego funkcji: te sprawdź wcześniej przez POST /tools/{id}/test. Agent może mieć do 30 testów; PATCH i DELETE /agents/{id}/tests/{test_id} zmieniają lub usuwają test.
Oprócz całych rozmów (type: "simulation", domyślnie) test może sprawdzić jedną odpowiedź. type: "next_reply" przyjmuje chat_history, czyli wcześniejszą rozmowę kończącą się wypowiedzią dzwoniącego ([{role, text}], gdzie role to caller lub agent), oraz success_criteria dla następnej odpowiedzi agenta. type: "tool_call" przyjmuje chat_history i expected_tool {name, arguments, match}: agent ma wywołać name (jedną ze swoich funkcji albo end_call, transfer_call, handoff_to_agent) z arguments, porównywanymi dokładnie (tekst bez spacji na brzegach, bez rozróżniania wielkości liter) lub, z match: "judge", według znaczenia; inne przekazane argumenty są pomijane. transcript przebiegu pokazuje w tool_calls funkcje wywołane przez agenta.
POST /agents/{id}/tests/generate proponuje testy na podstawie wersji roboczej agenta: jego instrukcji, wiedzy i funkcji. Przyjmuje count (1–10, domyślnie 5), types, focus (co mają obejmować) i language (en lub zh, dla nazw i kryteriów), kosztuje Credits jak jeden przebieg i zwraca szkice do przejrzenia i wysłania do POST /agents/{id}/tests; z save: true zapisuje je.
hooks coś robi w danym momencie połączenia, do 10 reguł: on: "silence" (rozmówca nic nie powiedział przez seconds, 3–600, od zakończenia wypowiedzi agenta; raz, dopóki znów się nie odezwie), on: "time_elapsed" (seconds od początku połączenia, 10–3600) lub on: "call_ending". Każda reguła wykonuje (do) do 3 spośród {type: "say", text}, {type: "transfer", destination?}, {type: "end_call", text?} i {type: "tool", name, args?}; reguła na koniec połączenia uruchamia tylko funkcje. Połączenie zakończone przez regułę ma ended_reason rule_ended.
W połączeniach telefonicznych klawisze, które naciska rozmówca, trafiają do agenta jako odpowiedź: wpis kończy się znakiem # lub dwusekundową pauzą. W połączeniach wychodzących agent może też sam naciskać klawisze, by przejść przez menu telefoniczne firmy.
transfer_number to główny numer agenta do przekazania rozmówcy człowiekowi oraz numer, na który się dzwoni, gdy w przestrzeni roboczej skończą się kredyty. transfer_destinations dodaje numery z nazwami, każdy z notatką, kiedy go użyć („Rozliczenia: pytania o faktury i zwroty”), a agent wybiera. Przy włączonym transfer_briefing agent podczas przekierowania pisze krótkie streszczenie; osoba, która odbierze, słyszy je głosem EvaSay i w języku streszczeń agenta, zanim rozmówca zostanie połączony. ended_reason połączenia ma wtedy wartość transferred.
Dzięki handoffs agent może przekazać połączenie innemu opublikowanemu agentowi w Twojej przestrzeni roboczej, na przykład recepcja przekazuje rezerwację agentowi od rezerwacji. Agent wybiera według when. Drugi agent przejmuje to samo połączenie ze wszystkim, co zostało dotąd powiedziane, i mówi według własnych instrukcji, głosu, wiedzy, narzędzi i przekazań. Połączenie nadal figuruje, jest analizowane i rozliczane na pierwszego agenta; jego transkrypcja odnotowuje każde przekazanie.
Numery łączy się w panelu z własnym kontem operatora. GET /api/v1/voice/phone-numbers je wymienia, a PATCH /api/v1/voice/phone-numbers/{id} z { "agent_id": "AGENT_ID" } kieruje połączenia numeru do agenta (jego opublikowanej wersji), a z "agent_id": null do żadnego; label zmienia jego nazwę.
Lista słów zawiera nazwy i terminy, które wypowiadają dzwoniący, a które rozpoznawanie mowy mogłoby źle usłyszeć, np. nazwy produktów, leków czy ulic. POST /api/v1/voice/vocabularies z { "name": "Products", "terms": ["Lumora", "Vantix Pro"] } (do 1000 terminów po 50 znaków albo "preset": "medical" lub "legal" na start), potem ustaw vocabulary_id agenta. GET, PATCH (name, terms) i DELETE /vocabularies/{id}; listy używanej przez agenta nie można usunąć.
Każdy numer ma reguły połączeń na stronie Numery telefonów, stosowane, zanim agent odbierze: zablokowane numery lub początki numerów (+1555) oraz rozmówcy ukrywający numer są odrzucani bez odebrania i nic nie kosztują; rozmówców z listy można od razu połączyć z innym numerem; a gdy agent nie może odebrać (nieopublikowany, brak workera, brak kredytów i brak numeru do przekierowania), połączenie może dzwonić na numer albo odtworzyć Twój własny komunikat. Zapytanie startowe (run_at_start) może też odrzucić rozmówcę, odpowiadając {"reject": true, "message": "..."}: agent wypowiada komunikat, jeśli jest, i się rozłącza, a ended_reason ma wartość rejected.
Wykonaj jedno połączenie z jednego z numerów telefonów Twojej przestrzeni roboczej. Każde żądanie wymaga unikalnego Idempotency-Key; ponowne wysłanie tego samego klucza zwraca to samo połączenie zamiast wybierać numer dwa razy.
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 jest wymagane, gdy przestrzeń robocza ma więcej niż jeden numer telefonu. variables wypełniają symbole zastępcze w stylu {{customer_name}} w pierwszej wiadomości i prompcie agenta.
GET /api/v1/voice/calls/{id} zwraca status połączenia, czas trwania, pobrane kredyty, podsumowanie, analizę i transkrypcję. Połączenia wylistujesz przez GET /api/v1/voice/calls?agent_id=&status=&channel=&user_id=&page= (user_id: Twój własny identyfikator osoby z tokenu połączenia webowego). Jego turns to dziennik diagnostyczny połączenia, tura po turze: na co poszedł czas od zakończenia wypowiedzi rozmówcy do usłyszenia odpowiedzi, w milisekundach — waitMs (koniec mowy), sttMs (rozpoznawanie), llmMs (pierwsze słowo modelu, plus jedno na każdą rundę funkcji), ttsMs (pierwszy dźwięk głosu), firstAudioMs (odpowiedź usłyszana) — wraz z wywołanymi funkcjami, przerwaniami i błędami. Pokazuje go także strona połączenia.
Przy recording_enabled każde połączenie jest nagrywane jako dwukanałowy WAV: rozmówca po lewej, agent po prawej, z częstotliwością próbkowania połączenia. Przy recording_consent: "announce" agent najpierw mówi, że rozmowa jest nagrywana; przy "ask" pyta, a nagranie jest zachowywane tylko, jeśli rozmówca się zgodzi (brak odpowiedzi liczy się jako „nie”); przy "none" nic nie jest mówione. Sprawdź, czego wymaga prawo tam, gdzie jesteście Ty i Twoi rozmówcy.
Nagrania są przechowywane w magazynie EvaSay albo we własnym buckecie zgodnym z S3 (AWS S3, Cloudflare R2, Google Cloud Storage, Supabase, MinIO) ustawionym w Integracje → Webhooki i powiadomienia → Przechowywanie nagrań. Worker obsługujący połączenie wysyła plik bezpośrednio do bucketu. GET /api/v1/voice/calls/{id} zwraca recordingUrl ważny przez godzinę po zapisaniu nagrania oraz recording.status (recording, stored, declined lub failed).
DELETE /api/v1/voice/calls/{id} usuwa zakończone połączenie wraz z transkrypcją, nagraniem (w magazynie EvaSay lub we własnym buckecie), analizą i wysyłkami webhooków. Wykorzystane Credits zostają w historii kredytów. Połączenie, którego nie można jeszcze usunąć, zwraca 409 z code call_not_deletable_yet: trwa lub dopiero się zakończyło, jest jeszcze rozliczane, nagranie może być jeszcze przesyłane albo ma zaległe Credits, których saldo nie pokryło (najpierw doładuj Credits).
Gdy ktoś prosi o usunięcie swoich danych, POST /api/v1/voice/calls/delete-by-caller z { "phone_number": "+14155550123" } usuwa wszystkie połączenia z tym numerem (numer dzwoniącego w przychodzących, numer wybrany w wychodzących), a z { "user_id": "..." } wszystkie połączenia internetowe z tym user_id w tokenie. Odpowiedź to { "deleted": 12, "kept_back": { "settling": 1 }, "more": false }: najwyżej 200 połączeń na żądanie, więc ponawiaj, dopóki more ma wartość true, a później jeszcze raz dla połączeń z kept_back. Jeśli numer jest na liście „Nie dzwonić”, pozostaje na niej. W panelu to samo robi Połączenia → Usuń dane rozmówcy.
Wyślij POST /api/v1/voice/calls/{id}/control, gdy połączenie trwa. Worker obsługujący połączenie odbiera polecenie w ciągu około 2 sekund; API odpowiada 202. Jeśli workera obsługującego połączenie nie da się osiągnąć, API odpowiada 503 (code call_unreachable), a polecenie nie zostaje wykonane.
type | Co się dzieje |
|---|---|
say | Agent mówi text po zakończeniu bieżącego zdania albo od razu przy "interrupt": true |
note | Agent dostaje text (nie jest wypowiadane) i uwzględnia to od następnej odpowiedzi; transkrypcja to odnotowuje |
end | Agent mówi text, jeśli podano, po czym się rozłącza; połączenie telefoniczne bez text jest rozłączane od razu |
ended_reason połączenia ma wtedy wartość api_ended. Strona połączenia w panelu ma te same elementy sterowania.
POST /api/v1/voice/calls/{id}/listen (opcjonalnie { "channels": 2 }) zwraca listen_url, którą można otworzyć w ciągu dwóch minut. To WebSocket: najpierw wiadomość JSON {"type":"start","sampleRate":8000,"channels":1,"encoding":"pcm_s16le"}, potem ramki binarne 16-bitowego PCM little-endian z tym, co słyszy rozmówca (oba głosy zmieszane; przy channels: 2 rozmówca po lewej, agent po prawej). Zamyka się, gdy połączenie się kończy. Dźwięk płynie prosto z workera obsługującego połączenie, z pominięciem płaszczyzny sterowania EvaSay; przycisk Słuchaj na stronie połączenia w panelu używa tego samego adresu.
Wysyłaj i odbieraj dźwięk połączenia z własnego programu, na przykład własnej centrali telefonicznej lub aplikacji. Rozpocznij połączenie:
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 w odpowiedzi zawiera jednorazowy token i można ją otworzyć w ciągu 15 minut. encoding to pcm_s16le (16-bitowy PCM little-endian z sample_rate 8000, 16000 lub 24000) albo mulaw (μ-law 8 kHz). Dźwięk płynie w obie strony jako ramki binarne w tym formacie, najlepiej po 20 ms. Ramki tekstowe to JSON:
| Kierunek | Wiadomości |
|---|---|
| Ty → EvaSay | {"type":"dtmf","digits":"12#"} naciśnięte klawisze; {"type":"end"} rozłącza; {"type":"text","text":"…"} słowa wpisane przez rozmówcę ("input":"button" dla odpowiedzi wybranej dotknięciem); {"type":"context","text":"…","key":"cart"} to, co zgłasza Twoja aplikacja (agent na to nie odpowiada); {"type":"tool_result","id":"…","result":{…}} (lub "error") odpowiedź funkcji strony |
| EvaSay → Ty | start (z callId); clear (rozmówca przerwał: odrzuć jeszcze nieodtworzony dźwięk agenta); transcript (role, text, final oraz input dla wpisanych słów); user_started_speaking, user_stopped_speaking, agent_started_speaking, agent_stopped_speaking; cards (cards, quick_replies); tool (id, name, arguments: uruchom jedną z Twoich funkcji strony); notice (code: too_fast lub limit dla wpisanych słów); ended (z reason, potem gniazdo się zamyka) |
Agent wita się, gdy tylko się połączysz. Do żądania można dodać user_id i page_tools (funkcje strony, na które odpowiada Twoja aplikacja, patrz niżej). Połączenia są rozliczane za minutę jak każde inne, z tymi samymi zapisami połączeń, analizą i webhookami.
W połączeniu webowym (widżet, strona hostowana, Twoja aplikacja) rozmówca widzi czat obok głosu, więc agent może robić coś więcej niż mówić. Rozmówcy dzwoniący przez telefon tego nie mają.
Włącz Agent → Narzędzia → Wyślij rozmówcy (send_to_caller w API agenta), a agent będzie mógł wyświetlać na ekranie rozmówcy do pięciu kart naraz: tytuł, wiersz pod nim, opis, obraz, link i do dwóch przycisków odpowiedzi w każdej; może też zaproponować do trzech szybkich odpowiedzi pod pytaniem. Linki nigdy nie są czytane na głos.
Agent może wysyłać tylko linki i obrazy, które dostał w tym połączeniu: jego instrukcje i powitanie, bazę wiedzy (dokument wczytany ze strony internetowej niesie jej adres), odpowiedzi Twoich funkcji i gotowe karty. Link musi też należeć do jednego z allowed_hosts (własna witryna gotowej karty zawsze jest dozwolona). Linki, które agent zmyśli albo które rozmówca wypowie na głos, nie są wysyłane. Linki do tych witryn w słowach agenta są w czacie klikalne.
"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": "" }
]
}
Każde wysłanie trafia do Ciebie jako webhook call.message_sent (channel, items z title i url, quickReplies, blocked). W jednym połączeniu można wysyłać najwyżej 10 razy.
Podczas połączenia rozmówca może też pisać (adres e-mail, numer zamówienia) lub dotykać przycisku odpowiedzi; agent odpowiada na to jak na mowę, wcześniej przerywając, jeśli mówił. Do 500 znaków na wiadomość, jedna na sekundę, 100 na połączenie. W zapisie połączenia takie tury są oznaczone jako wpisane lub dotknięte.
Widżet daje Twojej stronie obiekt evasay. Wszystko jest opcjonalne, a ramka połączenia słucha Twojej strony tylko wtedy, gdy należy ona do dozwolonych witryn widżetu (Agent → Wdrożenie):
<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 wypełniają {{name}} w instrukcjach i powitaniu. Pochodzą z przeglądarki odwiedzającego, więc agent jest informowany, że nie są zweryfikowane, i nigdy nie trafiają do Twoich funkcji ani do zapytania startowego.update informuje agenta, co dzieje się na stronie, bez zmuszania go do odpowiedzi; nowsza aktualizacja z tym samym key zastępuje starszą."kind": "page", bez url) i zarejestruj na swojej stronie funkcję o tej samej nazwie. Agent może ją wywołać tylko w połączeniach ze strony, która ją zarejestrowała; czeka na odpowiedź do timeout_sec (1–10, domyślnie 5), chyba że wait_for_result ma wartość false. Działa na sesji odwiedzającego, jak jego własne kliknięcie: przed wszystkim, co kosztuje pieniądze lub jest nieodwracalne, poproś o potwierdzenie na własnej stronie. Linki w jej odpowiedzi mogą być wysyłane jako karty.Ustaw Kto może rozpocząć połączenie na Tylko z tokenem i niech Twój serwer pobiera jednorazowy token dla zalogowanego użytkownika:
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 }'
Odpowiedź zawiera token (jedno połączenie, w ciągu expires_in sekund, 30–3600, domyślnie 600), agent (slug) i web_call_url. Przekaż token stronie przez data-token-url="/your/endpoint" w skrypcie widżetu (wysyła tam POST z ciasteczkami odwiedzającego i odczytuje { "token": "…" }) albo przez agent.setToken(async () => token). variables tokenu są zweryfikowane: mogą z nich korzystać instrukcje, zapytanie startowe i szablony Twoich funkcji, tak samo jak z user_id, który połączenie zachowuje (userId w połączeniu, user_id do filtrowania połączeń). Klucz API trzymaj na swoim serwerze.
W własnej aplikacji wyślij POST z { "token": "…", "pageTools": ["add_to_cart"] } na https://evasay.com + web_call_url. Odpowiedzią jest połączenie (adres oferty WebRTC albo adres WebSocket z tokenem połączenia) dla klienta RTVI, takiego jak klienckie SDK Pipecat na web, iOS, Android i React Native. Karty, funkcje strony i powiadomienia przychodzą jako RTVI server-message (evasay.cards, evasay.tool, evasay.notice); wpisane słowa wysyłaj przez send-text, a zgłoszenia i odpowiedzi strony jako client-message evasay.context i evasay.tool_result. Możesz też sam przenosić dźwięk przez Dźwięk przez WebSocket, przekazując user_id i page_tools.
W Numery telefonów → SIP Twoja własna centrala, telefony SIP lub trunk SIP mogą dzwonić bezpośrednio do Twoich agentów; połączenia rozlicza Twój dostawca SIP:
sip:ev…@<EvaSay's SIP domain>, tylko połączenia przychodzące, dla central (3CX, FreePBX, Asterisk…) i telefonów SIP.from); przekierowania do człowieka są przekazywane trunkowi przez REFER. Gdy trunk prosi o dane logowania, EvaSay odpowiada nazwą użytkownika i hasłem trunku, które wpisałeś.Połączenia przychodzące uwierzytelniają się przez digest nazwą użytkownika i hasłem nadanymi przez EvaSay (realm to domena SIP); możesz też ograniczyć źródłowe adresy IP albo przyjmować wyłącznie po IP, gdy Twój dostawca nie potrafi się zalogować (adresy IP są wtedy wymagane). Każdy adres SIP lub trunk ma limit równoczesnych połączeń; powyżej niego połączenia dostają 486. Hasła są pokazywane raz, przy utworzeniu lub zresetowaniu. Tylko UDP i G.711 (PCMU, PCMA); klawisze przez RFC 4733 lub SIP INFO; wyłącz rejestrację (REGISTER) na trunku centrali.
Kampania dzwoni do listy kontaktów jednym agentem, w godzinach dzwonienia i w ramach limitu współbieżności (max_concurrency, do 100 połączeń naraz i nigdy więcej niż limit równoczesnych połączeń Twojej przestrzeni roboczej), a do osób, których nie udało się złapać, dzwoni ponownie.
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" }
]
}'
Kontakty mogą być też tekstem CSV z numerem telefonu w pierwszej kolumnie. consent_confirmed: true jest wymagane: potwierdzasz nim, że wszyscy z listy zgodzili się na telefony, także od głosu SI, a EvaSay przechowuje to potwierdzenie razem z kampanią (consent_confirmed_at). Kampania powstaje jako wersja robocza; uruchom ją przez POST /api/v1/voice/campaigns/{id}/start, a pause i cancel stosuj tak samo. Agent musi być opublikowany, zanim kampania ruszy.
Przy włączonym voicemail_detection (domyślnie) połączenie wychodzące czeka, aż druga strona się odezwie, zanim agent się przywita; jeśli ta milczy, agent wita się po dwóch–trzech sekundach. Gdy agent usłyszy pocztę głosową lub nagranie operatora, czeka, aż powitanie i sygnał się skończą, zostawia voicemail_message albo się rozłącza, jeśli go nie ma. Takie połączenia kończą się z ended_reason voicemail, są rozliczane za czas, jaki trwały, i nie są analizowane ani zgłaszane w powiadomieniach.
W kampanii numer, który trafił na pocztę głosową, jest ponawiany jak ten, który nie odebrał, a wiadomość jest zostawiana dopiero przy ostatniej próbie, żeby nikt nie dostał tej samej wiadomości dwa razy.
Każde połączenie wychodzące, z kampanii lub z POST /api/v1/voice/calls, najpierw sprawdza listę „Nie dzwonić” przestrzeni roboczej. Numer z listy nigdy nie jest wybierany: API odpowiada 409 z code do_not_call, a kampania oznacza kontakt jako canceled z outcome do_not_call i przechodzi do następnego.
Gdy osoba w połączeniu wykonanym przez agenta prosi, by więcej do niej nie dzwonić, agent sam wpisuje jej numer na listę, mówi jej o tym i wysyłany jest webhook call.do_not_call. Numery można też dodawać i usuwać w Kampanie → Lista „Nie dzwonić” lub przez 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" }'
Numery podaje się w formacie międzynarodowym; do 1000 naraz. GET /api/v1/voice/do-not-call wyświetla je od najnowszych (search, limit, page), każdy z source (call, dashboard lub api). DELETE /api/v1/voice/do-not-call/%2B14155550100 ponownie zdejmuje numer z listy; rób to tylko wtedy, gdy osoba zgodziła się na telefony.
POST /api/v1/voice/voices tworzy głos z nagrania WAV jednej osoby (multipart audio, name, language, consent=true). Odpowiada 202 ze statusem running; odpytuj GET /api/v1/voice/voices, aż będzie ready, a potem ustaw voice_id agenta na voice_id głosu. language to język nagrania, dowolny z języków agentów wymienionych wyżej; głos mówi wszystkimi z nich oprócz chińskiego, gdzie odpowiada wbudowany głos tej samej płci, i najbardziej przypomina osobę z nagrania w języku nagrania. Każdy sklonowany głos kosztuje kredyty, które panel pokazuje przed jego utworzeniem.
Dodaj adres https w Integracje → Webhooki i powiadomienia, aby otrzymywać zdarzenia połączeń:
| Zdarzenie | Kiedy |
|---|---|
call.started | Agent odebrał |
call.ended | Połączenie się zakończyło, z czasem trwania i kredytami |
call.analyzed | Gotowe są podsumowanie, wynik, wyodrębnione pola i transkrypcja |
call.transcript | W trakcie połączenia, nowe wiersze transkrypcji co kilka sekund |
call.tool_called | Agent wywołał jedną z Twoich funkcji: name, arguments, status, durationMs |
call.handed_off | Połączenie przekazano innemu agentowi: fromAgentId, toAgentId |
call.transferred | Połączenie przekierowano do człowieka: destination |
call.voicemail | Połączenie wychodzące trafiło na pocztę głosową |
call.do_not_call | Osoba w połączeniu wychodzącym poprosiła, by więcej do niej nie dzwonić: phone |
call.recording_ready | Dwukanałowe nagranie zapisane: recordingUrl (ważny 24 godziny), durationMs |
call.message_sent | Agent pokazał karty na ekranie rozmówcy z weba lub aplikacji: items, quickReplies |
alert.triggered | Jeden z Twoich alertów (Webhooki i powiadomienia → Alerty) przekroczył próg |
alert.resolved | Ten alert wrócił do normy |
Zdarzenia wysyłane w trakcie połączenia niosą data.call (id, agentId, channel, direction, numery). Każde żądanie jest podpisane:
EvaSay-Signature: t=<unix seconds>,v1=<hex HMAC-SHA256 of "<t>.<raw body>">
EvaSay-Delivery: <delivery id>
Zweryfikuj podpis sekretem webhooka pokazanym przy jego tworzeniu i odpowiedz statusem 2xx. Nieudane dostarczenia są ponawiane po 1 minucie, 5 minutach, 30 minutach, 2 godzinach i 12 godzinach z tą samą treścią i tym samym identyfikatorem EvaSay-Delivery, więc powtórzenia możesz ignorować. Ostatnie dostarczenia webhooka, z dokładną wysłaną treścią i statusem, jaki zwrócił Twój endpoint, wyświetla GET /webhooks/{id}/deliveries oraz panel; POST /webhooks/{id}/deliveries/{delivery_id} wysyła jedno z nich ponownie.
Oprócz e-maila i webhooków alert.triggered / alert.resolved alerty mogą trafiać na kanał Slack (incoming webhook), do bota grupowego WeCom lub na Telegram w Webhooki i powiadomienia → Alerty → Wysyłaj też do.
Błędy mają jeden format:
{
"error": {
"type": "invalid_request_error",
"code": "invalid_agent_field",
"message": "status must be draft or published",
"param": "status"
}
}
| Status | Znaczenie |
|---|---|
| 400 | Nieprawidłowe dane (code i param wskazują, które) |
| 401 | Brak klucza API lub jest nieprawidłowy |
| 402 | Za mało kredytów, by rozpocząć połączenie |
| 403 | Klucz jest tylko do odczytu albo agent jest niedostępny |
| 404 | W tej przestrzeni roboczej nie ma takiego agenta, połączenia ani kampanii |
| 409 | Zły stan (np. uruchomienie trwającej kampanii), konto dostawcy nie jest połączone albo numer jest na liście „Nie dzwonić” (do_not_call) |
| 429 | Zbyt wiele żądań; zwolnij i spróbuj ponownie |
Żądania do API głosowego przestrzeni roboczej z ostatnich 30 dni (czas, endpoint, status, czas trwania, klucz i kod błędu; bez treści) są wymienione w Klucze API → Dziennik żądań API.
Odczyty są ograniczone do 120 żądań na minutę na klucz, zapisy i połączenia do 30.
Połączenia są rozliczane za minutę z kredytów przestrzeni roboczej, tego samego salda, z którego korzysta panel. Aktualne stawki znajdziesz na stronie cennika.
Użyj kontraktu OpenAPI, aby wygenerować klienta integracji. Publiczne API, panel i endpoint zarządzania MCP działają na tych samych zasobach i uprawnieniach przestrzeni roboczej.
| Możliwość | Endpoint |
|---|---|
| Narzędzia wielokrotnego użytku | GET / POST /api/v1/voice/tools |
| Odczyt / aktualizacja / usunięcie narzędzia | GET / PATCH / DELETE /api/v1/voice/tools/{id} |
| Podgląd lub test narzędzia | POST /api/v1/voice/tools/{id}/test |
| Powiązanie narzędzi z agentem | PUT /api/v1/voice/agents/{id}/tools |
| Bazy wiedzy | GET / POST /api/v1/voice/knowledge-bases |
| Odczyt / aktualizacja / usunięcie bazy wiedzy | GET / PATCH / DELETE /api/v1/voice/knowledge-bases/{id} |
| Dodanie / lista dokumentów | POST / GET /api/v1/voice/knowledge-bases/{id}/documents |
| Odczyt / usunięcie dokumentu | GET / DELETE /api/v1/voice/knowledge-bases/{id}/documents/{doc_id} |
| Subskrypcje zdarzeń połączeń | GET / POST /api/v1/voice/webhooks |
| Odczyt / aktualizacja / usunięcie subskrypcji | GET / PATCH / DELETE /api/v1/voice/webhooks/{id} |
| Wysłanie podpisanego zdarzenia testowego | POST /api/v1/voice/webhooks/{id}/test |
| Wykrywanie zdalnych narzędzi MCP | POST /api/v1/voice/mcp/discover |
| Endpoint zarządzania MCP | /api/v1/voice/mcp |
Zarejestruj narzędzie HTTP, które odpytuje Twój adapter 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 zwraca { "data": { "id": "TOOL_ID", ... } }. Powiąż jego definicję z agentem przez PUT /agents/AGENT_ID/tools z { "tool_ids": ["TOOL_ID"] }. To zastępuje całą listę narzędzi migawkami; powiąż ponownie, gdy narzędzie z biblioteki się zmieni. Usunięcie definicji z biblioteki zachowuje istniejące migawki. Definicje wbudowane możesz też odczytywać i zastępować polem tools API agenta. Użyj analysis_config i widget_config, aby przez API agenta skonfigurować analizę i widżet.
Narzędzie może używać GET, POST, PUT, PATCH lub DELETE. query i request_body przyjmują zagnieżdżone szablony JSON, takie jak { "customer": { "id": "{{customer_id}}" } }. Symbol zastępczy zajmujący całą wartość zachowuje pierwotny typ JSON. Symbole zastępcze w adresie URL są kodowane i mogą występować tylko po stałym, publicznym hoście HTTPS. Bez request_body narzędzia inne niż GET otrzymują { "arguments": {...}, "call": { "callId": "...", "agentId": "...", "toolCallId": "...", "idempotencyKey": "..." } }. response_path wybiera pole w notacji z kropkami. Proste parameters są nadal akceptowane; input_schema obsługuje dodatkowo zagnieżdżone obiekty, tablice, wyliczenia i granice liczbowe. Zewnętrzne odwołania do schematów i wyrażenia regularne nie są akceptowane.
static_args: wartości wysyłane z każdym wywołaniem bez względu na to, co napisze model, na przykład identyfikator sklepu; wypełniają też {{name}} w adresie i treści.extract: [{name, path}] zachowuje pola z odpowiedzi funkcji (data.id) dla {{name}} w kolejnych funkcjach, więc customer_id z wyszukiwania może trafić do następnego zamówienia bez powtarzania przez model. Pola zapytania startowego są zachowywane tak samo.reject_when: do 5 reguł {on: caller|agent, pattern, negate}; dopóki wszystkie są spełnione dla ostatnich słów, funkcja się nie uruchamia, a agent dostaje reject_message.model_says: przy każdym wywołaniu model pisze jedno zdanie, które jest wypowiadane w trakcie działania funkcji, zamiast stałego speak_before.Połącz aplikację w Integracje → Webhooki i powiadomienia → Połączone aplikacje, a potem użyj „Dodaj gotową funkcję” na karcie Wiedza i narzędzia agenta; nie potrzebujesz własnego endpointu:
check_availability (wolne terminy danego dnia) i book_appointment (rezerwuje termin wybrany przez rozmówcę). Terminy w Google Calendar zależą od ustawionej strefy czasowej, dni otwarcia, godzin i długości wizyty; Cal.com korzysta z własnej dostępności typu wydarzenia oraz z zapasowego adresu e-mail, który ustawisz na wypadek, gdy rozmówca żadnego nie poda.save_to_sheet, która dopisuje wiersz z wymienionymi przez Ciebie kolumnami oraz identyfikatorem i czasem połączenia.notify_team, która publikuje na kanale Slack (incoming webhook) wiadomość napisaną przez agenta.Płaszczyzna sterowania EvaSay uruchamia te funkcje na koncie, które połączyłeś; ani worker, ani definicja agenta nie przechowują danych logowania aplikacji. Przy odczycie agenta przez API występują jako kind: "app" (z app, action, connection_id, app_config); odeślij je bez zmian przy zastępowaniu tools, aby je zachować. Aplikacje łączy się wyłącznie w panelu.
Ustaw run_at_start: true na jednym z narzędzi agenta, a zostanie ono wywołane na początku każdego połączenia, zanim agent się przywita lub odpowie. Parametry o nazwach caller_number, called_number, direction, channel lub call_id są dla niego wypełniane automatycznie. Zwrócone pola wypełniają {{variables}} w first_message, system_prompt i idle_messages (pola piętro niżej jako {{customer.name}}), a cała odpowiedź trafia do agenta jako tło. Powitanie ze zmiennymi czeka na odpowiedź do 3 sekund; wszystko, czego nie znaleziono, zostaje puste. Na starcie może działać jedno narzędzie na agenta.
Dane logowania konfiguruje się w środowisku serwera dla Twojej przestrzeni roboczej i adresu docelowego; poproś support@evasay.com o przydzielenie credential_ref. Nie umieszczaj kluczy API w definicjach agentów, nagłówkach, promptach ani parametrach narzędzi. Twój adapter może trzymać dane logowania do CRM we własnym środowisku.
Zobacz podgląd narzędzia, zanim wyślesz żądania do systemu biznesowego:
{
"arguments": { "customer_id": "customer-123" },
"dry_run": true,
"mock_result": { "name": "Alex", "order_status": "shipped" }
}
Podgląd sprawdza parametry i pokazuje zbudowane żądanie; nie prowadzi rozmowy ani nie łączy się z systemem biznesowym. Ustaw dry_run=false dla prawdziwego wykonania. Prawdziwy test zapisu wymaga też confirmed=true i idempotency_key (8–128 liter, cyfr, kropek, podkreślników, dwukropków lub myślników). Odpowiedzi testów zawierają ok, status, duration_ms i ograniczony wynik. Nie zwracają danych logowania. Testy odczytu i zapisu wymagają uprawnienia do zapisu w przestrzeni roboczej.
Podczas połączenia żądania biznesowe niosą stały Idempotency-Key. Twój endpoint biznesowy musi go respektować. Identyczne zapisy w obrębie połączenia są deduplikowane, także operacje o nieznanym wyniku po przekroczeniu czasu. Inna, zamierzona operacja wymaga odrębnego argumentu biznesowego, np. operation_id. EvaSay nie powtarza zapisów automatycznie. Zdarzenia wykonania narzędzi zawierają status i czas trwania; funkcja testu rozmowy pozostaje symulacją tekstową, a nie testem wykonania narzędzi.
Skonfiguruj połączenie MCP Streamable HTTP z:
URL: https://evasay.com/api/v1/voice/mcp
Authorization: Bearer <your EvaSay API key>
Na przykład w Claude Code:
claude mcp add --transport http evasay https://evasay.com/api/v1/voice/mcp --header "Authorization: Bearer $EVASAY_API_KEY"
Połączenie mówi narzędziu, jak zbudować agenta: odczytać katalog, utworzyć wersję roboczą, dodać wiedzę, funkcje i listy słów, napisać testy i je uruchomić, poprawiać wersję roboczą, aż przejdą, przekazać Ci dashboard_url agenta do wypróbowania, a publikować i przypisywać numer dopiero po Twojej zgodzie. Narzędzia czytające dokumentację mogą zacząć od https://evasay.com/llms.txt, gdzie są linki do tej strony, kontraktu OpenAPI i adresu MCP.
Użyj bezpiecznego ustawienia tokenu lub zmiennej środowiskowej w kliencie. Endpoint używa uwierzytelniania kluczem API, a nie logowania OAuth. Udostępnia tworzenie i konfigurację agentów, narzędzia z biblioteki, wiedzę, webhooki i zapisy połączeń. Publikowanie i wykonywanie prawdziwych połączeń to akcje zapisu; połączenia wychodzące zużywają kredyty i opłaty operatora. Każde wywołanie narzędzia ponownie sprawdza uprawnienia przestrzeni roboczej. Włącz w kliencie potwierdzanie akcji zapisu. Endpoint używa bezstanowych odpowiedzi JSON; nie oferuje strumienia powiadomień w tle.
Przykładowa instrukcja dla narzędzia programistycznego: „Utwórz roboczego agenta EvaSay, zarejestruj endpoint wyszukiwania w moim CRM, zrób jego podgląd z argumentami testowymi, powiąż go z agentem i skonfiguruj webhook call.analyzed”. Sprawdź i wyraźnie zatwierdź publikację oraz prawdziwe połączenia.
POST /mcp/discover przyjmuje { "url": "https://crm.example.com/mcp", "credential_ref": "crm" } i zwraca narzędzia oraz schematy serwera. Zarejestruj tylko wybrane narzędzie:
{
"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"]
}
}
Agentowi dostępne stają się tylko zarejestrowane narzędzia. Połączenia zdalne obsługują Streamable HTTP z odpowiedziami JSON lub SSE na żądania, publiczne cele HTTPS i wstępnie skonfigurowane dane logowania. Nie są obsługiwane starsze endpointy SSE, negocjacja OAuth, inicjowane przez serwer sampling i elicitation ani załączniki binarne. Wykrywanie nie przyznaje automatycznie dostępu do wszystkich narzędzi serwera. Schemat zdalnego narzędzia musi być wbudowany i używać obsługiwanego podzbioru JSON Schema.
Utwórz webhook przez POST /webhooks z { "url": "https://crm.example.com/evasay-events", "events": ["call.analyzed"] }. Odpowiedź zawiera subskrypcję i wygenerowany sekret podpisu, zwracany tylko przy utworzeniu. Zweryfikuj EvaSay-Signature, deduplikuj po EvaSay-Delivery i zaktualizuj swój CRM. Webhooki zdarzeń to powiadomienia asynchroniczne; narzędzia w trakcie połączenia zwracają wyniki bezpośrednio agentowi.