API, web panelindekiyle aynı ajanlar, aramalar ve kredilerle çalışır. Panelde kurduğunuz her şeyi kendi sistemlerinizden yönetebilirsiniz, tersi de geçerlidir.
| Ne | Endpoint |
|---|---|
| Ajanları listele | GET /api/v1/voice/agents |
| Ajan oluştur | POST /api/v1/voice/agents |
| Ajanı getir / güncelle | GET · PATCH /api/v1/voice/agents/{id} |
| Giden arama yap | POST /api/v1/voice/calls |
| Aramaları listele | GET /api/v1/voice/calls |
| Bir aramayı getir (döküm) | GET /api/v1/voice/calls/{id} |
| Bir aramayı sil | DELETE /api/v1/voice/calls/{id} |
| Bir arayanın aramalarını sil | POST /api/v1/voice/calls/delete-by-caller |
| Kampanyalar | GET · POST /api/v1/voice/campaigns |
| Bir kampanyayı getir | GET /api/v1/voice/campaigns/{id} |
| Başlat / duraklat / iptal et | POST /api/v1/voice/campaigns/{id}/{action} |
| Klonlanmış sesler | GET · POST /api/v1/voice/voices |
| Klonlanmış sesi sil | DELETE /api/v1/voice/voices/{id} |
| Aranmayacaklar listesi | GET · POST /api/v1/voice/do-not-call |
| Listeden çıkar | DELETE /api/v1/voice/do-not-call/{phone} |
| Diller, sesler, şablonlar | GET /api/v1/voice/catalog |
| Bir ajanın testleri | GET · POST /api/v1/voice/agents/{id}/tests |
| Testleri çalıştır / çalıştırmayı oku | POST /api/v1/voice/agents/{id}/test-runs · GET /api/v1/voice/test-runs/{id} |
| Numarayı ajana yönlendir | PATCH /api/v1/voice/phone-numbers/{id} |
| Kelime listeleri | GET · POST /api/v1/voice/vocabularies |
Panelde Entegrasyonlar → API anahtarları altında (veya genel bakışta tek tıkla) bir anahtar oluşturun ve Bearer token olarak gönderin:
Authorization: Bearer sk-your-key
Anahtar, sahibinin çalıştığı çalışma alanında, o kişinin rolüyle işlem yapar: salt okunur bir üyenin anahtarı okuyabilir ama hiçbir şeyi değiştiremez.
Bir şablondan ajan oluşturun; isterseniz alanlarını aynı istekte ayarlayın. template verilmezse boş başlar; GET /api/v1/voice/catalog şablonları ve bir ajanın alabileceği dilleri ve sesleri listeler. Şablonlar İngilizce ve Çince yazılmıştır; başka bir dil için system_prompt ve first_message alanlarını o dilde gönderin. Ya isteğin tamamı kaydedilir ya da hiç ajan oluşturulmaz; bu yüzden reddedilen bir istek düzeltildikten sonra yeniden gönderilebilir. Yanıttaki dashboard_url, ajanın paneldeki sayfasıdır; orada taslakla tarayıcıdan konuşabilirsiniz. Her ajan, EvaSay'in her arama için dile ve yüke göre seçtiği ve arama fiyatına dahil olan EvaSay ASR, EvaSay LLM ve EvaSay TTS üzerinde çalışır; ayarlanacak başka sağlayıcıların hesapları yoktur.
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..."
}'
Bu alanlardan herhangi birini PATCH /api/v1/voice/agents/{id} ile güncelleyin:
| Alan | Notlar |
|---|---|
name, first_message, system_prompt | Metin |
status | published taslağı yeni bir sürüm olarak yayımlar; draft aramaları kabul etmeyi bırakır |
language | 27 dilden biri, örn. en, zh, es, de, veya arayanı izlemek için multi |
temperature | 0-100 |
max_duration_sec, silence_timeout_sec | Arama sınırları |
interruptible, end_call_enabled | Boole değerleri |
confirm_details | Boole; sayıları ve adları geri okur ve onaylatır |
ai_disclosure | Boole, varsayılan true; ilk mesajından önce ajan, arama dilinde arayanın bir yapay zekâyla konuştuğunu söyler (ilk mesaj bunu zaten söylüyorsa atlanır) |
voicemail_detection | Boole; giden aramalar önce dinler ve bir telesekretere ulaşınca biter |
voicemail_message | Telesekretere bırakılır, {{variables}} kullanabilir; telefonu kapatmak için boş bırakın |
transfer_number | Aktarılacak numara, uluslararası biçimde |
transfer_destinations | En fazla 9 numara daha, [{name, number, when}]; ajan when değerine göre birini seçer |
transfer_briefing | Boole; ilk açan kişi ajanın görüşme özetini duyar |
pronunciations | En fazla 100 öğe [{word, say, case_sensitive}]; ses word yerine say der |
idle_messages | Arama bitmeden önce sessiz kalan arayana sırayla söylenen en fazla 3 hatırlatma |
noise_reduction | Boole; arayanın arka plan gürültüsü duyulmadan önce süzülür |
recording_enabled | Boole; aramalar iki kanalda kaydedilir (arayan solda, ajan sağda) |
recording_consent | announce, ask (yalnızca arayan kabul ederse saklanır) veya none |
recording_notice | Önce söylenen bildirim veya soru; boşsa arama dilindeki yerleşik metin |
handoffs | En fazla 5 öğe [{agent_id, when}]: bu ajanın aramayı devredebileceği diğer ajanlar |
knowledge_base_id | Bilgi tabanlarınızdan biri |
vocabulary_id | Sözlüklerinizden biri; arayan terimleri söylediğinde daha güvenilir tanınır |
voice_id | Yerleşik bir EvaSay sesinin kimliği veya klonlanmış seslerinizden biri (clone:…) |
Yerleşik sesler dile bağlıdır: Çincede ming-designed-female-clear ve ming-designed-female-warm (kadın), ming-designed-male-clear ve ming-designed-male-friendly (erkek) vardır; diğer dillerde F1–F5 (kadın) ve M1–M5 (erkek) bulunur. Ajanın dilini konuşmayan yerleşik bir ses, o dili konuşan aynı cinsiyetteki sesle değiştirilir. Eski tts_voice_id alan adı hâlâ kabul edilir. voice_id için başka herhangi bir değer veya henüz hazır olmayan klonlanmış bir ses reddedilir.
analysis_config, her arama bittiğinde nelerin çıkarılacağını belirler: summary (varsayılan true), summaryLanguage, successCriteria (başarılı bir aramanın ulaştığı sonuç), fields (çıkarılacak en fazla 20 {name, type, description}; type değeri string, number veya boolean) ve scorecard (en fazla 10 {name, description, weight}, ağırlık 1–5). Sonuçlar aramanın analysis alanında ve call.analyzed webhook'unda yer alır.
Bir ajanın alanlarını değiştirmek taslağını değiştirir. Taslağı POST /api/v1/voice/agents/{id}/publish ile (isteğe bağlı olarak bir note ile) ya da aynı PATCH içinde status: "published" ile yayımlayana kadar aramalar yayımlanmış sürümü kullanmaya devam eder. Her yayımlama numaralı bir sürümü saklar; ajandaki published_version aramaların hangisini kullandığını söyler. Bir arama başladığında yayında olan sürümde kalır; bu yüzden arama sırasında yayımlamak onu değiştirmez. Panelden yapılan test aramaları taslağı kullanır.
GET /agents/{id}/versions sürümleri listeler, POST /agents/{id}/versions/{version}/restore ise geri alır: yeni aramalar o sürümü kullanır ve taslak o sürüm olur. Ajanın adı, dağıtımı, widget görünümü ve bildirimleri sürümlenmez ve hemen değişir; bilgi tabanının belgeleri de öyledir.
Bir test, bir aramayı metin olarak oynatır: simüle edilen arayan scenario senaryosunu canlandırır, ajan taslağından (talimatlar, ilk mesaj ve bilgi) yanıt verir ve bir değerlendirici konuşmayı success_criteria ölçütüne göre denetler. Önemli aramalar için testler yazın, her değişiklikten sonra çalıştırın ve geçtiklerinde yayımlayın.
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, ajanın tüm testlerini veya test_ids içindekileri çalıştırır ve her test için bir çalıştırma döndürür (status: "running"). status değeri passed, failed veya error olana kadar GET /test-runs/{id} uç noktasını sorgulayın; o zaman çalıştırmada transcript ve değerlendiricinin reason alanı bulunur (İngilizce, veya reason_language: "zh" ile Çince). Her çalıştırma Credits tutar; tutar paneldeki testlerin yanında gösterilir. Çalıştırmalar bakiyenin karşılayamadığı ilk testte durur ve not_started kalanları listeler. Oynatılamayan bir çalıştırma iade edilir. Testler ajanın ne söylediğini denetler, sesini veya zamanlamasını değil, ve fonksiyonlarını çağırmaz: bunları önceden POST /tools/{id}/test ile deneyin. Bir ajanın en fazla 30 testi olur; PATCH ve DELETE /agents/{id}/tests/{test_id} bir testi değiştirir veya kaldırır.
Tam aramaların (type: "simulation", varsayılan) yanı sıra bir test tek bir yanıtı da kontrol edebilir. type: "next_reply", yanıttan önceki ve arayanla biten konuşmayı (chat_history; [{role, text}], role değeri caller veya agent) ve ajanın sonraki yanıtı için success_criteria alır. type: "tool_call", chat_history ve expected_tool {name, arguments, match} alır: ajan name fonksiyonunu (kendi fonksiyonlarından biri ya da end_call, transfer_call, handoff_to_agent) arguments ile çağırmalıdır; değerler tam olarak (kenar boşlukları kırpılır, büyük/küçük harf fark etmez) ya da match: "judge" ile anlamına göre karşılaştırılır; ayrıca geçirdiği argümanlar dikkate alınmaz. Bir çalıştırmanın transcript alanı, ajanın çağırdığı fonksiyonları tool_calls içinde gösterir.
POST /agents/{id}/tests/generate, ajanın taslağından (talimatlar, bilgi ve fonksiyonlar) test önerir. count (1–10, varsayılan 5), types, focus (neleri kapsamalı) ve language (adlar ve ölçütler için en veya zh) alır, bir çalıştırma kadar Credits tutar ve gözden geçirip POST /agents/{id}/tests uç noktasına göndereceğiniz taslakları döndürür; save: true ile bunları kaydeder.
hooks, aramanın belirli bir anında bir şey yapar; en fazla 10 kural: on: "silence" (ajan bitirdiğinden beri arayan seconds süre, 3–600, bir şey söylemedi; arayan tekrar konuşana kadar bir kez), on: "time_elapsed" (aramanın seconds saniyesinde, 10–3600) veya on: "call_ending". Her kural şunlardan en fazla 3 tanesini yapar (do): {type: "say", text}, {type: "transfer", destination?}, {type: "end_call", text?} ve {type: "tool", name, args?}; aramanın sonuna ait bir kural yalnızca fonksiyon çalıştırır. Bir kuralın bitirdiği aramanın ended_reason değeri rule_ended olur.
Telefon aramalarında arayanın bastığı tuşlar ajana yanıt olarak ulaşır: bir giriş # ile ya da iki saniyelik bir duraklamayla biter. Giden aramalarda ajan, bir şirketin telefon menüsünden geçmek için tuşlara kendisi de basabilir.
transfer_number, arayanı bir insana vermek için ajanın ana numarası ve çalışma alanının kredisi bittiğinde aranan numaradır. transfer_destinations her biri ne zaman kullanılacağını anlatan bir nota sahip adlandırılmış numaralar ekler („Faturalama: faturalar ve iadeler hakkındaki sorular”) ve ajan seçer. transfer_briefing açıkken ajan aktarırken kısa bir özet yazar; telefonu açan kişi arayan bağlanmadan önce bunu bir EvaSay sesiyle ve ajanın özet dilinde duyar. Aramanın ended_reason değeri o zaman transferred olur.
handoffs ile bir ajan, çalışma alanınızdaki başka bir yayımlanmış ajana aramayı devredebilir; örneğin bir ön büro, rezervasyonu rezervasyon ajanına geçirir. Ajan when değerine göre seçer. Diğer ajan, o ana kadar söylenen her şeyle aynı aramayı devralır ve kendi talimatları, sesi, bilgisi, araçları ve devirleriyle konuşur. Arama ilk ajanın altında listelenmiş, analiz edilmiş ve faturalanmış kalır; dökümü her devri not eder.
Numaralar panelde kendi operatör hesabınızla bağlanır. GET /api/v1/voice/phone-numbers bunları listeler; PATCH /api/v1/voice/phone-numbers/{id} ile { "agent_id": "AGENT_ID" } gönderildiğinde numaranın aramaları bir ajana (yayımlanmış sürümüne), "agent_id": null ile hiçbirine gider; label numarayı yeniden adlandırır.
Kelime listesi, arayanların söylediği ve konuşma tanımanın aksi hâlde yanlış duyacağı ad ve terimleri içerir; örneğin ürün, ilaç veya sokak adları. { "name": "Products", "terms": ["Lumora", "Vantix Pro"] } ile POST /api/v1/voice/vocabularies (50 karakterlik en fazla 1.000 terim, ya da başlangıç olarak "preset": "medical" veya "legal"), ardından ajanın vocabulary_id alanını ayarlayın. GET, PATCH (name, terms) ve DELETE /vocabularies/{id}; bir ajanın kullandığı liste silinemez.
Her numaranın Telefon numaraları sayfasında, ajan açmadan önce uygulanan arama kuralları vardır: engellenmiş numaralar veya numara başlangıçları (+1555) ile numarasını gizleyen arayanlar yanıtlanmadan reddedilir ve hiçbir şeye mal olmaz; listedeki arayanlar doğrudan başka bir numaraya bağlanabilir; ajan yanıtlayamadığında (yayımlanmamış, worker yok, kredi bitmiş ve aktarma numarası yok) arama bir numarayı çaldırabilir veya kendi mesajınızı dinletebilir. Bir başlangıç sorgusu (run_at_start) da {"reject": true, "message": "..."} yanıtını vererek arayanı geri çevirebilir: ajan varsa mesajı söyler ve telefonu kapatır, ended_reason değeri rejected olur.
Çalışma alanınızın telefon numaralarından birinden tek bir arama yapın. Her istek benzersiz bir Idempotency-Key gerektirir; aynı anahtarı yeniden göndermek iki kez aramak yerine aynı aramayı döndürür.
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" }
}'
Çalışma alanında birden fazla telefon numarası varsa from zorunludur. variables, ajanın ilk mesajındaki ve isteminde yer alan {{customer_name}} gibi yer tutucuları doldurur.
GET /api/v1/voice/calls/{id} aramanın durumunu, süresini, düşülen kredileri, özetini, analizini ve dökümünü döndürür. Aramaları GET /api/v1/voice/calls?agent_id=&status=&channel=&user_id=&page= ile listeleyin (user_id: bir web arama token'ından gelen, kişi için kendi kimliğiniz). Bunun turns alanı aramanın tur tur hata ayıklama günlüğüdür: arayanın konuşmayı bitirmesinden yanıtı duymasına kadar zamanın nereye gittiği, milisaniye cinsinden — waitMs (konuşma sonu), sttMs (tanıma), llmMs (modelin ilk sözcüğü, her fonksiyon turu için bir tane daha), ttsMs (sesin ilk sesi), firstAudioMs (yanıt duyuldu) — çağrılan fonksiyonlar, kesintiler ve hatalarla birlikte. Arama sayfası da bunu gösterir.
recording_enabled ile her arama iki kanallı bir WAV olarak kaydedilir: arayan solda, ajan sağda, aramanın örnekleme hızında. recording_consent: "announce" ile ajan önce aramanın kaydedildiğini söyler; "ask" ile sorar ve kayıt yalnızca arayan kabul ederse saklanır (yanıt vermemek hayır sayılır); "none" ile hiçbir şey söylenmez. Sizin ve arayanlarınızın bulunduğu yerde yasanın ne gerektirdiğini kontrol edin.
Kayıtlar EvaSay'in depolamasında veya Entegrasyonlar → Webhook'lar ve bildirimler → Kayıt depolama altında ayarladığınız kendi S3 uyumlu bucket'ınızda (AWS S3, Cloudflare R2, Google Cloud Storage, Supabase, MinIO) tutulur. Aramayı yürüten worker dosyayı doğrudan bucket'a yükler. GET /api/v1/voice/calls/{id}, kayıt saklandıktan sonra bir saat geçerli bir recordingUrl ve recording.status (recording, stored, declined veya failed) döndürür.
DELETE /api/v1/voice/calls/{id}, biten bir aramayı dökümü, kaydı (EvaSay depolamasında veya kendi bucket'ınızda), analizi ve webhook gönderimleriyle birlikte siler. Kullanılan Credits kredi geçmişinde kalır. Henüz silinemeyen bir arama code değeri call_not_deletable_yet olan 409 döndürür: arama sürüyor veya az önce bitti, hâlâ faturalandırılıyor, kaydı hâlâ yükleniyor olabilir ya da bakiyenizin karşılamadığı Credits borcu var (önce Credits ekleyin).
Biri verilerinin silinmesini istediğinde POST /api/v1/voice/calls/delete-by-caller, { "phone_number": "+14155550123" } ile bu numarayla yapılan tüm aramaları (gelen aramalarda arayanın numarası, giden aramalarda aranan numara), { "user_id": "..." } ile de token'ında bu user_id bulunan tüm web aramalarını siler. Yanıt { "deleted": 12, "kept_back": { "settling": 1 }, "more": false } şeklindedir: istek başına en fazla 200 arama silinir; more değeri true olduğu sürece tekrar isteyin, kept_back içindeki aramalar için de daha sonra tekrar deneyin. Numara aranmayacaklar listenizdeyse orada kalır. Panelde aynı işlemi Aramalar → Arayan kişinin verilerini sil yapar.
Bir arama sürerken POST /api/v1/voice/calls/{id}/control gönderin. Aramayı yürüten worker komutu yaklaşık 2 saniye içinde alır; API 202 ile yanıt verir. Aramayı yürüten worker'a ulaşılamazsa API 503 (code call_unreachable) ile yanıt verir ve komut uygulanmaz.
type | Ne olur |
|---|---|
say | Ajan, mevcut cümlesinden sonra text değerini söyler; "interrupt": true ise hemen söyler |
note | Ajana text söylenir (sesli okunmaz) ve bir sonraki yanıtından itibaren bunu hesaba katar; döküm bunu not eder |
end | Ajan verilmişse text değerini söyler, sonra telefonu kapatır; text olmayan bir telefon araması hemen kapatılır |
Aramanın ended_reason değeri o zaman api_ended olur. Paneldeki arama sayfasında aynı kontroller vardır.
POST /api/v1/voice/calls/{id}/listen (isteğe bağlı olarak { "channels": 2 }), iki dakika içinde açabileceğiniz bir listen_url döndürür. Bu bir WebSocket'tir: önce {"type":"start","sampleRate":8000,"channels":1,"encoding":"pcm_s16le"} JSON mesajı, ardından arayanın duyduğu şeyi içeren 16 bit little-endian PCM ikili çerçeveleri gelir (iki ses karışık; channels: 2 ile arayan solda, ajan sağda). Arama bittiğinde kapanır. Ses doğrudan aramayı yürüten worker'dan gelir, EvaSay'in kontrol düzleminden geçmez; panelin arama sayfasındaki Dinle düğmesi aynı adresi kullanır.
Bir aramanın sesini kendi programınızdan, örneğin kendi telefon sisteminizden veya uygulamanızdan gönderip alın. Aramayı başlatın:
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 }'
Yanıttaki websocket_url tek kullanımlık bir token taşır ve 15 dakika içinde açılabilir. encoding değeri pcm_s16le (8000, 16000 veya 24000 sample_rate ile 16 bit little-endian PCM) ya da mulaw (8 kHz μ-law) olur. Ses, bu biçimde, tercihen her biri 20 ms olan ikili çerçeveler olarak iki yönde akar. Metin çerçeveleri JSON'dur:
| Yön | Mesajlar |
|---|---|
| Siz → EvaSay | {"type":"dtmf","digits":"12#"} tuş basışları; {"type":"end"} kapatır; {"type":"text","text":"…"} arayanın yazdığı sözcükler (dokunulan bir yanıt için "input":"button"); {"type":"context","text":"…","key":"cart"} uygulamanızın bildirdiği şey (ajan buna yanıt vermez); {"type":"tool_result","id":"…","result":{…}} (veya "error") bir sayfa fonksiyonunun yanıtı |
| EvaSay → siz | start (callId ile); clear (arayan sözünü kesti: ajanın henüz çalınmamış sesini atın); transcript (role, text, final ve yazılan sözcükler için input); user_started_speaking, user_stopped_speaking, agent_started_speaking, agent_stopped_speaking; cards (cards, quick_replies); tool (id, name, arguments: sayfa fonksiyonlarınızdan birini çalıştırın); notice (code: yazılan sözcükler için too_fast veya limit); ended (reason ile, sonra soket kapanır) |
Ajan bağlanır bağlanmaz selam verir. İsteğe user_id ve page_tools (uygulamanızın yanıtladığı sayfa fonksiyonları, aşağıya bakın) eklenebilir. Aramalar diğerleri gibi dakika başına faturalanır; aynı arama kayıtları, analiz ve webhook'larla.
Bir web aramasında (widget, barındırılan sayfa, uygulamanız) arayan sesin yanında bir sohbet görür, böylece ajan konuşmaktan fazlasını yapabilir. Telefondan arayanlar bunların hiçbirini görmez.
Ajan → Araçlar → Arayana gönder seçeneğini açın (ajan API'sinde send_to_caller) ve ajan arayanın ekranına aynı anda en fazla beş kart koyabilsin: bir başlık, altında bir satır, bir açıklama, bir resim, bir bağlantı ve her birinde en fazla iki yanıt düğmesi; bir sorunun altında en fazla üç hızlı yanıt da sunabilir. Bağlantılar asla sesli okunmaz.
Ajan yalnızca o aramada kendisine verilen bağlantıları ve resimleri gönderebilir: talimatları ve karşılama, bilgi tabanı (bir web sayfasından okunan belge o sayfanın adresini taşır), fonksiyonlarınızın yanıtları ve hazır kartlar. Ayrıca bağlantı allowed_hosts içindeki sitelerden birinde olmalıdır (hazır bir kartın kendi sitesi her zaman olur). Ajanın uydurduğu veya arayanın sesli okuduğu bağlantılar gönderilmez. Ajanın sözlerindeki bu sitelere giden bağlantılar sohbette tıklanabilir.
"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": "" }
]
}
Her gönderim size call.message_sent webhook'u olarak iletilir (channel, title ve url içeren items, quickReplies, blocked). Bir arama en fazla 10 kez gönderim yapar.
Arama sırasında arayan yazabilir (bir e-posta adresi, bir sipariş numarası) veya bir yanıt düğmesine dokunabilir; ajan buna konuşmaya yanıt verir gibi yanıt verir, konuşuyorsa önce durur. Mesaj başına en fazla 500 karakter, saniyede bir, arama başına 100. Arama kaydında bu turlar yazılmış veya dokunulmuş olarak işaretlenir.
Widget sayfanıza bir evasay nesnesi verir. Her şey isteğe bağlıdır ve arama çerçevesi sayfanızı yalnızca widget'ın izin verilen web sitelerinden biriyse dinler (Ajan → Dağıt):
<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 değerleri talimatlardaki ve karşılamadaki {{name}} yer tutucusunu doldurur. Ziyaretçinin tarayıcısından gelirler; bu yüzden ajana doğrulanmadıkları söylenir ve fonksiyonlarınıza veya başlangıç sorgusuna asla ulaşmazlar.update, ajanı yanıt vermeye zorlamadan sayfada ne olduğunu ona bildirir; aynı key ile gelen daha yeni bir güncelleme eskisinin yerini alır."kind": "page", url yok) bir fonksiyon ekleyin ve sayfanızda aynı adı taşıyan bir fonksiyon kaydedin. Ajan onu yalnızca kaydetmiş bir sayfadan gelen aramalarda çağırabilir; wait_for_result değeri false değilse yanıtı en fazla timeout_sec (1–10, varsayılan 5) bekler. Ziyaretçi olarak oturum açmış biçimde çalışır, tıpkı onun kendi tıklaması gibi: para harcatan veya geri alınamayan herhangi bir şeyden önce kendi sayfanızda onay isteyin. Yanıtındaki bağlantılar kart olarak gönderilebilir.Kimler arama başlatabilir ayarını Yalnızca token ile yapın ve sunucunuzun oturum açmış kullanıcı için tek kullanımlık bir token almasını sağlayın:
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 }'
Yanıtta token (bir arama, expires_in saniye içinde, 30–3600, varsayılan 600), agent (slug) ve web_call_url bulunur. Token'ı sayfaya widget betiğindeki data-token-url="/your/endpoint" ile (ziyaretçinin çerezleriyle oraya POST eder ve { "token": "…" } okur) ya da agent.setToken(async () => token) ile verin. Token'ın variables değerleri doğrulanmıştır: talimatlar, başlangıç sorgusu ve fonksiyonlarınızın şablonları bunları kullanabilir; aramanın sakladığı user_id de öyle (aramada userId, aramaları süzmek için user_id). API anahtarınızı sunucunuzda tutun.
Kendi uygulamanızda { "token": "…", "pageTools": ["add_to_cart"] } verisini https://evasay.com + web_call_url adresine POST edin. Yanıt, aramanın bağlantısıdır (bir WebRTC teklif adresi veya arama token'ıyla bir WebSocket adresi); web, iOS, Android ve React Native için Pipecat'in istemci SDK'ları gibi bir RTVI istemcisi içindir. Kartlar, sayfa fonksiyonları ve bildirimler RTVI server-message olarak gelir (evasay.cards, evasay.tool, evasay.notice); yazılan sözcükleri send-text ile, sayfa bildirimlerini ve yanıtlarını evasay.context ve evasay.tool_result adlı client-message olarak gönderin. Ya da sesi kendiniz WebSocket üzerinden ses bölümündeki gibi, user_id ve page_tools ileterek taşıyın.
Telefon numaraları → SIP altında kendi santraliniz, SIP telefonlarınız veya SIP hattınız ajanlarınızı doğrudan arayabilir; aramaları SIP sağlayıcınız faturalar:
sip:ev…@<EvaSay's SIP domain>, yalnızca gelen aramalar, santraller (3CX, FreePBX, Asterisk…) ve SIP telefonlar için.from olarak kullanın); bir kişiye aktarmalar REFER ile hatta devredilir. Hat kimlik bilgisi istediğinde EvaSay, girdiğiniz hat kullanıcı adı ve parolasıyla yanıt verir.Gelen aramalar, EvaSay'in verdiği kullanıcı adı ve parolayla digest üzerinden oturum açar (realm SIP alan adıdır); kaynak IP'leri de sınırlayabilir veya sağlayıcınız oturum açamıyorsa yalnızca IP'ye göre kabul edebilirsiniz (IP'ler o zaman zorunludur). Her SIP adresinin veya hattın eşzamanlı arama sınırı vardır; bunun ötesinde aramalar 486 alır. Parolalar yalnızca oluşturulurken veya sıfırlanırken bir kez gösterilir. Yalnızca UDP ve G.711 (PCMU, PCMA); tuş basışları RFC 4733 veya SIP INFO ile; santralinizin hattında kaydı (REGISTER) kapatın.
Bir kampanya, arama saatleri ve bir eşzamanlılık sınırı (max_concurrency, aynı anda en fazla 100 arama ve asla çalışma alanınızın eşzamanlı arama sınırından fazla değil) içinde tek bir ajanla bir kişi listesini arar ve ulaşamadığı kişileri yeniden dener.
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" }
]
}'
Kişiler, ilk sütunda telefon numarası olan CSV metni de olabilir. consent_confirmed: true zorunludur: bununla listedeki herkesin, yapay zekâ sesiyle de olsa aranmayı kabul ettiğini onaylarsınız ve EvaSay bu onayı kampanyayla birlikte saklar (consent_confirmed_at). Kampanya taslak olarak oluşturulur; POST /api/v1/voice/campaigns/{id}/start ile başlatın, pause ve cancel işlemlerini aynı şekilde kullanın. Bir kampanya başlamadan önce ajanın yayımlanmış olması gerekir.
voicemail_detection açıkken (varsayılan) giden arama, ajan selam vermeden önce karşı tarafın konuşmasını bekler; sessiz kalırlarsa ajan iki üç saniye sonra selam verir. Ajan bir telesekreter ya da bir telefon şirketinin kaydını duyduğunda karşılamanın ve bip sesinin bitmesini bekler, voicemail_message mesajını bırakır, yoksa telefonu kapatır. Bu tür aramalar ended_reason değeri voicemail ile biter, sürdükleri süre kadar faturalanır ve analiz edilmez, bildirilmez.
Bir kampanyada telesekretere ulaşan bir numara, açmayan bir numara gibi yeniden aranır ve mesaj yalnızca son denemede bırakılır; böylece kimse aynı mesajı iki kez almaz.
Her giden arama, bir kampanyadan veya POST /api/v1/voice/calls ile yapılmış olsun, önce çalışma alanının aranmayacaklar listesine bakar. Listedeki bir numara asla aranmaz: API do_not_call code değeriyle 409 yanıtı verir ve kampanya kişiyi canceled olarak ve outcome do_not_call ile işaretler ve bir sonrakine geçer.
Ajanın yaptığı bir aramadaki kişi bir daha aranmak istemediğini söylediğinde ajan numarayı kendisi listeye ekler, bunu kişiye söyler ve call.do_not_call webhook'u gönderilir. Numaralar ayrıca Kampanyalar → Aranmayacaklar listesi altında veya API ile eklenip çıkarılabilir:
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" }'
Numaralar uluslararası biçimdedir; tek seferde en fazla 1.000. GET /api/v1/voice/do-not-call bunları en yenisi başta olacak şekilde listeler (search, limit, page), her biri source değeriyle (call, dashboard veya api). DELETE /api/v1/voice/do-not-call/%2B14155550100 birini yeniden listeden çıkarır; bunu yalnızca kişi aranmayı kabul ettiğinde yapın.
POST /api/v1/voice/voices, tek bir konuşmacının WAV kaydından bir ses oluşturur (multipart audio, name, language, consent=true). running durumuyla 202 yanıtı verir; ready olana kadar GET /api/v1/voice/voices sorgulayın, sonra bir ajanın voice_id değerini sesin voice_id değerine ayarlayın. language, kayıtta konuşulan dildir; yukarıdaki ajan dillerinden herhangi biri olabilir; ses Çince hariç hepsini konuşur, Çincede aynı cinsiyetteki yerleşik bir ses yanıt verir ve kaydın dilinde kayıttaki konuşmacıya en çok benzer. Her klonlanmış ses kredi harcar; panel bir tane oluşturmadan önce bunları gösterir.
Arama olaylarını almak için Entegrasyonlar → Webhook'lar ve bildirimler altına bir https adresi ekleyin:
| Olay | Ne zaman |
|---|---|
call.started | Ajan açtı |
call.ended | Arama bitti; süresi ve kredileriyle |
call.analyzed | Özet, sonuç, çıkarılan alanlar ve döküm hazır |
call.transcript | Arama sırasında, dökümün yeni satırları birkaç saniyede bir |
call.tool_called | Ajan fonksiyonlarınızdan birini çağırdı: name, arguments, status, durationMs |
call.handed_off | Arama başka bir ajana devredildi: fromAgentId, toAgentId |
call.transferred | Arama bir kişiye aktarıldı: destination |
call.voicemail | Giden bir arama telesekretere ulaştı |
call.do_not_call | Giden aramadaki kişi bir daha aranmak istemediğini söyledi: phone |
call.recording_ready | İki kanallı kayıt saklandı: recordingUrl (24 saat geçerli), durationMs |
call.message_sent | Ajan, web veya uygulama arayanının ekranında kartlar gösterdi: items, quickReplies |
alert.triggered | Uyarılarınızdan biri (Webhook'lar ve bildirimler → Uyarılar) eşiğini aştı |
alert.resolved | O uyarı normale döndü |
Arama sırasında gönderilen olaylar data.call taşır (id, agentId, channel, direction, numaralar). Her istek imzalıdır:
EvaSay-Signature: t=<unix seconds>,v1=<hex HMAC-SHA256 of "<t>.<raw body>">
EvaSay-Delivery: <delivery id>
İmzayı webhook'u oluştururken gösterilen webhook gizli anahtarıyla doğrulayın ve 2xx durumuyla yanıt verin. Başarısız teslimatlar aynı gövde ve aynı EvaSay-Delivery kimliğiyle 1 dakika, 5 dakika, 30 dakika, 2 saat ve 12 saat sonra yeniden denenir; böylece tekrarları yok sayabilirsiniz. Bir webhook'un son teslimatları, gönderilen tam gövde ve uç noktanızın verdiği durumla birlikte GET /webhooks/{id}/deliveries ve panel tarafından listelenir; POST /webhooks/{id}/deliveries/{delivery_id} birini yeniden gönderir.
E-postanın ve alert.triggered / alert.resolved webhook'larının yanı sıra uyarılar Webhook'lar ve bildirimler → Uyarılar → Şuraya da gönder altından bir Slack kanalına (incoming webhook), bir WeCom grup robotuna veya Telegram'a gidebilir.
Hatalar tek bir biçim kullanır:
{
"error": {
"type": "invalid_request_error",
"code": "invalid_agent_field",
"message": "status must be draft or published",
"param": "status"
}
}
| Durum | Anlamı |
|---|---|
| 400 | Geçersiz girdi (code ve param hangisi olduğunu söyler) |
| 401 | API anahtarı eksik veya geçersiz |
| 402 | Aramayı başlatmak için yeterli kredi yok |
| 403 | Anahtar salt okunur veya ajan kullanılamıyor |
| 404 | Bu çalışma alanında böyle bir ajan, arama veya kampanya yok |
| 409 | Yanlış durum (örn. çalışan bir kampanyayı başlatmak), bir sağlayıcı hesabı bağlı değil veya numara aranmayacaklar listesinde (do_not_call) |
| 429 | Çok fazla istek; yavaşlayın ve yeniden deneyin |
Çalışma alanının son 30 gündeki Voice API istekleri (zaman, endpoint, durum, süre, anahtar ve hata kodu; gövde yok) API anahtarları → API istek günlüğü altında listelenir.
Okumalar anahtar başına dakikada 120 istekle, yazmalar ve aramalar 30 istekle sınırlıdır.
Aramalar, panelin kullandığı aynı bakiye olan çalışma alanının kredilerinden dakika başına faturalanır. Güncel ücretler fiyatlandırma sayfasındadır.
Bir entegrasyon istemcisi oluşturmak için OpenAPI sözleşmesini kullanın. Genel API, panel ve MCP yönetim uç noktası aynı çalışma alanı kaynakları ve izinleri üzerinde çalışır.
| Yetenek | Endpoint |
|---|---|
| Yeniden kullanılabilir araçlar | GET / POST /api/v1/voice/tools |
| Aracı oku / güncelle / sil | GET / PATCH / DELETE /api/v1/voice/tools/{id} |
| Aracı önizle veya test et | POST /api/v1/voice/tools/{id}/test |
| Araçları bir ajana bağla | PUT /api/v1/voice/agents/{id}/tools |
| Bilgi tabanları | GET / POST /api/v1/voice/knowledge-bases |
| Bilgi tabanını oku / güncelle / sil | GET / PATCH / DELETE /api/v1/voice/knowledge-bases/{id} |
| Belge ekle / listele | POST / GET /api/v1/voice/knowledge-bases/{id}/documents |
| Belgeyi oku / sil | GET / DELETE /api/v1/voice/knowledge-bases/{id}/documents/{doc_id} |
| Arama olayı abonelikleri | GET / POST /api/v1/voice/webhooks |
| Aboneliği oku / güncelle / sil | GET / PATCH / DELETE /api/v1/voice/webhooks/{id} |
| İmzalı bir test olayı gönder | POST /api/v1/voice/webhooks/{id}/test |
| Uzak MCP araçlarını keşfet | POST /api/v1/voice/mcp/discover |
| MCP yönetim uç noktası | /api/v1/voice/mcp |
CRM bağdaştırıcınızı sorgulayan bir HTTP aracı kaydedin:
{
"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", ... } } döndürür. Tanımını PUT /agents/AGENT_ID/tools ve { "tool_ids": ["TOOL_ID"] } ile bir ajana bağlayın. Bu, araç listesinin tamamını anlık görüntülerle değiştirir; bir kütüphane aracı değiştiğinde yeniden bağlayın. Bir kütüphane tanımını silmek mevcut anlık görüntüleri korur. Satır içi tanımları ayrıca ajan API'sinin tools alanıyla okuyup değiştirebilirsiniz. Analizi ve widget'ı ajan API'si üzerinden yapılandırmak için analysis_config ve widget_config kullanın.
Bir araç GET, POST, PUT, PATCH veya DELETE kullanabilir. query ve request_body, { "customer": { "id": "{{customer_id}}" } } gibi iç içe JSON şablonlarını kabul eder. Tam bir yer tutucu özgün JSON türünü korur. URL yer tutucuları kodlanır ve yalnızca sabit bir genel HTTPS ana bilgisayarından sonra yer alabilir. request_body olmadan GET olmayan araçlar { "arguments": {...}, "call": { "callId": "...", "agentId": "...", "toolCallId": "...", "idempotencyKey": "..." } } alır. response_path noktalı gösterimle bir alan seçer. Basit parameters hâlâ kabul edilir; input_schema ek olarak iç içe nesneleri, dizileri, sabit listeleri ve sayısal sınırları destekler. Harici şema başvuruları ve düzenli ifadeler kabul edilmez.
static_args: model ne yazarsa yazsın her çağrıyla gönderilen değerler, örneğin bir mağaza kimliği; adres ve gövdedeki {{name}} yer tutucusunu da doldururlar.extract: [{name, path}], bir fonksiyonun yanıtındaki alanları (data.id) sonraki fonksiyonların {{name}} değeri için saklar; böylece bir aramanın customer_id değeri, model onu tekrarlamadan sonraki siparişe girebilir. Bir başlangıç sorgusunun alanları da aynı şekilde saklanır.reject_when: en fazla 5 kural {on: caller|agent, pattern, negate}; son sözcükler için hepsi geçerli olduğu sürece fonksiyon çalışmaz ve ajan reject_message alır.model_says: her çağrıda model, sabit speak_before yerine fonksiyon çalışırken söylenen bir cümle yazar.Entegrasyonlar → Webhook'lar ve bildirimler → Bağlı uygulamalar altında bir uygulama bağlayın, sonra ajanın Bilgi ve araçlar sekmesinde „Hazır fonksiyon ekle” seçeneğini kullanın; kendi uç noktanıza gerek yoktur:
check_availability (bir tarihteki boş saatler) ve book_appointment (arayanın seçtiği saati ayırır) ekler. Google Calendar saatleri sizin belirlediğiniz saat dilimini, açık günleri, saatleri ve randevu süresini izler; Cal.com etkinlik türünün kendi uygunluğunu ve arayan hiçbir adres vermediğinde kullanılmak üzere belirlediğiniz yedek e-postayı kullanır.save_to_sheet ekler; bu, sıraladığınız sütunlara ek olarak aramanın kimliği ve saatiyle bir satır ekler.notify_team ekler; ajanın yazdığı bir mesajı bir Slack kanalına (incoming webhook) gönderir.EvaSay kontrol düzlemi bu fonksiyonları sizin bağladığınız hesapla çalıştırır; ne worker ne de ajan tanımı uygulamanın kimlik bilgilerini tutar. Bir ajanı API ile okumak bunları kind: "app" olarak gösterir (app, action, connection_id, app_config ile); korumak için tools alanını değiştirirken bunları olduğu gibi geri gönderin. Uygulamalar yalnızca panelde bağlanır.
Bir ajanın araçlarından birinde run_at_start: true ayarlayın; her arama başlarken, ajan selam vermeden veya yanıtlamadan önce çağrılır. caller_number, called_number, direction, channel veya call_id adlı parametreler onun için doldurulur. Döndürdüğü alanlar first_message, system_prompt ve idle_messages içindeki {{variables}} yer tutucularını doldurur (bir düzey aşağıdaki alanlar {{customer.name}} olarak) ve yanıtın tamamı ajana arka plan olarak verilir. Değişkenli bir karşılama yanıt için en fazla 3 saniye bekler; bulunamayan her şey boş bırakılır. Ajan başına başlangıçta bir araç çalışabilir.
Kimlik bilgileri çalışma alanınız ve hedefiniz için sunucu ortamında yapılandırılır; bir credential_ref hazırlanması için support@evasay.com ile iletişime geçin. API anahtarlarını ajan tanımlarına, başlıklara, istemlere veya araç parametrelerine koymayın. Bağdaştırıcınız CRM kimlik bilgilerini kendi ortamında tutabilir.
İş sistemine istek göndermeden önce bir aracı önizleyin:
{
"arguments": { "customer_id": "customer-123" },
"dry_run": true,
"mock_result": { "name": "Alex", "order_status": "shipped" }
}
Önizleme parametreleri doğrular ve eşlenen isteği gösterir; bir konuşma yürütmez ve iş sistemiyle iletişim kurmaz. Gerçek yürütme için dry_run=false ayarlayın. Canlı bir yazma testi ayrıca confirmed=true ve idempotency_key gerektirir (8–128 harf, rakam, nokta, alt çizgi, iki nokta veya kısa çizgi). Test yanıtları ok, status, duration_ms ve sınırlı bir sonuç içerir. Kimlik bilgilerini döndürmezler. Okuma ve yazma testleri çalışma alanı yazma izni gerektirir.
Bir arama sırasında iş istekleri sabit bir Idempotency-Key taşır. İş uç noktanız buna uymalıdır. Bir arama içindeki özdeş yazmalar tekilleştirilir; zaman aşımından sonra sonucu bilinmeyen işlemler de dahil. Farklı, bilerek yapılan bir işlem operation_id gibi ayrı bir iş argümanı gerektirir. EvaSay yazmaları otomatik olarak yeniden oynatmaz. Araç yürütme olayları durum ve süre içerir; konuşma testi özelliği bir metin simülasyonu olarak kalır, araç yürütme testi değildir.
Şu değerlerle bir Streamable HTTP MCP bağlantısı yapılandırın:
URL: https://evasay.com/api/v1/voice/mcp
Authorization: Bearer <your EvaSay API key>
Örneğin Claude Code içinde:
claude mcp add --transport http evasay https://evasay.com/api/v1/voice/mcp --header "Authorization: Bearer $EVASAY_API_KEY"
Bağlantı, araca bir ajanın nasıl kurulacağını anlatır: kataloğu okumak, taslak oluşturmak, bilgi, fonksiyon ve kelime listeleri eklemek, testler yazıp çalıştırmak, testler geçene kadar taslağı düzeltmek, denemeniz için ajanın dashboard_url adresini size vermek, ve yayımlamayı ve numara yönlendirmeyi ancak onayınızdan sonra yapmak. Belge okuyan araçlar, bu sayfaya, OpenAPI sözleşmesine ve MCP adresine bağlantı veren https://evasay.com/llms.txt adresinden başlayabilir.
İstemcinin güvenli token veya ortam değişkeni ayarını kullanın. Uç nokta API anahtarı kimlik doğrulaması kullanır, OAuth oturum açma akışı kullanmaz. Ajan oluşturmayı ve yapılandırmayı, kütüphane araçlarını, bilgiyi, webhook'ları ve arama kayıtlarını sunar. Yayımlamak ve gerçek aramalar yapmak yazma eylemleridir; giden aramalar kredi ve operatör ücretleri harcar. Her araç çağrısı çalışma alanı izinlerini yeniden denetler. İstemcinizin yazma eylemleri için onay denetimlerini açın. Uç nokta durumsuz JSON yanıtları kullanır; arka plan bildirim akışı sunmaz.
Kodlama aracı için örnek bir talimat: „Taslak bir EvaSay ajanı oluştur, CRM sorgu uç noktamı kaydet, test argümanlarıyla önizle, ajana bağla ve bir call.analyzed webhook'u yapılandır.” Yayımlamayı ve gerçek aramaları gözden geçirin ve açıkça yetkilendirin.
POST /mcp/discover, { "url": "https://crm.example.com/mcp", "credential_ref": "crm" } kabul eder ve sunucunun araçlarını ve şemalarını döndürür. Yalnızca seçilen aracı kaydedin:
{
"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"]
}
}
Ajanın kullanımına yalnızca kayıtlı araçlar açılır. Uzak bağlantılar, isteklere JSON veya SSE yanıtlarıyla Streamable HTTP'yi, genel HTTPS hedeflerini ve önceden yapılandırılmış kimlik bilgilerini destekler. Eski SSE uç noktaları, OAuth müzakeresi, sunucu tarafından başlatılan sampling/elicitation ve ikili ekler desteklenmez. Keşif, sunucunun tüm araçlarına otomatik erişim vermez. Uzak aracın şeması satır içi olmalı ve JSON Schema'nın desteklenen alt kümesini kullanmalıdır.
POST /webhooks ile { "url": "https://crm.example.com/evasay-events", "events": ["call.analyzed"] } kullanarak bir webhook oluşturun. Yanıt, aboneliği ve yalnızca oluşturma sırasında döndürülen, üretilmiş bir imza gizli anahtarını içerir. EvaSay-Signature değerini doğrulayın, EvaSay-Delivery değerini tekilleştirin ve CRM'inizi güncelleyin. Olay webhook'ları eşzamansız bildirimlerdir; arama sırasındaki araçlar sonuçları doğrudan ajana döndürür.