# Tatvan Belediyesi — Kurumsal Web Sitesi API'si (v1)

Bu doküman, Tatvan Belediyesi kurumsal web sitesinin ALO 153 Destek Merkezi ile
entegrasyonu için hazırlanmıştır. Vatandaş; yeni talep oluşturur, kendi talebini
sorgular, aşamasını görür, iletişim bilgilerini düzeltir, yanıt yazar, çözüldü
olarak işaretler veya kaydını kapatır.

| | |
|---|---|
| **Base URL** | `https://153.vegabyte.com.tr/api/web/v1` |
| **Biçim** | JSON (istek ve yanıt) |
| **Kimlik doğrulama** | `X-API-Key` başlığı |
| **Karakter kodlaması** | UTF-8 |
| **Çevrimiçi sürüm** | https://153.vegabyte.com.tr/api-dokumantasyon |

Bu dokümanın her zaman güncel hâli yukarıdaki adreste yayımlanır; giriş gerektirmez.
Elinizdeki kopya eskimiş olabilir, kuşkuya düştüğünüzde çevrimiçi sürümü esas alın.

---

## 1. Kimlik Doğrulama

Her istek (yalnızca `/health` hariç) geçerli bir API anahtarı gerektirir.
Anahtar, yönetim panelinden **Yönetim → API Anahtarları** ekranından üretilir.

```http
X-API-Key: tvb_xxxxxxxxxxxx.yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
```

### Kritik güvenlik kuralları

- Anahtar **yalnızca sunucu tarafında** saklanmalıdır. Tarayıcıya inen
  JavaScript'e, mobil pakete veya herkese açık depoya **konulmamalıdır**.
  Web sitesi, tarayıcıdan gelen isteği kendi sunucusunda karşılayıp API'ye
  sunucu-sunucu (server-to-server) iletmelidir.
- Anahtar yalnızca üretildiği anda bir kez gösterilir; sistemde geri
  çevrilemez biçimde (özet olarak) saklanır. Kaybedilirse yenisi üretilir.
- Anahtar sızarsa panelden **anında devre dışı** bırakılabilir.
- Anahtar URL'de (query string) **gönderilmemelidir**; yalnızca başlıkta.

### Hata yanıtları (kimlik doğrulama)

| Durum | Anlamı |
|---|---|
| `401` | Anahtar eksik veya geçersiz |
| `403` | Anahtar devre dışı bırakılmış |

---

## 2. Hız Sınırları (Rate Limit)

| Uç grubu | Sınır |
|---|---|
| Yeni talep (`POST /tickets`) | Ziyaretçi başına dakikada **3** istek |
| Sorgulama (`lookup`, `tickets/{token}`, `neighborhoods`) | Ziyaretçi başına dakikada **30** istek |
| İşlem (`update`, `reply`, `resolve`, `close`) | Ziyaretçi başına dakikada **10** istek |
| Başarısız eşleşme (yanlış talep no/telefon) | Ziyaretçi başına **10** başarısız denemeden sonra 5 dk kilit |

Sınır aşıldığında `429 Too Many Requests` döner. Web sitesi bu durumu
kullanıcıya "Çok fazla deneme yaptınız, lütfen biraz sonra tekrar deneyin"
şeklinde göstermelidir.

### 2.1 `X-Client-IP` — ziyaretçiyi bildirin (önemli)

İstekler web sitesinin **sunucusundan** geldiği için, API tüm ziyaretçileri
varsayılan olarak tek bir IP gibi görür. Bu durumda bir kişinin denemeleri
sitenin tüm ziyaretçilerini kilitler.

Bunu önlemek için her istekte son kullanıcının IP adresini iletin:

```http
X-Client-IP: 88.240.15.72
```

Sınırlar o ziyaretçiye özel uygulanır; bir ziyaretçinin kilitlenmesi
diğerlerini etkilemez. Başlık gönderilmezse tüm trafik ortak kotayı paylaşır.

