{"openapi":"3.0.3","info":{"title":"ÇözümDevi WhatsApp API","version":"2.0.0","description":"Kendi sisteminizden **WhatsApp** üzerinden müşterilerinize bildirim gönderin (kargo/sipariş vb.).\n\n**Tasarım:** Kaynak-yönelimli, düz JSON. Kimlikler opak prefixli dizgelerdir (`msg_`, `ctc_`, `snd_`, `tpl_`) — ham veritabanı kimliği yoktur. Başarı HTTP durum koduyla, hatalar tek tip `{ \"error\": { ... } }` zarfıyla döner.\n\n**⚠️ Kimlikler CASE-SENSITIVE'dir:** base62 kodlamadır (`0-9A-Za-z`), yani `msg_FkR` ≠ `msg_Fkr`. Depolama katmanınızda **büyük/küçük harf duyarlı** karşılaştırma kullanın (ör. SQL Server'da `BIN2`/`_CS_AS` collation) — aksi halde farklı id'ler çakışır (satır çakışması).\n\n**Kimlik doğrulama:** OAuth2 `client_credentials`. Panelden bir uygulama oluşturup `client_id` + `client_secret` alın; `POST /oauth/token` ile kısa ömürlü **access token** üretip diğer çağrılarda `Authorization: Bearer <access_token>` kullanın.\n\n**Tek yönlü:** Siz gönderirsiniz; müşteri cevap verirse cevap ÇözümDevi panelinizde görünür.\n**Şablon-öncelikli:** 24 saatlik pencere dışında yalnız onaylı WhatsApp şablonu gönderilebilir.\n\n**Hız limiti:** Uygulama başına (token/anahtar başına), SABİT saniye-penceresi — okuma **5/sn**, gönderim **3/sn**, token ucu **2/sn** (client_id/IP başına). Aşımda `429 rate_limit_error` + `Retry-After`. Ayrıca kanal başına günlük gönderim kotası (`quota_exceeded`) geçerlidir. **Her yanıtta** hem `RateLimit-Limit/Remaining/Reset` (IETF; Reset=kalan saniye) hem `X-RateLimit-Limit/Remaining/Reset` (X-Reset=Unix epoch) döner → adapter akıllı backoff yapabilir.\n\nHer yanıtta `Request-Id` header'ı bulunur; destek talebinde paylaşın."},"servers":[{"url":"https://api.cozumdevi.com/api/v2/wa","description":"Canlı"}],"security":[{"OAuth2":[]}],"tags":[{"name":"OAuth","description":"Access token üretimi (client_credentials)"},{"name":"Messages","description":"Bildirim gönderimi"},{"name":"Senders","description":"Gönderen numaralar & şablonlar"},{"name":"Contacts","description":"Kişi & gönderim durumu"},{"name":"Inbox","description":"Gelen konuşmalar & mesaj geçmişi (okuma)"}],"paths":{"/oauth/token":{"post":{"tags":["OAuth"],"summary":"Access token al (client_credentials)","description":"Uygulamanızın **client_id + client_secret** bilgisiyle kısa ömürlü bir **access token** üretir. Kimlik body'de (`client_id`/`client_secret`) veya `Authorization: Basic base64(client_id:client_secret)` ile verilebilir. Bu uç PUBLIC'tir (token gerektirmez).\n\n**Token davranışı (adapter yazanlar için — ÖNEMLİ):**\n- Token **stateless JWT**'dir; **iptal listesi/depolama YOKTUR**. Yeni token almak eskiyi **GEÇERSİZ KILMAZ** — aynı anda **birden fazla token** yaşayabilir (hepsi kendi süresi dolana dek geçerli). Bu yüzden yenilerken race/401-zinciri OLMAZ; eski tokenı beklemeden bırakabilirsiniz.\n- **TTL:** `expires_in` = **3600 sn (1 saat)**, sabit (env `WA_APP_TOKEN_TTL` ile ayarlanabilir).\n- **Süresi dolmuş / geçersiz token** → API çağrılarında **`401`** `{ error.type:\"authentication_error\", code:\"unauthorized\" }`. (403 yalnız scope eksikliğinde: `insufficient_scope`.)\n- Uygulama iptal edilirse OAuth `/token` **`401 invalid_client`** verir; ama önceden verilmiş bir token **süresi dolana dek (≤1 sa)** çalışmaya devam edebilir (stateless).","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["grant_type","client_id","client_secret"],"properties":{"grant_type":{"type":"string","enum":["client_credentials"],"example":"client_credentials"},"client_id":{"type":"string","example":"cid_7d5d2004dcffeee5315213f8ad983355"},"client_secret":{"type":"string","example":"csec_b926f9efad92a5f45f18a11125a16a76…"}}},"example":{"grant_type":"client_credentials","client_id":"cid_7d5d…","client_secret":"csec_b926…"}}}},"responses":{"200":{"description":"Access token","content":{"application/json":{"schema":{"type":"object","properties":{"access_token":{"type":"string"},"token_type":{"type":"string","example":"Bearer"},"expires_in":{"type":"integer","example":3600},"scope":{"type":"string","example":"messaging:read messaging:send"}}},"example":{"access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…","token_type":"Bearer","expires_in":3600,"scope":"messaging:read messaging:send"}}}},"400":{"description":"Geçersiz istek","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthError"},"example":{"error":"unsupported_grant_type","error_description":"grant_type 'client_credentials' olmalı.","request_id":"req_8Fk2Qp"}}}},"401":{"description":"Geçersiz istemci","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthError"},"example":{"error":"invalid_client","error_description":"client_id veya client_secret hatalı.","request_id":"req_8Fk2Qp"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/messages":{"get":{"tags":["Inbox"],"summary":"Mesaj akışı (kaldığın yerden)","description":"Kiracının **tüm** mesajları tek akışta, **kesin ve değişmez sırayla**. `/conversations` → her kişi için\n`/contacts/{id}/messages` → istemci tarafında diff üçlemesinin yerine geçer.\n\n**İmleç:** yanıttaki `next_cursor` değerini bir sonraki isteğe `since` olarak verin. İmleç opaktır\n(`fd_…`), günlerce geçerlidir ve istemcide kalıcı saklanmalıdır. `since` verilmezse akış en baştan başlar.\n\n**Garanti — en az bir kez (at-least-once):** aynı mesaj tekrar dönebilir; mesajın `id` alanıyla\ntekilleştirin. **Kayıp olmaz.**\n\n**Sıra neden `id` değil:** veritabanı kimliği kayıt yazılmadan ÖNCE ayrılır, dolayısıyla küçük\nkimlikli bir mesaj büyük kimlikli birinden SONRA görünür olabilir. Böyle bir anda `id` ile ilerleyen\nbir istemci aradaki mesajı kalıcı olarak atlar. Bu uçtaki sıra numarası kayıt **kesinleştikten sonra**\nverilir; bu yüzden bir kez geçtiğiniz noktanın gerisine sonradan mesaj düşmez.\n\n**Henüz kesinleşmemiş mesajlar teslim edilmez** — bir sonraki çağrıda sıralarıyla birlikte gelirler.\nYani akış şüphe anında atlamaz, bekler. Bu nedenle çok kısa (saniyeler mertebesinde) bir gecikme normaldir.\n\nSistem mesajları (`system_*`) akışa dahil değildir.","security":[{"OAuth2":["messaging:read"]}],"parameters":[{"name":"since","in":"query","required":false,"schema":{"type":"string"},"description":"Opak imleç (`fd_…`) — önceki yanıtın `next_cursor` değeri. Verilmezse baştan başlar."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":100,"maximum":500}}],"responses":{"200":{"description":"Mesaj akışı","headers":{"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Pencere başına izinli istek"},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Pencerede kalan istek"},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Sıfırlanmaya kalan saniye"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageFeedList"},"example":{"object":"list","url":"/api/v2/wa/messages","has_more":true,"next_cursor":"fd_15M","data":[{"object":"message","id":"msg_8","direction":"inbound","type":"text","preview":"Talep Kodu: Y2W6-SVS8\nAraç: Renault Megane (BM0) 2014\nMotor: 1.5 dCi\nŞanzıman: Manuel\nNot: Sol ön kapı da gerekiyor, fiyat verebilir misiniz?","text":"Talep Kodu: Y2W6-SVS8\nAraç: Renault Megane (BM0) 2014\nMotor: 1.5 dCi\nŞanzıman: Manuel\nNot: Sol ön kapı da gerekiyor, fiyat verebilir misiniz?","status":null,"created":1751327000,"contact":{"object":"contact","id":"ctc_7B","phone":"+905551234567"}},{"object":"message","id":"msg_10","direction":"inbound","type":"image","preview":"[image: hasar.jpg]","text":"Talep Kodu: Y2W6-SVS8 — hasarlı parçanın fotoğrafı","media":{"type":"image","filename":"hasar.jpg"},"status":null,"created":1751327800,"contact":{"object":"contact","id":"ctc_7B","phone":"+905551234567"}}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"tags":["Messages"],"summary":"Mesaj gönder","description":"WhatsApp bildirimi gönderir.\n\n**İçerik (tam olarak biri):** `template` objesi → onaylı şablon; `body` (string) → pencere-içi serbest metin. İkisi birden / hiçbiri → `400 parameter_conflict`.\n\n**Alıcı (biri zorunlu):** `to` (E.164 telefon — kişi yoksa oluşturulur) **veya** `contact` (opak `ctc_` kimliği).\n\n**`sender` opsiyonel:** tek aktif WhatsApp numaranız varsa otomatik seçilir.\n\n**Idempotency:** `Idempotency-Key` header'ı ile çift gönderim önlenir; tekrar-oynatmada yanıt `Idempotency-Replayed: true` header'ı ile döner.","security":[{"OAuth2":["messaging:send"]}],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","example":"order-12345-shipped"},"description":"Aynı anahtarla tekrar çağrılırsa mesaj yeniden gönderilmez."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendMessageRequest"},"examples":{"template":{"summary":"Şablon (kargo bildirimi)","value":{"sender":"snd_1i","to":"+905551234567","recipient_name":"Ahmet","template":{"name":"kargo_bildirimi","language":"tr","variables":{"body":{"1":"Ahmet","2":"12345"}}}}},"text":{"summary":"Serbest metin (24s pencere içinde)","value":{"contact":"ctc_7B","body":"Merhaba, siparişiniz hazırlanıyor."}}}}}},"responses":{"200":{"description":"Tekrar-oynatma — aynı Idempotency-Key ile daha önce gönderilmiş; mesaj yeniden gönderilmedi","headers":{"Idempotency-Replayed":{"schema":{"type":"string"},"description":"Her zaman \"true\""},"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Pencere başına izinli istek"},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Pencerede kalan istek"},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Sıfırlanmaya kalan saniye"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Message"},"example":{"object":"message","id":"msg_SEPncmVhdGVk","status":"accepted","direction":"outbound","channel":"whatsapp","sender":"snd_1i","to":{"object":"contact","id":"ctc_7B","phone":"+905551234567"},"content":{"kind":"template","template":"tpl_5","template_name":"kargo_bildirimi","language":"tr"},"created":1751328000}}}},"201":{"description":"Oluşturuldu — kabul edildi (Meta'ya iletildi)","headers":{"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Pencere başına izinli istek"},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Pencerede kalan istek"},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Sıfırlanmaya kalan saniye"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Message"},"example":{"object":"message","id":"msg_SEPncmVhdGVk","status":"accepted","direction":"outbound","channel":"whatsapp","sender":"snd_1i","to":{"object":"contact","id":"ctc_7B","phone":"+905551234567"},"content":{"kind":"template","template":"tpl_5","template_name":"kargo_bildirimi","language":"tr"},"created":1751328000}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"Pencere kapalı / kanal aktif değil","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"type":"invalid_request_error","code":"messaging_window_closed","message":"24 saatlik pencere kapalı — onaylı şablon kullanın.","param":"body","messaging_state":{"sendable":false,"within_window":false,"window_expires_at":null,"requires_template":true},"request_id":"req_8Fk2Qp","doc_url":"https://api.cozumdevi.com/api/v2/wa/docs#messaging_window_closed"}}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/media":{"post":{"tags":["Messages"],"summary":"Medya yükle (dosya)","description":"Bir dosyayı (PDF / görsel / video) yükler ve şablon header'ında kullanılacak opak **`med_…` handle** döner. `multipart/form-data`; alan **`file`** (zorunlu), opsiyonel `sender` (snd_ id) ve `filename`. Handle **~30 gün** geçerlidir; gönderirken `template.header = { \"type\": \"document\", \"media\": \"med_…\", \"filename\": \"Fatura.pdf\" }` olarak verin. Alternatif: dosyayı public bir URL'de tutup `template.header = { \"type\":\"document\", \"link\":\"https://…\" }` da kullanabilirsiniz.","security":[{"OAuth2":["messaging:send"]}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["file"],"properties":{"file":{"type":"string","format":"binary","description":"Yüklenecek dosya (PDF/görsel/video, ≤16MB)"},"sender":{"type":"string","description":"Opsiyonel gönderen (snd_ id) — tek WhatsApp varsa gerekmez"},"filename":{"type":"string","description":"Opsiyonel — DOCUMENT için dosya adı"}}}}}},"responses":{"201":{"description":"Yüklendi","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Media"},"example":{"object":"media","id":"med_eyJpIjo3MiwibSI6IjEyMzQ1NiJ9","type":"document","filename":"Fatura-12345.pdf","mime_type":"application/pdf"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/senders":{"get":{"tags":["Senders"],"summary":"Gönderen numaraları listele","description":"Bağlı aktif WhatsApp numaralarınız (gönderenler).","security":[{"OAuth2":["messaging:read"]}],"responses":{"200":{"description":"Liste","headers":{"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Pencere başına izinli istek"},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Pencerede kalan istek"},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Sıfırlanmaya kalan saniye"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SenderList"},"example":{"object":"list","url":"/api/v2/wa/senders","has_more":false,"data":[{"object":"sender","id":"snd_1i","channel":"whatsapp","name":"+90 555 123 4567","phone_number":"+90 555 123 4567"}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/senders/{sender_id}/templates":{"get":{"tags":["Senders"],"summary":"Şablonları listele","description":"Bir gönderenin onaylı (APPROVED) WhatsApp şablonları. `variables` hangi değişkenlerin doldurulacağını, `preview` metni gösterir.","security":[{"OAuth2":["messaging:read"]}],"parameters":[{"name":"sender_id","in":"path","required":true,"schema":{"type":"string"},"example":"snd_1i"}],"responses":{"200":{"description":"Liste","headers":{"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Pencere başına izinli istek"},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Pencerede kalan istek"},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Sıfırlanmaya kalan saniye"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateList"},"example":{"object":"list","url":"/api/v2/wa/senders/snd_1i/templates","has_more":false,"data":[{"object":"template","id":"tpl_5","name":"kargo_bildirimi","language":"tr","category":"utility","variables":{"body":["1","2"],"header":[],"buttons":[]},"preview":"Merhaba {{1}}, {{2}} numaralı siparişiniz kargoya verildi."}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/contacts/{contact_id}":{"get":{"tags":["Contacts"],"summary":"Kişi getir (opak id)","description":"Opak `ctc_` kimliğiyle kişi + gönderim durumu.","security":[{"OAuth2":["messaging:read"]}],"parameters":[{"name":"contact_id","in":"path","required":true,"schema":{"type":"string"},"example":"ctc_7B"}],"responses":{"200":{"description":"Kişi","headers":{"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Pencere başına izinli istek"},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Pencerede kalan istek"},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Sıfırlanmaya kalan saniye"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Contact"},"example":{"object":"contact","id":"ctc_7B","name":"Ahmet","phone":"+905551234567","channel":"whatsapp","messaging":{"sendable":false,"within_window":false,"window_expires_at":null,"requires_template":true}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/contacts":{"get":{"tags":["Contacts"],"summary":"Kişi ara (telefon)","description":"E.164 telefonla kişi + gönderim durumu. Tekil kişi objesi döner (liste değil).","security":[{"OAuth2":["messaging:read"]}],"parameters":[{"name":"phone","in":"query","required":true,"schema":{"type":"string"},"example":"+905551234567"}],"responses":{"200":{"description":"Kişi","headers":{"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Pencere başına izinli istek"},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Pencerede kalan istek"},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Sıfırlanmaya kalan saniye"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Contact"},"example":{"object":"contact","id":"ctc_7B","name":"Ahmet","phone":"+905551234567","channel":"whatsapp","messaging":{"sendable":false,"within_window":false,"window_expires_at":null,"requires_template":true}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/conversations":{"get":{"tags":["Inbox"],"summary":"Konuşmalar (kim yazdı)","description":"Son mesaja göre sıralı WhatsApp konuşmaları — her biri kişi + son mesaj (yön/önizleme/tarih) + okunmamış sayısı. Sayfalama: `limit` (varsayılan 25, max 100) + `before` (son öğenin `last_message.created` değeri, epoch sn).","security":[{"OAuth2":["messaging:read"]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":25,"maximum":100}},{"name":"before","in":"query","required":false,"schema":{"type":"integer"},"description":"Cursor — Unix epoch (saniye); bundan eski konuşmalar"},{"name":"order","in":"query","required":false,"schema":{"type":"string","enum":["desc","asc"],"default":"desc"},"description":"desc = en yeni önce (varsayılan) · asc = en eski önce"}],"responses":{"200":{"description":"Konuşma listesi","headers":{"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Pencere başına izinli istek"},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Pencerede kalan istek"},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Sıfırlanmaya kalan saniye"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversationList"},"example":{"object":"list","url":"/api/v2/wa/conversations","has_more":false,"data":[{"object":"conversation","contact":{"object":"contact","id":"ctc_7B","name":"Ahmet","phone":"+905551234567","channel":"whatsapp"},"last_message":{"id":"msg_9","direction":"inbound","type":"text","preview":"Talep Kodu: Y2W6-SVS8\nAraç: Renault Megane (BM0) 2014\nMotor: 1.5 dCi\nŞanzıman: Manuel\nNot: Sol ön kapı da gerekiyor, fiyat verebilir misiniz?","text":"Talep Kodu: Y2W6-SVS8\nAraç: Renault Megane (BM0) 2014\nMotor: 1.5 dCi\nŞanzıman: Manuel\nNot: Sol ön kapı da gerekiyor, fiyat verebilir misiniz?","status":null,"created":1751328000},"unread":2}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/contacts/{contact_id}/messages":{"get":{"tags":["Inbox"],"summary":"Mesaj geçmişi","description":"Bir kişinin mesaj geçmişi (gelen + giden). Varsayılan `order=asc` (eskiden yeniye). **Polling için `order=desc`** kullanın → en yeni mesajlar önce döner (yeni gelen mesajları anında yakalar). Sayfalama: `limit` (max 200) + `before` (opak `msg_` id — bundan eski mesajlar).","security":[{"OAuth2":["messaging:read"]}],"parameters":[{"name":"contact_id","in":"path","required":true,"schema":{"type":"string"},"example":"ctc_7B"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50,"maximum":200}},{"name":"before","in":"query","required":false,"schema":{"type":"string"},"description":"Cursor — opak msg_ id; bundan eski mesajlar"},{"name":"order","in":"query","required":false,"schema":{"type":"string","enum":["asc","desc"],"default":"asc"},"description":"asc = en eski önce (varsayılan) · **desc = en yeni önce (polling için)**"}],"responses":{"200":{"description":"Mesaj listesi","headers":{"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Pencere başına izinli istek"},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Pencerede kalan istek"},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Sıfırlanmaya kalan saniye"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageHistoryList"},"example":{"object":"list","url":"/api/v2/wa/contacts/ctc_7B/messages","has_more":false,"data":[{"object":"message","id":"msg_8","direction":"inbound","type":"text","preview":"Talep Kodu: Y2W6-SVS8\nAraç: Renault Megane (BM0) 2014\nMotor: 1.5 dCi\nŞanzıman: Manuel\nNot: Sol ön kapı da gerekiyor, fiyat verebilir misiniz?","text":"Talep Kodu: Y2W6-SVS8\nAraç: Renault Megane (BM0) 2014\nMotor: 1.5 dCi\nŞanzıman: Manuel\nNot: Sol ön kapı da gerekiyor, fiyat verebilir misiniz?","status":null,"created":1751327000},{"object":"message","id":"msg_10","direction":"inbound","type":"image","preview":"[image: hasar.jpg]","text":"Talep Kodu: Y2W6-SVS8 — hasarlı parçanın fotoğrafı","media":{"type":"image","filename":"hasar.jpg"},"status":null,"created":1751327800},{"object":"message","id":"msg_9","direction":"outbound","type":"template","preview":"Merhaba Ahmet, siparişiniz kargoya verildi.","text":"Merhaba Ahmet, siparişiniz kargoya verildi.","status":"sent","created":1751327500}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}}},"components":{"securitySchemes":{"OAuth2":{"type":"oauth2","description":"Panel → Uygulamalar → Uygulamalarım'dan bir uygulama oluşturup **client_id + client_secret** alın. Bu bilgilerle `POST /oauth/token` (client_credentials) çağırıp **access token** üretin; API çağrılarında `Authorization: Bearer <access_token>` kullanın.","flows":{"clientCredentials":{"tokenUrl":"https://api.cozumdevi.com/api/v2/wa/oauth/token","scopes":{"messaging:send":"Mesaj / şablon gönder","messaging:read":"Gönderenler, şablonlar, kişi durumu"}}}}},"schemas":{"SendMessageRequest":{"type":"object","description":"İçerik için tam olarak biri: `template` VEYA `body`. Alıcı için biri: `to` VEYA `contact`.","properties":{"sender":{"type":"string","description":"Gönderen (opak snd_ id). Tek aktif numara varsa opsiyonel.","example":"snd_1i"},"to":{"type":"string","description":"Alıcı telefon (E.164). Kişi yoksa oluşturulur.","example":"+905551234567"},"contact":{"type":"string","description":"Alıcı kişi (opak ctc_ id) — `to` yerine.","example":"ctc_7B"},"recipient_name":{"type":"string","description":"Yeni kişi oluşurken isim (opsiyonel).","example":"Ahmet"},"template":{"$ref":"#/components/schemas/TemplateSend"},"body":{"type":"string","description":"Serbest metin (yalnız 24s pencere içinde).","example":"Merhaba, siparişiniz hazırlanıyor."}}},"TemplateSend":{"type":"object","required":["name"],"properties":{"name":{"type":"string","example":"kargo_bildirimi"},"language":{"type":"string","example":"tr"},"variables":{"type":"object","description":"Değişkenler. `body`: {{n}} → değer eşlemesi (adlandırılmış obje veya dizi).","properties":{"body":{"type":"object","additionalProperties":{"type":"string"},"example":{"1":"Ahmet","2":"12345"}}}},"header":{"description":"Şablon başlığı: TEXT header değişkeni için **string**, IMAGE/VIDEO/DOCUMENT header için **medya objesi**. Verilmezse şablonun kendi örnek medyası kullanılır.","oneOf":[{"type":"string","description":"TEXT header değişkeni"},{"type":"object","description":"Medya header — `link` (public URL) VEYA `media` (yüklenmiş handle) birini ver","properties":{"type":{"type":"string","enum":["image","video","document"]},"link":{"type":"string","description":"Public medya URL (`media` yoksa)"},"media":{"type":"string","description":"POST /media'dan dönen opak handle (`med_…`)"},"filename":{"type":"string","description":"DOCUMENT (PDF vb.) için alıcıda görünecek dosya adı"}}}]},"buttons":{"type":"object","description":"Dinamik URL buton değişkenleri (opsiyonel)."}}},"Message":{"type":"object","properties":{"object":{"type":"string","example":"message"},"id":{"type":"string","example":"msg_SEPncmVhdGVk"},"status":{"type":"string","example":"accepted"},"direction":{"type":"string","example":"outbound"},"channel":{"type":"string","example":"whatsapp"},"sender":{"type":"string","example":"snd_1i"},"to":{"type":"object","properties":{"object":{"type":"string"},"id":{"type":"string"},"phone":{"type":"string","nullable":true}}},"content":{"type":"object","description":"kind=template → {template, template_name, language}; kind=text → {preview}"},"created":{"type":"integer","description":"Unix epoch (sn)","example":1751328000}}},"Sender":{"type":"object","properties":{"object":{"type":"string","example":"sender"},"id":{"type":"string","example":"snd_1i"},"channel":{"type":"string","example":"whatsapp"},"name":{"type":"string","nullable":true},"phone_number":{"type":"string","nullable":true}}},"Template":{"type":"object","properties":{"object":{"type":"string","example":"template"},"id":{"type":"string","example":"tpl_5"},"name":{"type":"string"},"language":{"type":"string"},"category":{"type":"string","example":"utility"},"variables":{"type":"object","example":{"body":["1","2"],"header":[],"buttons":[]}},"preview":{"type":"string"}}},"MessagingState":{"type":"object","properties":{"sendable":{"type":"boolean","description":"Serbest metin gönderilebilir mi (pencere açık mı)"},"within_window":{"type":"boolean"},"window_expires_at":{"type":"string","format":"date-time","nullable":true},"requires_template":{"type":"boolean"}}},"Contact":{"type":"object","properties":{"object":{"type":"string","example":"contact"},"id":{"type":"string","example":"ctc_7B"},"name":{"type":"string","nullable":true},"phone":{"type":"string","nullable":true},"channel":{"type":"string","example":"whatsapp"},"messaging":{"$ref":"#/components/schemas/MessagingState"}}},"SenderList":{"type":"object","properties":{"object":{"type":"string","example":"list"},"url":{"type":"string"},"has_more":{"type":"boolean"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Sender"}}}},"TemplateList":{"type":"object","properties":{"object":{"type":"string","example":"list"},"url":{"type":"string"},"has_more":{"type":"boolean"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Template"}}}},"Conversation":{"type":"object","properties":{"object":{"type":"string","example":"conversation"},"contact":{"type":"object","properties":{"object":{"type":"string"},"id":{"type":"string","example":"ctc_7B"},"name":{"type":"string","nullable":true},"phone":{"type":"string","nullable":true},"channel":{"type":"string"}}},"last_message":{"type":"object","properties":{"id":{"type":"string","example":"msg_9"},"direction":{"type":"string","example":"inbound","description":"inbound | outbound"},"type":{"type":"string","example":"text","description":"text | template | image | video | document | audio"},"preview":{"type":"string","description":"Listede göstermek için kısa gösterim. Düz metin mesajlarda TAM gövdedir (kırpılmaz), ama medya mesajlarında içerik yerine yer tutucu döner: `[image: fatura.pdf]`. Bu alan geriye dönük uyumluluk için korunuyor — metin işleyen entegrasyonlar `text` kullanmalı."},"text":{"type":"string","nullable":true,"description":"Mesajın TAM gövdesi. Kırpılmaz, satır sonları (\\n) korunur, uzunluk sınırı yoktur. Medya mesajlarında müşterinin yazdığı açıklama varsa onu taşır; yalnızca dosya gönderilmişse `null`. Gerçek metin yoksa boş string değil `null` döner (\"metin yok\" ile \"metin boş\" ayrımı korunur)."},"status":{"type":"string","nullable":true,"description":"Son mesaj durumu (sent | delivered | read | failed | …); gelen mesajda null"},"media":{"type":"object","nullable":true,"properties":{"type":{"type":"string"},"filename":{"type":"string","nullable":true}}},"created":{"type":"integer","description":"Unix epoch (saniye)"}}},"unread":{"type":"integer","description":"Okunmamış gelen mesaj sayısı"}}},"ConversationList":{"type":"object","properties":{"object":{"type":"string","example":"list"},"url":{"type":"string"},"has_more":{"type":"boolean"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Conversation"}}}},"HistoryMessage":{"type":"object","properties":{"object":{"type":"string","example":"message"},"id":{"type":"string","example":"msg_9","nullable":true,"description":"Opak msg_ id; yapısal-öncesi webhook kaynaklı eski mesajlarda null olabilir"},"direction":{"type":"string","example":"inbound","description":"inbound | outbound"},"type":{"type":"string","example":"text"},"preview":{"type":"string","description":"Listede göstermek için kısa gösterim. Düz metin mesajlarda TAM gövdedir (kırpılmaz), ama medya mesajlarında içerik yerine yer tutucu döner: `[image: fatura.pdf]`. Bu alan geriye dönük uyumluluk için korunuyor — metin işleyen entegrasyonlar `text` kullanmalı."},"text":{"type":"string","nullable":true,"description":"Mesajın TAM gövdesi. Kırpılmaz, satır sonları (\\n) korunur, uzunluk sınırı yoktur. Medya mesajlarında müşterinin yazdığı açıklama varsa onu taşır; yalnızca dosya gönderilmişse `null`. Gerçek metin yoksa boş string değil `null` döner (\"metin yok\" ile \"metin boş\" ayrımı korunur)."},"media":{"type":"object","nullable":true,"properties":{"type":{"type":"string"},"filename":{"type":"string","nullable":true}}},"status":{"type":"string","nullable":true},"created":{"type":"integer","description":"Unix epoch (sn)"}}},"MessageHistoryList":{"type":"object","properties":{"object":{"type":"string","example":"list"},"url":{"type":"string"},"has_more":{"type":"boolean"},"data":{"type":"array","items":{"$ref":"#/components/schemas/HistoryMessage"}}}},"FeedMessage":{"type":"object","description":"Akış öğesi — mesaj geçmişindeki mesajla aynı alanlar, ARTI `contact`.","properties":{"object":{"type":"string","example":"message"},"id":{"type":"string","example":"msg_8"},"direction":{"type":"string","example":"inbound","description":"inbound | outbound"},"type":{"type":"string","example":"text"},"preview":{"type":"string","description":"Listede göstermek için kısa gösterim. Düz metin mesajlarda TAM gövdedir (kırpılmaz), ama medya mesajlarında içerik yerine yer tutucu döner: `[image: fatura.pdf]`. Bu alan geriye dönük uyumluluk için korunuyor — metin işleyen entegrasyonlar `text` kullanmalı."},"text":{"type":"string","nullable":true,"description":"Mesajın TAM gövdesi. Kırpılmaz, satır sonları (\\n) korunur, uzunluk sınırı yoktur. Medya mesajlarında müşterinin yazdığı açıklama varsa onu taşır; yalnızca dosya gönderilmişse `null`. Gerçek metin yoksa boş string değil `null` döner (\"metin yok\" ile \"metin boş\" ayrımı korunur)."},"contact":{"type":"object","description":"Mesajın ait olduğu kişi. Akış kiracı genelinde olduğu için öğenin içinde döner — ayrıca `/contacts/…` çağırmanız gerekmez.","properties":{"object":{"type":"string"},"id":{"type":"string","example":"ctc_7B"},"phone":{"type":"string","nullable":true}}},"media":{"type":"object","nullable":true,"properties":{"type":{"type":"string"},"filename":{"type":"string","nullable":true}}},"status":{"type":"string","nullable":true},"created":{"type":"integer","description":"Unix epoch (sn)"}}},"MessageFeedList":{"type":"object","properties":{"object":{"type":"string","example":"list"},"url":{"type":"string"},"has_more":{"type":"boolean","description":"Bu imlecin ötesinde daha fazla mesaj var mı."},"next_cursor":{"type":"string","nullable":true,"description":"Bir sonraki isteğe `since` olarak verin. Boş sayfada bile döner — imlecinizi kaybetmezsiniz."},"data":{"type":"array","items":{"$ref":"#/components/schemas/FeedMessage"}}}},"Media":{"type":"object","properties":{"object":{"type":"string","example":"media"},"id":{"type":"string","description":"Opak handle — send'de `template.header.media`","example":"med_eyJpIjo3MiwibSI6IjEyMyJ9"},"type":{"type":"string","example":"document"},"filename":{"type":"string","nullable":true},"mime_type":{"type":"string","nullable":true}}},"OAuthError":{"type":"object","description":"Token endpoint hata (OAuth2 standart)","properties":{"error":{"type":"string","example":"invalid_client","description":"invalid_request | invalid_client | unsupported_grant_type | server_error"},"error_description":{"type":"string"},"request_id":{"type":"string","example":"req_8Fk2Qp"}}},"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"type":{"type":"string","example":"invalid_request_error","description":"authentication_error | permission_error | invalid_request_error | rate_limit_error | api_error"},"code":{"type":"string","example":"template_not_found"},"message":{"type":"string"},"param":{"type":"string","nullable":true,"description":"Hatalı alan"},"request_id":{"type":"string","example":"req_8Fk2Qp"},"doc_url":{"type":"string"}}}}}},"responses":{"Unauthorized":{"description":"Anahtar eksik/geçersiz/iptal","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"type":"authentication_error","code":"unauthorized","message":"Geçersiz veya eksik API anahtarı.","request_id":"req_8Fk2Qp","doc_url":"https://api.cozumdevi.com/api/v2/wa/docs#unauthorized"}}}}},"Forbidden":{"description":"Anahtarda gerekli scope yok","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"type":"permission_error","code":"insufficient_scope","message":"Bu işlem için scope gerekli: messaging:send","request_id":"req_8Fk2Qp","doc_url":"https://api.cozumdevi.com/api/v2/wa/docs#insufficient_scope"}}}}},"BadRequest":{"description":"Geçersiz istek","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"type":"invalid_request_error","code":"template_not_found","message":"Onaylı şablon bulunamadı: kargo_bildirimi","param":"template.name","request_id":"req_8Fk2Qp","doc_url":"https://api.cozumdevi.com/api/v2/wa/docs#template_not_found"}}}}},"NotFound":{"description":"Kaynak bulunamadı","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"type":"invalid_request_error","code":"resource_missing","message":"Kişi bulunamadı.","param":"contact","request_id":"req_8Fk2Qp","doc_url":"https://api.cozumdevi.com/api/v2/wa/docs#resource_missing"}}}}},"RateLimited":{"description":"Hız limiti / kota aşıldı (uygulama başına, saniye penceresi)","headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Kaç saniye sonra tekrar denenebilir"},"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Pencere başına izin (X-RateLimit-Limit ile aynı)"},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Kalan istek (X-RateLimit-Remaining ile aynı)"},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Sıfırlanmaya kalan SANİYE (delta)"},"X-RateLimit-Limit":{"schema":{"type":"integer"}},"X-RateLimit-Remaining":{"schema":{"type":"integer"}},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Sıfırlanma zamanı — Unix EPOCH (sn)"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"type":"rate_limit_error","code":"rate_limit_exceeded","message":"İstek hız limiti aşıldı (3/1s). 1s sonra tekrar deneyin.","request_id":"req_8Fk2Qp","doc_url":"https://api.cozumdevi.com/api/v2/wa/docs#rate_limit_exceeded"}}}}}}}}