# Hatalar

> HTTP durum kodları, makinece okunur hata kodları ve hatalara eşlik eden ek alanlar.

Başarısız bir istekte yanıt gövdesi şu biçimdedir:

```json
{
  "ok": false,
  "error": "similar",
  "message": "Benzer adlı firma var — mevcut kaydı seçin ya da confirm_new=1 ile yeni açın.",
  "similar": [ { "id": 7, "name": "Alize Paketleme San. ve Tic. A.Ş.", "…": "…" } ]
}
```

- **`error`** makinece okunur, değişmez bir koddur — kodunuzda buna göre dallanın.
- **`message`** kullanıcıya gösterilebilecek Türkçe açıklamadır; metni değişebilir, ayrıştırmayın.
- Bazı hatalar ek alan taşır: `similar`, `customer`, `gate_problems`, `module`, `state`, `otp`.

## Hata kodları

| Kod | HTTP | Anlamı |
|---|---|---|
| `unauthorized` | 401 | Belirteç yok, geçersiz, süresi dolmuş ya da iptal edilmiş. |
| `credentials` | 401 | Kullanıcı adı veya şifre hatalı. |
| `otp_required` | 401 | İki adımlı doğrulama kodu gerekli (`otp`). |
| `otp_invalid` | 401 | Doğrulama kodu hatalı ya da süresi geçmiş. |
| `forbidden` | 403 | Kaydı görme / düzenleme yetkiniz yok. |
| `insufficient_scope` | 403 | OAuth belirtecinde `crm.write` yok. |
| `module` | 403 | Modül lisansta ya da kullanıcının izinlerinde kapalı. |
| `license` | 403 | Lisans süresi doldu — yalnız yönetici erişebilir. |
| `ip` | 403 | Bu ağdan erişim izni yok (IP kısıtı). |
| `not_found` | 404 | Kayıt bulunamadı. |
| `invalid` | 400 | Eksik ya da geçersiz alan (`message` ayrıntıyı verir). |
| `send` | 400 | E-posta gönderilemedi (hesap bağlı değil, alıcı geçersiz…). |
| `exists` | 409 | Bu adla firma var (`customer` alanında döner). |
| `similar` | 409 | Benzer adlı firmalar var (`similar`); `confirm_new: true` ile yine açılır. |
| `gate` | 409 | Aşama kapısı sağlanmadı (`gate_problems`). |
| `locked` | 429 | Çok fazla hatalı giriş — 60 sn bekleyin. |
| `rate_limited` | 429 | Dakikalık istek sınırı aşıldı (`Retry-After`). |
| `limit` | 429 | Kullanıcı başına saatlik soru sınırı (40) doldu. |

## HTTP durum kodları

| Kod | Anlamı |
|---|---|
| `200` | Başarılı. |
| `201` | Oluşturuldu (OAuth istemci kaydı, SCIM kullanıcı açma). |
| `204` | İçeriksiz başarı (SCIM kullanıcı kapatma). |
| `400` | Eksik / geçersiz alan. |
| `401` | Kimlik doğrulanamadı — belirteci yenileyin ya da yeniden giriş yapın. |
| `403` | Yetki yok — rol, modül, kapsam, lisans ya da IP. |
| `404` | Kayıt yok ya da görme yetkiniz dışında. |
| `405` | Yöntem desteklenmiyor (ör. MCP ucuna `GET`). |
| `409` | İş kuralı çakışması — mükerrer firma, aşama kapısı. |
| `429` | Hız sınırı — `Retry-After` kadar bekleyin. |
| `500` | Sunucu hatası. Tekrarlanıyorsa isteği ve zamanı `info@solk.app`'e iletin. |

## Örnek: mükerrer firma

```python
r = s.post(f"{BASE}/api/v1/customers", json={"name": "Alize Ambalaj"})
j = r.json()
if r.status_code == 409 and j["error"] == "exists":
    firma = j["customer"]                       # zaten var — onu kullan
elif r.status_code == 409 and j["error"] == "similar":
    adaylar = j["similar"]                      # kullanıcıya seçtir ya da:
    j = s.post(f"{BASE}/api/v1/customers", json={"name": "Alize Ambalaj", "confirm_new": True}).json()
```

## Örnek: aşama kapısı

```python
r = s.post(f"{BASE}/api/v1/opportunities/{oid}/stage", json={"stage": "Viable"})
if r.status_code == 409 and r.json()["error"] == "gate":
    for sorun in r.json()["gate_problems"]:
        print("Eksik:", sorun)
```