> Bu başlık yalnızca anahtar sahibi güvenilen istemciden kabul edilir. Değeri
> son kullanıcının **gerçek** IP'si olmalıdır (`$_SERVER['REMOTE_ADDR']` veya
> proxy arkasındaysanız doğruladığınız `X-Forwarded-For` değeri).

---

## 3. Sahiplik Doğrulaması

Vatandaşın yalnızca **kendi** talebine erişebilmesi için her istek sahiplik
kanıtı taşır. İki yöntemden biri kullanılır:

1. **Takip token'ı** — `track_token`: talep oluşturulurken üretilen, tahmin
   edilemez benzersiz değer (vatandaşa SMS/e-posta ile giden takip bağlantısında yer alır).
2. **Talep numarası + telefon** — `ticket_number` + `phone`: ikisi birlikte
   eşleşmelidir.

Kanıt eşleşmezse `404` döner; talebin var olup olmadığı dahi sızdırılmaz.

> **Not:** Telefon numarası içeren tüm sorgular `POST` ile yapılır. Numara URL'de
> taşınsaydı sunucu/proxy erişim loglarına düşerdi (KVKK riski).

> **Yalnızca telefonla sorgulama yoktur.** Telefon numarası gizli bir bilgi
> değildir; tek başına yeterli sayılsaydı numarayı bilen herkes o kişinin tüm
> taleplerini, adresini ve yazışmalarını görebilirdi. Bu nedenle sorgulama daima
> **talep numarası + telefon** ya da **takip token'ı** ile yapılır.

---

## 4. Uçlar (Endpoints)

### 4.1 Servis Durumu

```http
GET /health
```

Anahtar gerektirmez. İzleme (uptime) kontrolü için kullanılır.

```json
{
  "status": "ok",
  "service": "tatvan-web-api",
  "time": "2026-07-29T21:21:14+03:00"
}
```

---

### 4.2 Mahalle Listesi (form için)

```http
GET /neighborhoods
X-API-Key: ...
```

Talep formundaki mahalle açılır listesini doldurmak içindir. Yalnızca aktif ve
**herkese açık** mahalleler döner; çağrı merkezine özel kayıtlar (örn.
"Adres Belirtilmedi") dışarı verilmez.

**Yanıt: `200 OK`**

```json
{
  "data": [
    { "id": 1, "name": "Atatürk Mahallesi" },
    { "id": 2, "name": "Sahil Mahallesi" }
  ]
}
```

> Liste nadiren değişir; sitenizde **günlük önbelleğe** almanız önerilir.

---

### 4.3 Yeni Talep Oluşturma

```http
POST /tickets
X-API-Key: ...
X-Client-IP: 88.240.15.72
Content-Type: application/json
```

```json
{
  "citizen_name": "Ahmet Yılmaz",
  "citizen_phone": "0532 999 88 77",
  "citizen_email": "ahmet@example.com",
  "neighborhood_id": 2,
  "address": "Sahil Mah. 123. Sok. No:4",
  "subject": "Sokak lambası yanmıyor",
  "description": "Sokağımızdaki aydınlatma direği üç gündür yanmıyor.",
  "latitude": 38.5012,
  "longitude": 42.2795,
  "kvkk_consent": true
}
```

| Alan | Zorunlu | Kural |
|---|---|---|
| `citizen_name` | ✔ | 3–120 karakter |
| `citizen_phone` | ✔ | 10–20 karakter; esnek biçim kabul edilir |
| `citizen_email` | — | Geçerli e-posta, en fazla 150 karakter |
| `neighborhood_id` | — | `GET /neighborhoods` listesinden bir kimlik |
| `address` | — | En fazla 255 karakter |
| `subject` | ✔ | 5–160 karakter |
| `description` | ✔ | 10–4000 karakter |
| `latitude` / `longitude` | — | Sayısal koordinat |
| `kvkk_consent` | ✔ | `true` olmalıdır |
| `photos[]` | — | En fazla **5** dosya, her biri en fazla **8 MB** |

