Arama Başlatma — POST /calls
Bir CRM’e yeni lead düştüğünde tek bir POST isteğiyle yapay zeka asistanın o numarayı saniyeler içinde arayabilir — kontak yoksa otomatik açılır, asistan konuşur, görüşme faturalandırılır. Bu uç, Purvisor AI Public API’nin çekirdek yazma aksiyonudur; Zapier ve Make’teki “Arama Başlat” adımı da tam olarak bunu çağırır. Bir düşün: reklamdan gelen lead’i elle aramak yerine, form gönderilir gönderilmez asistanın devreye girer.
Bu uç anında arama yapar; kadans/kampanya zamanlaması uygulamaz. Zamana yayılmış çok adımlı takip için Kadans Nedir? veya Kampanya Oluşturma tarafını kullan.
İstek
POST /api/v1/calls
X-API-Key: pk_live_...
Content-Type: application/jsonKimlik doğrulama tüm /api/v1/* uçlarında olduğu gibi API anahtarıyla yapılır. Anahtarı nasıl alacağın ve gönderme biçimleri için Kimlik Doğrulama sayfasına bak.
Gövde alanları
İstek gövdesi hem snake_case hem camelCase kabul eder (örn. assistant_id veya assistantId).
| Alan | Zorunlu | Açıklama |
|---|---|---|
phone | Evet | Aranacak numara. En az 10 hane içermeli (rakam dışı karakterler yok sayılır). |
assistant_id | Evet | Aramayı yapacak asistanın id’si. Bu hesaba ait ve aktif olmalı. Listeyi GET /assistants ile çek. |
phone_number_id | Hayır | Arayan numara (caller-ID). Verilmezse hesaptaki ilk aktif numara kullanılır. Verilirse hesaba ait ve aktif olmalı. |
first_name | Hayır | Kontak yoksa oluşturulurken kullanılır. Boşsa "Müşteri" yazılır. |
last_name | Hayır | Kontak yoksa oluşturulurken kullanılır. |
Numarayı elle vermek yerine
phone_number_idalanını GET /phone-numbers çıktısındaki bir id ile besle — böylece hangi hattan çıktığını kontrol edersin.
curl örneği
curl -X POST https://app.purvisor.ai/api/v1/calls \
-H "X-API-Key: pk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"phone": "+905321234567",
"assistant_id": "asst_abc123",
"phone_number_id": "42",
"first_name": "Ayşe",
"last_name": "Yılmaz"
}'İşleyiş ve kapılar
Uç, kanıtlanmış kampanya makinesini (oda metadata + SIP + agent dispatch + faturalama) yeniden kullanır. Arama gerçekleşmeden önce sırayla şu kapılardan geçilir:
POST /calls
↓
Gövde doğrulama (phone + assistant_id, ≥10 hane) → 400
↓
Asistan sahipliği (bu hesaba ait + aktif mi?) → 404
↓
Arayan numara (verilen ait+aktif mi / ilk aktif) → 404
↓
Arama izni: hesap penceresi ∩ yasal pencere → 422
↓
Eşzamanlılık ve hacim tavanı → 429 / 503
↓
Dakika bakiyesi ≥ 1 → 402
↓
Kontağı bul (telefonla dedupe) — yoksa oluştur
↓
Gizli "zapier-auto" kampanyası (asistan+numara başına)
↓
Devam eden arama var mı? (çift arama koruması) → 409
↓
makeCall → arama başlar → 502 (başarısızsa)
↓
201 { call_id, status: "initiated", ... }Gizli kampanya mekaniği. Arama, (asistan, numara) çifti başına oluşturulan gizli bir kampanya altında yürür — description="zapier-auto", status="completed", isActive=false. Bu bayraklar sayesinde zamanlayıcılar ve kampanya worker’ı bu kampanyaya dokunmaz; panelindeki normal kampanya raporlarını kirletmez. Sen bir şey yapmana gerek yok, otomatik yönetilir.
Kontak eşleştirmesi POST /contacts ile aynı mantıkla çalışır: tam numara veya son 10 hane eşleşmesi. Eşleşen kontak yoksa yenisi leadSource="zapier", status="new" ile açılır.
Aynı kişiye devam eden bir API araması varken ikinci istek atarsan HTTP 409 dönersin — çift arama böyle engellenir. Bir Zap’i yanlışlıkla iki kez tetiklersen ikinci çağrı sessizce yeni arama başlatmaz.
Başarılı yanıt
Arama başlatıldığında HTTP 201 ve şu gövde döner:
{
"call_id": "1287",
"status": "initiated",
"contact_id": "9043",
"assistant_id": "asst_abc123",
"from_number": "+904440000",
"to": "+905321234567"
}| Alan | Açıklama |
|---|---|
call_id | Başlatılan aramanın id’si. Görüşme bitince call.completed webhook’unda bu değerle eşleşir. |
status | Her zaman "initiated" — arama kuyruğa alındı, henüz bağlanma sonucu değil. |
contact_id | Aranan kontağın id’si (bulunan veya yeni oluşturulan). |
assistant_id | İstekte gönderdiğin asistan id’si. |
from_number | Aramanın çıktığı numara (caller-ID). |
to | Kontağın kayıtlı telefon numarası. |
warning: "outside_your_business_hours" alanı kaldırıldı. Eskiden çalışma saatlerin dışında başlatılan aramalar 201 + bu uyarıyla dönüyordu. Hesap arama penceresi artık bağlayıcı bir kapı olduğu için o durum 201 değil 422 üretir (aşağıya bak). Bu alana bakan bir Zap/senaryon varsa koşulu HTTP durum koduna taşı.
Arama saatleri
Arama iki katmandan geçer ve ikisi de kapıdır:
- Hesap arama penceresi — Ayarlar → Arama penceresi’nde tanımladığın saat ve günler. API’yi çağırmış olman bu pencereyi aşmaz.
- Aranan ülkenin yasal penceresi — numaradan çözülen ülkeye göre uygulanır.
İzin = hesap penceresi ∩ (uygulanıyorsa) yasal pencere. Biri kapalıysa arama başlatılmaz: HTTP 422.
Hangi ülkede hangi pencere?
| Aranan | Pencere (aranan yerel saatiyle) | Bildirgeyle aşılabilir mi? |
|---|---|---|
| ABD | 08:00–21:00, her gün (TCPA) | Hayır |
| Birleşik Krallık | 08:00–21:00, Pzt–Cmt (Ofcom) | Hayır |
| Türkiye | 09:00–19:00, Pzt–Cmt | Evet |
| Paketi olmayan ülke (DE, CA, AE…) | 09:00–19:00, Pzt–Cmt | Hayır |
| Ülkesi / saat dilimi çözülemeyen numara | — | Arama hiç yapılmaz |
Türkiye’de yasal pencere kapı değil, eşiktir. Onaysız bir hesabın arama penceresi bu eşiğin içinde kalmak zorundadır; bildirgeyi onaylayan hesap dışına çıkabilir (Ayarlar → Arama penceresi). Sonuç: TR numarada outside_legal_call_window dönmez — pencere dışı istek outside_business_hours alır, onaylı geniş pencerede ise arama gider (201).
Ülkesi ya da saat dilimi çözülemeyen numara aranmaz. Yasal saati doğrulanamayan bir numarayı aramak yerine istek 422 callee_region_unknown ile reddedilir. Kanada ve Avustralya gibi çok saat dilimli ülkelerde kişide kayıtlı saat dilimi yoksa tahmin yapılmaz — o numara da bu kapıya takılır. Çözüm pencereyi değiştirmek değil, numarayı E.164 biçiminde (+49…) göndermek ya da kişinin ülke/saat dilimi bilgisini doldurmaktır.
422 yanıt gövdesi
{
"error": "Calling is outside your account's calling hours (Settings → Call window). Retry after next_allowed_at.",
"code": "outside_business_hours",
"blocked_by": ["account"],
"country_code": "TR",
"next_allowed_at": "2026-09-16T06:00:00.000Z"
}| Alan | Tür | Anlam |
|---|---|---|
code | "outside_legal_call_window" · "outside_business_hours" · "callee_region_unknown" | Reddi tek sebebe indirger. |
blocked_by | dizi: "account" · "campaign" · "legal" | Dallanmak için kullanacağın kararlı alan — reddeden bütün katmanları sayar. |
country_code | dizge · null | Numaradan çözülen aranan ülke. |
next_allowed_at | ISO-8601 · null | Bir sonraki izinli an. Katmanlar önümüzdeki haftalarda hiç kesişmiyorsa null. |
error | dizge | İnsan okuması için; dallanmada kullanma, uyarısız değişir. |
Ne yapmalı?
next_allowed_atdolu,blocked_byiçinde"legal"→ yapılacak şey beklemek; Zap’inext_allowed_at’e ertele.next_allowed_atdolu, yalnız"account"→ ya bekle ya da Ayarlar → Arama penceresi’ni genişlet.next_allowed_atnull→ bu numara bu hesap penceresiyle önümüzdeki haftalarda aranamaz; otomatik yeniden deneme kurma. Pencereyi değiştir. (Yaz saati geçişleri kesişimi haftalar sonra açabilir; pencere değişmeden tekrar denemek günlerce aynı 422’yi döndürür.)code: "callee_region_unknown"→ yeniden deneme yok, pencere değişikliği de çözmez. Numarayı düzelt.
Kesişim yok örneği (TR hesap 09:00–18:00 + Los Angeles numarası — saat 12:00’de de 20:00’de de aynı gövde döner):
{
"error": "Your account's calling hours do not overlap the legally permitted hours for the destination country in the coming weeks, so this number cannot be called with the current call window. Retrying will not help; widen the call window (Settings → Call window).",
"code": "outside_legal_call_window",
"blocked_by": ["account", "legal"],
"country_code": "US",
"next_allowed_at": null
}Bölgesi çözülemeyen numara örneği:
{
"error": "The destination number's country or time zone could not be determined, so its legal calling hours cannot be verified and this number will not be called. Retrying will not help; check the phone number (international format, e.g. +49…).",
"code": "callee_region_unknown",
"blocked_by": ["legal"],
"country_code": "NG",
"next_allowed_at": null
}Değişenler (entegrasyonunu etkileyebilir). TR numarada outside_legal_call_window artık dönmüyor; hesap penceresi kapalıyken eskiden gelen yanıltıcı 502 "Could not start the call…" yerine artık sebebiyle birlikte 422 dönüyor; next_allowed_at null olabilir; blocked_by yeni eklendi; warning: "outside_your_business_hours" kaldırıldı. Koşullarını error metnine değil, HTTP durum koduna ve blocked_by/code alanlarına bağla.
status: "initiated"“telefon çaldı/açıldı” demek değildir; sadece aramanın başarıyla kuyruğa alındığını bildirir. Görüşmenin sonucunu (süre, özet, duygu) öğrenmek içincall.completedwebhook’una abone ol — bkz. Webhook’lar.
Eşzamanlılık ve hacim tavanları
Aynı anda kaç aramanın açık olabileceği planına bağlıdır; ayrıca hesabına saatlik ve günlük toplam arama tavanı koyabilirsin. İstek bu kapıya takılırsa arama başlatılmaz ve bakiyenden dakika düşülmez — kapı bakiye rezervinden önce çalışır.
| Durum | HTTP | code | Gövdedeki ek alanlar |
|---|---|---|---|
| Eşzamanlılık tavanı dolu | 429 | concurrency_limit | scope: "tenant" (senin planının tavanı) veya "global" (platform tavanı), active, limit |
| Saatlik arama tavanı dolu | 429 | hourly_call_cap | scope: "hourly", active, limit |
| Günlük arama tavanı dolu | 429 | daily_call_cap | scope: "daily", active, limit |
| Kapı karar veremedi | 503 + Retry-After: 30 | call_gate_unavailable | retry_after: 30 |
{
"error": "Concurrent or volume call limit reached",
"code": "concurrency_limit",
"scope": "tenant",
"active": 10,
"limit": 10
}429 ile 503 aynı şey değil:
- 429 gerçek bir tavandır.
activevelimitile birlikte gelir; hızını düşür, kuyruğunu yay. Eşzamanlılık tavanı planına göredir (Planlar); saatlik/günlük tavanlar senin kendi koyduğun frendir ve tanımlı değilse uygulanmaz. - 503 geçicidir. Tavan dolduğu için değil, kapı o an doğrulanamadığı için reddedilir (arka plandaki sayaç okunamamıştır).
Retry-Afterkadar bekleyip aynı isteği tekrar dene — bir şeyi düzeltmen gerekmiyor. Kapı doğrulanamadan arama açılmaz; çünkü açık geçilseydi tavan hiç uygulanmamış olurdu.
Değişti — eskiden bunların hepsi tek bir 502 "Could not start the call — check your minute balance and phone number" idi. Entegratör tavanın dolduğunu anlayamıyor, boşuna bakiye ve numara kontrol ediyordu. Artık her durumun ayrı kodu var; bakiye (402) ve arama penceresi (422) zaten ayrı kodlardı. Koşullarını error metnine değil, HTTP durum koduna ve code alanına bağla.
Hata kodları
| Kod | Gövde (error) | Neden |
|---|---|---|
| 400 | invalid_json | Gövde geçerli JSON değil. |
| 400 | phone and assistant_id are required | Zorunlu alanlardan biri eksik. |
| 400 | Invalid phone — must contain at least 10 digits | Telefon 10 haneden az. |
| 401 | invalid_api_key | API anahtarı geçersiz/eksik. |
| 402 | Insufficient minute balance | Dakika bakiyesi 1’in altında. |
| 429 | concurrency_limit · hourly_call_cap · daily_call_cap | Eşzamanlılık ya da hacim tavanı dolu. Gövdedeki active/limit ile ne kadar aştığını görürsün. |
| 422 | outside_legal_call_window | Aranan ülkenin yasal arama saatleri dışında (ya da hesap penceresiyle yasal pencere hiç kesişmiyor). next_allowed_at doluysa o ana ertele, null ise pencereyi değiştir. |
| 422 | outside_business_hours | Hesabının arama penceresi dışında (Ayarlar → Arama penceresi). |
| 422 | callee_region_unknown | Numaranın ülkesi / saat dilimi çözülemedi; yasal saat doğrulanamadığı için arama yapılmaz. Yeniden deneme çözmez — numarayı E.164 biçiminde gönder. |
| 404 | assistant_id not found, not owned by this account, or inactive | Asistan yok, başka hesaba ait ya da pasif. |
| 404 | phone_number_id not found or inactive | Verilen numara yok/pasif. |
| 404 | No active phone number on this account | phone_number_id verilmedi ve hesapta aktif numara yok. |
| 409 | A call to this contact is already in progress | Aynı kontağa açık bir API araması mevcut. |
| 503 | call_gate_unavailable | Eşzamanlılık kapısı o an doğrulanamadı. Geçici — Retry-After (30 sn) kadar bekleyip tekrar dene. |
| 502 | Could not start the call — check your minute balance and phone number | Arama başlatılamadı. Kalan sebepler: kişi DNC listesinde ya da SIP/santral sorunu. Arama penceresi (422), eşzamanlılık/hacim tavanı (429), kapı doğrulanamaması (503) ve bakiye (402) artık bu satıra düşmez — kendi kodlarıyla döner. |
Hata metinlerine göre dallanma yapma — bu dizeler uyarı yapılmadan değişir (v1 mesajları bir kez toptan Türkçe’den İngilizce’ye çevrildi). Zap/senaryo koşullarını HTTP durum koduna göre kur.
Tüm hata kodlarının ortak referansı için Limitler ve Hatalar sayfasına bak.
KVKK notu. Bu uç gerçek bir telefon araması başlatır. Aradığın numaranın açık rızası otomatik arama kapsamını da içermeli; asistanının açılışında kim olduğunu ve neden aradığını net söylediğinden emin ol. Rıza yönetimi için KVKK ve Veri Yönetimi.
Sorun giderme
| Belirti | Olası neden / çözüm |
|---|---|
| 402 dönüyor | Dakika bakiyesi bitmiş → Dakika Paketleri ile yükle. |
| 404 (asistan) | Yanlış assistant_id veya asistan pasif → GET /assistants ile doğru id’yi çek. |
| 404 (numara) | Hesapta aktif SIP numarası yok → Telefon Numarası (SIP) ile bağla. |
| 409 dönüyor | Aynı kişiye zaten açık bir arama var; Zap’in çift tetiklenmediğinden emin ol. |
422 outside_legal_call_window dönüyor | Aranan ülkenin yasal arama penceresi dışındasın (TR 09:00–19:00, Pzt–Cmt). Hemen tekrar denemek işe yaramaz: yanıttaki next_allowed_at zamanına kadar bekle ve isteği o zaman gönder. |
| 502 dönüyor | Kişinin DNC listesinde olup olmadığını, numaranın aktif ve SIP trunk’lı olduğunu kontrol et. Bakiye, arama penceresi ve eşzamanlılık/hacim tavanı artık 502 değil, kendi kodlarını (402 / 422 / 429) döner. |
| 429 alıyorum ama tavanı ben koymadım | scope alanına bak: tenant planının eşzamanlılık tavanı, global platform tavanı, hourly/daily ise hesabındaki saatlik/günlük arama freni. |
503 call_gate_unavailable sürekli geliyor | Tekrar denemeye devam et (Retry-After kadar bekleyerek); tavan dolu değil, kapı doğrulanamıyor. Sürüyorsa destekle iletişime geç. |
| 201 aldım ama telefon çalmadı | initiated yalnızca kuyruğa alındığını gösterir; sonucu call.completed webhook’undan izle. |