Limitler ve Hata Kodları
Entegrasyonunu yazarken en çok zamanı beklenmedik hata yanıtları yer. Purvisor Public API bu konuda öngörülebilir: her hata aynı JSON zarfıyla döner — kök alanı her zaman error — ve iki gerçek kaynak kapısı vardır: arama başlatmada dakika bakiyesi (402) ve yeni kayıt açarken plan bazlı adet kotası (403 + code: "quota_exceeded"). Bu sayfa, hangi durumda hangi HTTP kodunun geldiğini ve entegrasyonunun bunları nasıl ele alması gerektiğini net biçimde açıklar.
Hız limiti (rate limit) var mı?
Evet — API anahtarı başına dakikada 60 istek. Limit IP’ye değil anahtara bağlıdır: paylaşımlı bir IP’den çalışan entegrasyonlar birbirini etkilemez, tek bir anahtar da tüm Public API’yi boğamaz.
Limit aşılırsa 429 döner ve yanıtta Retry-After başlığı bulunur — kaç saniye sonra tekrar deneyebileceğini bu başlıktan oku.
Public API, Pro ve üzeri planlarda kullanılabilir. Anahtarın geçerli olsa bile planın kapsamıyorsa her çağrı 403 ile {"error":"premium_required","feature":"api_access"} (+ açıklayıcı bir message) döner. Bu kapı anahtar üretiminde değil her istekte uygulanır; yani Pro’yken anahtar üretip sonra plan düşerse erişim de durur.
Plan kapısının dışında iki kaynak kapısı vardır: dakika bakiyesi — yalnızca arama başlatmada devreye girer (402, aşağıda) — ve plan adet kotası — yeni kayıt açan uçlarda (POST /contacts, POST /leads) devreye girer ve 403 + code: "quota_exceeded" döner.
İyi bir entegrasyon limitlere kibar davranır — gereksiz döngüsel çağrılardan kaçın,
429aldığındaRetry-Afterkadar bekle, hataları geri-çekilmeli (backoff) tekrar dene. Aşağıdaki “İyi uygulamalar” bölümüne bak.
Dakika bakiyesi kısıtı — POST /calls
Arama başlatmadaki kaynak kapısı dakika bakiyesidir. POST /api/v1/calls çağrısı, hesabının dakika bakiyesi en az 1 olmadıkça aramayı başlatmaz:
POST /api/v1/calls
↓
API anahtarı geçerli mi? → hayır: 401 invalid_api_key
↓ evet
assistant_id bu hesaba ait+aktif? → hayır: 404
↓ evet
arayan numara ait+aktif? → hayır: 404
↓ evet
dakika bakiyesi ≥ 1 mi? → hayır: 402 "Insufficient minute balance"
↓ evet
aynı kişiye devam eden arama var mı? → evet: 409
↓ hayır
santral araması başlatılır → başarısız: 502 / başarılı: 201Bakiye yetersizse yanıt:
HTTP/1.1 402 Payment Required
Content-Type: application/json
{"error":"Insufficient minute balance"}402, entegrasyonun “duracağı” ana durumdur. Bir Zap/senaryo yüksek hacimde arama tetikliyorsa ve bakiye biterse, her yeni çağrı 402 döner ve arama yapılmaz. Otomasyonuna düşük-bakiye uyarısı ekle; dakika satın alma için bkz. Dakika Paketleri.
Not: Arama başlatmak dakika bakiyesinin yanı sıra hesaba ait aktif bir asistan ve hesaba ait aktif bir telefon numarası da gerektirir. İkisi de yoksa
POST /calls404döner (aşağıdaki tabloya bak).
Hata biçimi
Tüm hatalar aynı zarfla gelir — kök alanı her zaman error olan bir JSON nesnesi:
{ "error": "invalid_api_key" }Doğrulama hatalarında (400 validation_error, zod ile denetlenen uçlar — ör. POST /hooks) ek bir details dizisi bulunur; her eleman insan-okur bir mesaj dizesidir:
{
"error": "validation_error",
"details": [
"targetUrl must start with https://",
"event must be one of the supported events"
]
}Bazı hatalar error dışında da alan taşır:
| Hata | Ek alanlar |
|---|---|
400 validation_error | details[] — insan-okur doğrulama mesajları |
400 invalid_event | validEvents[] — kabul edilen olay adları |
403 premium_required | message, feature ("api_access") |
403 kota aşımı | code: "quota_exceeded", quota: { key, limit, current, plan } |
error alanının değeri iki türdendir: bazıları makine-kodudur (invalid_api_key, validation_error, invalid_json, invalid_event, not_found, internal_error), bazıları ise doğrudan insan mesajıdır — bugün İngilizce (Insufficient minute balance, assistant_id not found, not owned by this account, or inactive), kota hatalarında ise Türkçe (kişi limitine ulaştınız (…)). Bu metinler uyarı yapılmadan değişir; entegrasyonunda dallanma yaparken HTTP durum koduna (403 için ayrıca code alanına) göre karar ver, mesaj metnine değil.
Hata kodu tablosu
| HTTP | error değeri (örnek) | Ne zaman olur | Ne yapmalı |
|---|---|---|---|
| 400 | invalid_json | İstek gövdesi geçerli JSON değil | Gövdeyi düzelt; Content-Type: application/json gönder |
| 400 | (alan mesajı) örn. phone and assistant_id are required, Invalid phone — must contain at least 10 digits | Zorunlu alan eksik/geçersiz (POST /calls, POST /contacts, POST /leads) | Eksik alanı ekle; telefon en az 10 hane |
| 400 | validation_error (+ details[]) | Zod doğrulaması başarısız (POST /hooks: event, targetUrl vb.) | details dizisini oku, ilgili alanı düzelt |
| 400 | invalid_event (+ validEvents[]) | GET /samples?event=... geçersiz olay adı | validEvents listesinden geçerli bir olay seç |
| 401 | invalid_api_key | Anahtar yok, biçimi bozuk (pk_live_ ile başlamıyor) veya eşleşmiyor | Anahtarı kontrol et; bkz. Kimlik Doğrulama |
| 402 | Insufficient minute balance | POST /calls — dakika bakiyesi 1’in altında | Dakika yükle; bkz. Dakika Paketleri |
| 403 | premium_required (+ message, feature: "api_access") | Anahtar geçerli ama plan Public API’yi kapsamıyor — her /api/v1/* çağrısında kontrol edilir | Pro veya üzeri bir plana geç |
| 403 | (kota mesajı) + code: "quota_exceeded" ve quota nesnesi | POST /contacts / POST /leads — planının kişi adedi kotası dolu. Kota yalnızca yeni kayıt açılırken bakılır; mevcut kişi döndürülüyorsa tüketilmez | Kullanılmayan kişileri sil ya da planını yükselt; yanıttaki quota.current / quota.limit alanlarına bak |
| 429 | Too many requests | Anahtar başına dakikada 60 istek aşıldı | Yanıttaki Retry-After saniyesi kadar bekle, sonra tekrar dene |
| 404 | assistant_id not found, not owned by this account, or inactive | POST /calls — asistan yok / başka hesaba ait / pasif | assistant_id doğru mu? GET /assistants ile doğrula |
| 404 | No active phone number on this account / phone_number_id not found or inactive | POST /calls — arayan numara yok/pasif | Aktif SIP numarası bağla; GET /phone-numbers |
| 404 | assistant_id / campaign_id / cadence_id not found or not owned by this account | POST /leads — gönderilen kimlik yok veya başka hesaba ait | Kimlikleri lookup uçlarından doğrula |
| 404 | not_found | DELETE /hooks/{id} — abonelik yok veya başkasına ait | Var olan bir abonelik id’si kullan (varlık sızıntısı yapılmaz) |
| 409 | A call to this contact is already in progress | POST /calls — aynı kişiye zaten “calling” durumunda arama var | Bekle; çift arama gönderme (idempotency — aşağı bak) |
| 502 | Could not start the call — check your minute balance and phone number | POST /calls — santral/makeCall katmanı aramayı başlatamadı | Geri-çekilmeli tekrar dene; sürerse durum/santralı kontrol et |
| 500 | internal_error | Beklenmeyen sunucu hatası (örn. /hooks ya da /leads işlenirken) | Geri-çekilmeli tekrar dene; sürerse destek |
404 iki farklı anlama gelir. Kaynak arama uçlarında (GET /contacts) “bulunamadı” 404 değildir — Zapier/Make uyumu için boş dizi [] döner. 404 yalnızca sahiplik/varlık kapılarında (POST /calls asistan/numara, POST /leads asistan/kampanya/kadans, DELETE /hooks/{id}) görülür.
İyi uygulamalar — retry ve idempotency
Hız limitinin bol olması (anahtar başına dakikada 60 istek) “istediğin kadar dene” demek değildir. Sağlam bir entegrasyon şu kurallara uyar:
- Duruma göre yeniden dene, geri-çekilerek.
502ve500geçici olabilir → üstel geri-çekilme (ör. 15s taban) ile birkaç kez dene.429için yanıttakiRetry-Afterkadar bekle.400,401,402,403,404,409için tekrar deneme anlamsızdır — istek/hesap durumunu düzeltmeden aynı sonucu alırsın. POST /contactsvePOST /callstelefon-bazlı idempotenttir. Kişi oluşturma, telefonun tam ya da son-10-hane eşleşmesine bakar: aynı numarayı iki kez gönderirsen yeni kayıt oluşmaz, var olan kişi döner (POST /contacts→existing:true, HTTP200). Yani ağ hatasında güvenle tekrar edebilirsin.- Çift aramayı
409ile ele al.POST /calls, aynı kişiye devam eden bir arama varken ikinci isteği409ile reddeder. Bunu bir hata değil, “zaten sırada” olarak yorumla; tekrar tetikleme. 402’yi bir dur-noktası olarak modelle. Bakiye biterse tüm aramalar durur. Otomasyonuna düşük-bakiye eşiği ekle ve bittiğinde tetiklemeyi durdur.- HTTP koduna göre dallan, mesaja göre değil.
errormetinleri uyarı yapılmadan değişir — v1 mesajları bir kez toptan Türkçe’den İngilizce’ye çevrildi ve mesaja göre dallanan entegrasyonlar sessizce kırıldı. Durum kodları (403’te ayrıcacodealanı) sözleşmedir; metin değildir.
KVKK. API üzerinden başlattığın her arama, aradığın kişinin gerçek telefonuna çıkar. Otomasyonun yalnızca açık rıza/aydınlatma kapsamındaki numaraları aramalı; asistanın açılışında kim olduğunu ve neden aradığını net söylemeli. Rıza dışı toplu arama tetiklemek yasal risktir. Bkz. KVKK ve Veri Yönetimi.