> **Kategori (konu başlığı) gönderilmez.** Talebi hangi kategoriye ve müdürlüğe
> yönlendireceğine çağrı merkezi temsilcisi karar verir. Vatandaş yalnızca
> konuyu serbest metin olarak yazar.

#### Fotoğraf/belge eki ile gönderim

Ek göndereceksiniz `multipart/form-data` kullanın (JSON yerine). İzin verilen
türler: `jpg`, `jpeg`, `png`, `webp`, `heic`, `pdf`, `doc`, `docx`.

```bash
curl -X POST https://153.vegabyte.com.tr/api/web/v1/tickets \
  -H "X-API-Key: $ALO153_API_KEY" \
  -H "X-Client-IP: 88.240.15.72" \
  -F "citizen_name=Ahmet Yılmaz" \
  -F "citizen_phone=05329998877" \
  -F "subject=Sokak lambası yanmıyor" \
  -F "description=Aydınlatma direği üç gündür yanmıyor." \
  -F "kvkk_consent=1" \
  -F "photos[]=@lamba.jpg" \
  -F "photos[]=@sokak.jpg"
```

**Yanıt: `201 Created`** — [Talep Detay Nesnesi](#5-talep-detay-nesnesi)

Yanıttaki `ticket_number` ve `track_token` değerlerini kullanıcıya gösterin;
`track_token` takip ekranına yönlendirme için kullanılır. Vatandaşa ayrıca
sistem tarafından bilgilendirme SMS'i/e-postası gönderilir.

**Hatalar:**

| Durum | Anlamı |
|---|---|
| `422` | Doğrulama hatası (eksik alan, KVKK onayı yok, geçersiz dosya türü/boyutu) |
| `429` | Dakikada 3 talep sınırı aşıldı |

---

### 4.4 Takip Token'ı ile Talep Detayı

```http
GET /tickets/{track_token}
X-API-Key: ...
```

Vatandaşa gönderilen takip bağlantısındaki token ile talebin tüm detayını döner.

**Yanıt: `200 OK`** — bkz. [Talep Detay Nesnesi](#5-talep-detay-nesnesi)

**Hatalar:** `404` (talep bulunamadı)

---

### 4.5 Talep No + Telefon ile Sorgulama

```http
POST /tickets/lookup
X-API-Key: ...
Content-Type: application/json

{
  "ticket_number": "153-2026-000004",
  "phone": "0532 999 88 77"
}
```

Telefon numarası esnek biçimde kabul edilir: `05329998877`, `0532 999 88 77`,
`+90 532 999 88 77`, `905329998877` — hepsi aynı numaraya normalize edilir.

**Yanıt: `200 OK`** — [Talep Detay Nesnesi](#5-talep-detay-nesnesi)

**Hatalar:**

| Durum | Anlamı |
|---|---|
| `404` | Talep numarası ve telefon eşleşmedi |
| `422` | Geçersiz telefon formatı |
| `429` | Çok fazla başarısız deneme |

---

### 4.6 Bilgi Düzeltme (Düzenleme)

```http
POST /tickets/update
X-API-Key: ...
Content-Type: application/json

{
  "track_token": "5b7d4c7e-...",
  "citizen_name": "Ahmet Yılmaz",
  "citizen_email": "yeni@example.com",
  "address": "Sahil Mah. 123. Sok. No:6",
  "neighborhood_id": 3
}
```

Vatandaşın, talebini oluştururken hatalı/eksik girdiği **iletişim ve konum**
bilgilerini düzeltmesi içindir. Sahiplik kanıtı olarak `track_token` yerine
`ticket_number` + `phone` de gönderilebilir.

#### Değiştirilebilen alanlar

| Alan | Kural |
|---|---|
| `citizen_name` | 3–120 karakter |
| `citizen_email` | Geçerli e-posta veya `null` |
| `address` | En fazla 255 karakter veya `null` |
| `neighborhood_id` | `GET /neighborhoods` listesinden bir kimlik veya `null` |

Yalnızca gönderdiğiniz alanlar değerlendirilir (kısmi güncelleme); göndermediğiniz
alanlar olduğu gibi kalır.

#### Değiştirilemeyen alanlar

- **Telefon numarası** — sahiplik kanıtıdır; değiştirilmesi talebin başkasına
  devredilmesi anlamına gelirdi. İstekte gönderilse bile yok sayılır.
- **Konu ve açıklama** — personelin üzerinde çalıştığı içeriktir. Ek bilgi için
  [yanıt yazma](#47-talebe-yanıt-yazma) ucunu kullanın.

#### Süre kısıtı

Düzeltme yalnızca talep oluşturulduktan sonraki **ilk 24 saat** içinde ve talep
**açıkken** yapılabilir. Süre dolduktan sonra `409` döner; bu durumda vatandaş
yanıt yazarak düzeltmeyi iletebilir.

**Yanıt: `200 OK`**

```json
{
  "message": "Bilgileriniz güncellendi.",
  "updated": [
    "Ad Soyad: \"Ahmet Yilmaz\" → \"Ahmet Yılmaz\"",
    "Adres: \"Sahil Mah. 123. Sok. No:4\" → \"Sahil Mah. 123. Sok. No:6\""
  ],
  "data": { }
}
```

`updated` dizisi uygulanan değişikliklerin okunabilir özetidir; kullanıcıya
onay mesajı olarak gösterilebilir. Aynı değişiklik panelde personele **iç not**
olarak da işlenir, böylece kayıt geçmişi izlenebilir kalır.

**Hatalar:**

| Durum | Anlamı |
|---|---|
| `409` | 24 saatlik düzeltme süresi doldu veya talep kapatılmış |
| `422` | Doğrulama hatası ya da hiçbir alanda değişiklik yok |
| `404` | Sahiplik eşleşmedi |

---

### 4.7 Talebe Yanıt Yazma

```http
POST /tickets/reply
X-API-Key: ...
Content-Type: application/json

{
  "track_token": "5b7d4c7e-...",
  "message": "Sorun devam ediyor, tekrar bakılabilir mi?"
}
```

Sahiplik kanıtı olarak `track_token` yerine `ticket_number` + `phone` de
gönderilebilir. `message`: 2–1000 karakter.

**Yanıt: `201 Created`**

```json
{
  "message": "Yanıtınız talebinize eklendi.",
  "data": {
    "id": 12,
    "body": "Sorun devam ediyor, tekrar bakılabilir mi?",
    "author_type": "citizen",
    "created_at": "2026-07-29T21:22:07+03:00"
  }
}
```

**Hatalar:** `409` (talep kapalı), `404`, `422`

---

### 4.8 Çözüldü Olarak İşaretleme

```http
POST /tickets/resolve
X-API-Key: ...
Content-Type: application/json

{
  "track_token": "5b7d4c7e-...",
  "note": "Sorun giderildi, teşekkürler."
}
```

`note` isteğe bağlıdır (en fazla 1000 karakter).

**Yanıt: `200 OK`**

```json
{
  "message": "Talebiniz çözüldü olarak işaretlendi.",
  "data": {
    "ticket_number": "153-2026-000004",
    "status": { "value": "resolved", "label": "Çözüldü", "order": 3, "color": "teal" },
    "resolved_at": "2026-07-29T21:23:07+03:00"
  }
}
```

**Hatalar:** `409` (zaten çözülmüş), `404`

---

### 4.9 Kaydı Kapatma

```http
POST /tickets/close
X-API-Key: ...
Content-Type: application/json

{
  "track_token": "5b7d4c7e-...",
  "reason": "Talebimden vazgeçtim."
}
```

Vatandaşın talebinden vazgeçmesi / kaydı sonlandırması içindir.
`reason` isteğe bağlıdır (en fazla 500 karakter).

> **Önemli:** Kayıt **silinmez.** Yönetim panelinde ve raporlarda görünmeye
> devam eder; yalnızca "Çözüldü" durumuna alınır ve geçmişine
> "Vatandaş kaydı kapattı" notu işlenir. Kurumsal arşiv bütünlüğü korunur.

**Yanıt: `200 OK`**

```json
{
  "message": "Kaydınız kapatıldı. Kurum arşivinde saklanmaya devam eder.",
  "data": {
    "ticket_number": "153-2026-000004",
    "status": { "value": "resolved", "label": "Çözüldü", "order": 3, "color": "teal" },
    "closed_at": "2026-07-29T21:23:07+03:00"
  }
}
```

**Hatalar:** `409` (zaten kapalı), `404`

---

## 5. Talep Detay Nesnesi

```json
{
  "data": {
    "ticket_number": "153-2026-000004",
    "track_token": "5b7d4c7e-...",
    "subject": "API test talebi",
    "status": { "value": "resolved", "label": "Çözüldü", "order": 3, "color": "teal" },
    "is_open": false,
    "category": "Alt Yapı / Yol Şikayeti",
    "neighborhood": "Sahil Mahallesi",
    "department": "Fen İşleri Müdürlüğü",
    "is_emergency": false,
    "created_at": "2026-07-29T21:22:40+03:00",
    "resolved_at": "2026-07-29T21:23:07+03:00",
    "description": "Talep açıklaması...",
    "citizen": {
      "name": "Test Vatandaş",
      "phone": "05329998877",
      "email": null,
      "address": "Test Adres"
    },
    "location": { "latitude": null, "longitude": null },
    "attachments_count": 0,
    "stages": [
      { "key": "received",  "label": "Talep Alındı",   "order": 0, "state": "completed", "completed": true,  "active": false, "reached_at": "2026-07-29T21:22:40+03:00" },
      { "key": "forwarded", "label": "Birime İletildi", "order": 1, "state": "completed", "completed": true,  "active": false, "reached_at": null },
      { "key": "in_review", "label": "İnceleniyor",     "order": 2, "state": "completed", "completed": true,  "active": false, "reached_at": null },
      { "key": "resolved",  "label": "Çözüldü",         "order": 3, "state": "completed", "completed": true,  "active": false, "reached_at": "2026-07-29T21:23:07+03:00" }
    ],
    "replies": [
      {
        "id": 4,
        "body": "Talebiniz ilgili müdürlüğe iletilmiştir.",
        "author_type": "staff",
        "author_name": "Merve K.",
        "created_at": "2026-07-29T21:23:31+03:00"
      }
    ],
    "can": { "reply": false, "resolve": false, "close": false }
  }
}
```

### Alan açıklamaları

| Alan | Açıklama |
|---|---|
| `status.value` | `received`, `forwarded`, `in_review`, `resolved` |
| `status.order` | 0–3 arası sıra numarası |
| `status.color` | Öneri renk anahtarı: `sky`, `blue`, `amber`, `teal` |
| `is_open` | Talep hâlâ açık mı |
| `citizen` | Vatandaşın kendi bilgileri (tam) |
| `replies[].author_type` | `citizen` (vatandaş) veya `staff` (belediye personeli) |
| `replies[].author_name` | Personel için **soyadı maskeli** (örn. `Merve K.`); vatandaş yanıtlarında `null` |
| `attachments_count` | Talebe eklenen dosya sayısı (yalnızca sayı) |
| `can` | Bu talepte hangi işlemlerin yapılabileceği — butonları buna göre gösterin |

> **Ek dosyalar indirilemez.** `attachments_count` yalnızca kaç dosya
> eklendiğini bildirir; dosyaların kendisi web API'si üzerinden **sunulmaz**.
> Vatandaşın yüklediği belgeler yalnızca yetkili personelin panelinde
> görüntülenir. Sitenizde "3 dosya eklediniz" biçiminde bilgi gösterebilirsiniz.

### Aşama (stage) durumları

| `state` | Anlamı | Önerilen gösterim |
|---|---|---|
| `completed` | Bu aşama tamamlandı | Dolu / işaretli |
| `active` | Talep şu an bu aşamada | Vurgulu / animasyonlu |
| `pending` | Henüz ulaşılmadı | Soluk |

`reached_at` alanı, aşamaya ulaşıldığı an (ISO 8601). `null` ise o aşama için
ayrı bir kayıt tutulmamıştır (örn. doğrudan çözüme geçen talepler).

### Gizlilik notları

- Belediye personelinin **soyadı maskelenir** (`Merve K.`); tam ad dışarı verilmez.
- Personelin **iç notları** (dahili yazışmalar) API'de **hiçbir koşulda dönmez**.
- Vatandaşın kendi ad/telefon/adres bilgisi tam döner (kendi kaydını sorgulamaktadır).

---

## 6. Hata Biçimi

Doğrulama hataları Laravel standardındadır:

```json
{
  "message": "Geçerli bir cep telefonu numarası giriniz (örn. 0532 111 22 33).",
  "errors": {
    "phone": ["Geçerli bir cep telefonu numarası giriniz (örn. 0532 111 22 33)."]
  }
}
```

Diğer hatalarda yalnızca `message` döner.

| Kod | Anlamı |
|---|---|
| `200` | Başarılı |
| `201` | Oluşturuldu (yeni talep, yanıt ekleme) |
| `401` | API anahtarı eksik/geçersiz |
| `403` | API anahtarı devre dışı |
| `404` | Talep bulunamadı veya sahiplik eşleşmedi |
| `409` | İşlem talebin mevcut durumuyla çelişiyor (örn. kapalı talebe yanıt) |
| `422` | Doğrulama hatası (eksik/geçersiz alan) |
| `429` | Hız sınırı aşıldı |

---

## 7. Örnek Entegrasyon

### PHP (sunucu tarafı)

```php
$response = Http::withHeaders([
        'X-API-Key' => env('ALO153_API_KEY'),
        'Accept'    => 'application/json',
    ])
    ->post('https://153.vegabyte.com.tr/api/web/v1/tickets/lookup', [
        'ticket_number' => $request->input('ticket_number'),
        'phone'         => $request->input('phone'),
    ]);

if ($response->status() === 404) {
    return back()->withErrors('Talep numarası ve telefon eşleşmedi.');
}

$ticket = $response->json('data');
```

### Node.js (sunucu tarafı)

```js
const res = await fetch('https://153.vegabyte.com.tr/api/web/v1/tickets/lookup', {
  method: 'POST',
  headers: {
    'X-API-Key': process.env.ALO153_API_KEY,
    'Content-Type': 'application/json',
    Accept: 'application/json',
  },
  body: JSON.stringify({ ticket_number: ticketNumber, phone }),
});

if (res.status === 404) throw new Error('Talep bulunamadı.');
const { data } = await res.json();
```

### cURL

```bash
curl -X POST https://153.vegabyte.com.tr/api/web/v1/tickets/lookup \
  -H "X-API-Key: $ALO153_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"ticket_number":"153-2026-000004","phone":"05329998877"}'
```

---

## 8. Web Sitesi İçin Öneriler

1. **Anahtarı gizleyin.** Tarayıcıdan gelen formu kendi sunucunuzda karşılayın,
   API'ye oradan gidin. Anahtar hiçbir zaman istemciye inmemelidir.
2. **`X-Client-IP` gönderin.** Aksi halde tüm ziyaretçileriniz aynı hız sınırı
   kotasını paylaşır ve bir kişi hepsini kilitleyebilir (bkz. bölüm 2.1).
3. **Formda kendi bot korumanızı kullanın.** Yeni talep ucu dakikada 3 istekle
   sınırlıdır; buna ek olarak sitenizde captcha/honeypot bulundurun.
4. **Mahalle listesini önbelleğe alın.** `GET /neighborhoods` günde bir kez
   çekilmesi yeterlidir.
5. **Aşama çubuğunu `stages` dizisiyle çizin.** `state` alanı doğrudan
   tamamlandı/aktif/bekliyor gösterimine karşılık gelir.
6. **Butonları `can` nesnesine göre gösterin.** Kapalı taleplerde yanıt/çözüldü/kapat
   butonları gizlenmelidir; aksi halde `409` alırsınız.
7. **Düzenleme butonunu süreye göre gösterin.** `created_at` üzerinden 24 saat
   geçmişse "Bilgilerimi düzelt" seçeneğini gizleyin.
8. **KVKK uyarısı gösterin.** Hem talep formunda hem sorgulama ekranında kişisel
   veri işlendiğine dair bilgilendirme ve açık onay kutusu bulunmalıdır.
9. **Sorgulama formunda iki alanı da isteyin.** Yalnızca telefonla sorgulama
   bilinçli olarak desteklenmez (bkz. bölüm 3). Formunuzda hem talep numarası
   hem telefon alanı zorunlu olmalıdır.
10. **Takip bağlantısını kullanıcıya hatırlatın.** Talep oluşturulduğunda dönen
    `track_token` ile hazırlanan bağlantı, kullanıcının talep numarasını
    hatırlamadan doğrudan takip ekranına girmesini sağlar.

---

## 9. Çalışan Örnek Entegrasyon

Bu dokümandaki tüm uçları kullanan, çalışır durumda bir referans uygulama
hazırlanmıştır. Kendi entegrasyonunuzu yazmadan önce incelemeniz önerilir.

| | |
|---|---|
| **Adres** | `https://153.vegabyte.com.tr/ornek/` |
| **Erişim parolası** | Sistem yöneticisinden talep edilir |

Örnek uygulama düz PHP ile yazılmıştır ve şunları gösterir:

- Anahtarın **sunucu tarafında** tutulması ve isteklerin sunucu-sunucu iletilmesi
  (`lib/Alo153Client.php`)
- `X-Client-IP` başlığının doğru gönderimi
- Fotoğraf ekli talep oluşturma (`multipart/form-data`)
- Talep numarası + telefon ile sorgulama ve takip token'ı ile detay
- Aşama çubuğunun `stages` dizisiyle çizilmesi
- Yanıt yazma, bilgi düzeltme, çözüldü işaretleme ve kayıt kapatma
- Her ekranda yapılan API çağrısının istek/yanıt/süre dökümü (teknik günlük)

> Örnek site canlı API'ye bağlıdır; burada oluşturduğunuz talepler **gerçek
> kayıt** olarak sisteme düşer ve vatandaşa SMS gider. Test ederken bunu göz
> önünde bulundurun.

---

## 10. Sürüm ve Destek

- Sürüm: **v1** (`/api/web/v1`)
- Geriye dönük uyumsuz değişiklikler yeni sürüm yolu (`/api/web/v2`) ile yayınlanır.
- Yeni anahtar talebi, anahtar iptali ve teknik destek: yönetim paneli
  **Yönetim → API Anahtarları** ekranı / sistem yöneticisi.

### Değişiklik geçmişi

| Tarih | Değişiklik |
|---|---|
| 30.07.2026 | **Telefonla listeleme ucu (`POST /tickets/by-phone`) kaldırıldı.** Sorgulama artık yalnızca talep numarası + telefon veya takip token'ı ile yapılır (bkz. bölüm 3). |
| 30.07.2026 | Tüm uçlara güvenlik sertleştirmesi uygulandı; hız sınırları ziyaretçi bazında işler hâle getirildi (`X-Client-IP`). |
| 29.07.2026 | Talep oluşturma (`POST /tickets`), bilgi düzeltme (`POST /tickets/update`) ve mahalle listesi (`GET /neighborhoods`) uçları eklendi. |
