M

msg724 — Geliştirici Rehberi

API Dokümantasyonu →

DoWaba — Geliştirici Entegrasyon Rehberi (Menü → API)

Kime: DoWaba panelini kendi uygulamasından (headless) kullanan geliştirici partner'lar. Özellikle Trusted Partner OAuth ile bağlanıp kullanıcı adına panel API'larını çağıranlar için.

Bu doküman neden var: API Dokümantasyonu (/api-docs/) endpoint'leri API grubuna göre dizer — eksiksiz referans ama "panelde gördüğüm şeyin API karşılığı ne?" sorusuna doğrudan cevap vermez. Bu rehber panelin sol menüsünü takip eder: her menü için → ne işe yarar · paneldeki form/alanlar · hangi endpoint · örnek istek · örnek yanıt. İkisi birbirini tamamlar:

İhtiyaç Kaynak
"Hangi menü hangi endpoint, akış nasıl?" Bu doküman
"Bu endpoint'in tam validasyon alanları neler?" API Dokümantasyonu — her endpoint'te alan listesi
Postman / OpenAPI 3 /api-docs/collection.json · /api-docs/openapi.yaml
Çalışan örnek projeler Örnek Projeler
Toplu gönderim / pazarlama uyum kuralları § 0.6 — opt-out + İYS + KVKK (ZORUNLU)
API'lerde ne değişti? (yeni endpoint / kural değişikliği) Panelde: Geliştirici → Sürüm Notları (canlı, sürümlü) — bkz. § 13

0. Nasıl çalışır — Trusted Partner OAuth

Senin uygulaman kendi users tablosunu tutmuyor; kullanıcılar DoWaba'nın user havuzunda. Uygulaman, kullanıcı adına DoWaba'nın panel API'larını (sites, conversations, contacts…) çağırıyor — sanki kullanıcı DoWaba'nın resmi mobil uygulamasındaymış gibi. Bunu sağlayan şey sanctum_token.

0.1 sanctum_token'ı nasıl alıyorsun (özet)

  1. (Tek seferlik) DoWaba ekibi senin OAuth client'ında issues_sanctum_token özelliğini açar. Bu, sözleşmeli partner için elle açılır — self-service panelden açılamaz.
  2. Kullanıcı "Login with DoWaba" ile gelir (PKCE + onay ekranı).
  3. Backend'in POST /api/oauth/token çağırır (grant_type=authorization_code).
  4. Yanıtta standart alanların yanında ekstra döner:
{
  "access_token":       "doat_...",
  "id_token":           "eyJ...",
  "refresh_token":      "dort_...",
  "expires_in":         3600,
  "scope":              "openid profile email",

  "sanctum_token":      "12|abc...",   // ← panel API'ları için BU
  "sanctum_token_type": "Bearer"
}

sanctum_token gelmiyorsa: client trusted partner değil veya kullanıcı superadmin (superadmin trusted partner app'lere giremez — onay ekranında bloklanır).

0.2 Her istekte

curl https://dowaba.com/api/sites \
  -H "Authorization: Bearer 12|abc..." \
  -H "Accept: application/json"

0.3 "Kullanıcı kim?" (kimlik / yetki kontrolü)

GET /api/auth/user        # oturum açan kullanıcı: id, name, email, role, reseller_id,
                          # allowed_modules, assigned_site_id, is_agent_only ...
GET /api/user             # profil detayları

allowed_modules alanı kritik: kullanıcı kısıtlıysa (sub-user / bayi müşterisi / agent) bu bir whitelist (["whatsapp","mail",...]). null ise tüm modüller açık. Bir menü/endpoint'i göstermeden önce buna bak.

0.4 Yanıt zarfı & hatalar

0.4.2 Genel rate limit & kota sorgulama (2026-07-12)

Geliştirici anahtarıyla (panelde Geliştirici > API Anahtarları'ndan üretilen dev: token'lar ve Trusted Partner oauth- token'ları) yapılan tüm istekler kullanıcı başına tek dakikalık sayaçta toplanır — uç başına ayrı limit değildir. Panel/mobil oturum trafiği bu sayaca girmez.

Hesap Limit (istek/dk)
Business 60
Professional 120
Premium 300
Ultimate 600
Bayi 300 + aktif müşteri × 30 (tavan 1.500)
Bayi müşterisi 60
GET /api/me/rate-limit
#   → 200 { "unlimited": false, "limit": 120, "used": 34, "remaining": 86,
#          "window_seconds": 60, "source": "plan" }
#   source: plan | reseller | reseller_customer | override | default

0.4.1 Kullanıcıyı panele taşı — tek-kullanımlık SSO giriş linki (2026-06-17)

API erişimin var ama kullanıcıyı tarayıcıda panele sokmak istiyorsan (örn. kendi arayüzünde "Panele Git" butonu):

POST /api/oauth/login-link
#   Bearer: sanctum_token   (yalnız 'oauth-' Trusted Partner PAT; panel/'api-token' → 403)
#   → 200 { "success": true, "url": "https://<marka-host>/admin/impersonate?token=...",
#          "expires_at": "...", "expires_in_seconds": 300 }
#   → 403 token Trusted Partner değilse  ·  422 pasif/askıdaki hesap

0.5 ⚠️ Veri kullanım kuralları (Meta uyumu — ZORUNLU)

inbox/unified, {kanal}/messages/..., inbox/media, contacts gibi endpoint'ler WhatsApp / Instagram / Messenger mesaj içeriği + telefon + isim + medya (Meta Verisi) döndürür. Bu veriyi çekmek = Meta Verisi'nin 3. tarafça işlenmesi → Meta Platform Terms + WhatsApp Business Solution Terms'e tabidir. Kurallar (DoWaba Partner Sözleşmesi ile bağlayıcı):

Özet: "İşletme kendi DoWaba verisi için senin aracını kullanıyor" = uygun. "Sen DoWaba verisiyle kendi ürününü besliyorsun" = ihlal.


0.6 ⚠️ Toplu gönderim & pazarlama uyumu (ZORUNLU)

scheduled-jobs, contacts/groups/{id}/send-template, mail-campaigns gibi toplu / kampanya gönderim endpoint'lerini çağırıyorsan Türkiye mevzuatı (ETK 6563 + İYS + KVKK) senin entegrasyonun için de geçerlidir — panelden manuel gönderim ile API üzerinden gönderim aynı hukuki yükümlülüğe tabidir. Üç kural:

1. Red (opt-out) listesi — DAİMA uygula. Alıcı "DUR / STOP / İPTAL" yazdıysa veya elle çıkarıldıysa kanal-bağımsız suppression listesine (message_opt_outs) düşer. Bu kişilere gönderim yapılamaz.

2. İYS onayı — pazarlamada ön onay zorunlu. Ticari/pazarlama içerikli toplu mesaj için alıcının önceden onayı

3. KVKK aydınlatması — botu kapatırsan SEN sorumlusun. Bir konuşmada toggle-bot ile AI'yı kapatıp kendi uygulamandan cevaplıyorsan, DoWaba'nın otomatik KVKK aydınlatması (son alıcının ilk mesajında gönderilen) devreye girmez — aydınlatma AI cevabına bağlıdır. Bu durumda son alıcıya KVKK aydınlatmasını sen sağlamak zorundasın (veri sorumlusu = DoWaba hesap sahibi; sen onun adına işleyensin → Partner Sözleşmesi).

Red (opt-out) listesi endpoint'leri:

GET    /api/message-opt-outs?site_id=12&channel=whatsapp   # red listesi — channel: whatsapp | sms | mail | call
POST   /api/message-opt-outs   { "site_id": 12, "channel": "call", "identifier": "+905551112233", "reason": "telefonla beni aramayın dedi" }
DELETE /api/message-opt-outs/{id}

identifier = telefon (whatsapp/sms/call, E.164) veya e-posta (mail) — sistem normalize eder. Yanıt sayfalıdır (data, total, current_page, last_page). channel=call sesli arama kampanyalarının (§ 5) red listesidir; kaydı silmek numarayı tekrar aranabilir yapar.


Menü → bölüm haritası

DoWaba sol menüsündeki sırayla:

Sol menü Bölüm Ana endpoint(ler)
Siteler §1 sites, sites/{site}/faqs, sites/{site}/documents, sites/{site}/functions
Mesaj Kutusu (Inbox) §2 inbox/unified, inbox/mark-read, inbox/media/...
Kanallar (WhatsApp · Telegram · Messenger · Instagram · X · TikTok · Mail · Widget · Google Yorumlar) §3 {kanal}/conversations, {kanal}/messages/..., {kanal}/send
Rehber & WP Kampanya §4 contacts/groups, contacts/..., scheduled-jobs
Şablonlar / Kampanyalar (+ Çağrı Kampanyası) §5 whatsapp/templates, mail-templates, mail-campaigns, voice-campaigns
Reklam Yönetimi (Meta Ads) §5.5 ads/accounts, ads/studio/*, ads/campaigns, ads/insights, ads/guard
Çağrılar (Voice) + Outbound + Santral §6 sites/{site}/voice-conversations, voice/call, sites/{site}/transfer-targets, outbound-intents, outbound-messages
Müşteri Talepleri (Callback) §7 callback-requests
Potansiyel Müşteriler (Leads / CRM) §7.5 leads, leads/scan, leads/{id}/stage, leads/{id}/assign, leads/{id}/activities
Bilgi Tabanı (site içi: SSS + Belge) §8 sites/{site}/faqs, sites/{site}/documents
Kullanım & Krediler / Abonelik §9 me/usage, credits, subscriptions/*, ai-credit/*
Geliştirici (kendi token/webhook/oauth + ChatGPT MCP) §10 me/tokens, webhook-endpoints, me/oauth/clients, https://dowaba.com/mcp
Profil & Ayarlar §11 auth/user, user, settings
Faturalarım / Bayi (+ Referans Programı) §12 me/billing, reseller/*, reseller/referral/*

Atlananlar / kısmi (2026-07-15 güncellendi): Paylaşım (social-posts/*, youtube/*, facebook-page/*) — kısmi/deneysel (YouTube videoları compliance audit'e kadar PRIVATE, IG publish scope App Review'da). Randevu (ayrı modül, site'ta appointment modülü gerektirir), Trendyol Q&A (trendyol/*). İhtiyaç olursa aynı şablonla eklenir.


1. Siteler

Ne işe yarar: Bir "site" = bir AI asistan yapılandırması (sistem promptu, dil, widget ayarı, bağlı kanallar). Her şeyin merkezi; çoğu endpoint sites/{site}/... altındadır.

Paneldeki form (Yeni Site): ad, açıklama, sistem promptu, dil(ler), (opsiyonel) domain.

Listeleme

GET /api/sites
{ "success": true, "data": [ { "id": 12, "name": "Örnek Mağaza", "is_active": true, "api_key": "...", ... } ] }

Tek site

GET /api/sites/{site}

Yeni site açma (hibrit slot / pay-per-site)

POST /api/sites
Content-Type: application/json

{ "name": "Örnek Mağaza", "description": "...", "settings": { "system_prompt": "Sen ...", "languages": ["tr"] } }

Bayi, müşteri adına site açar: POST /api/reseller/customers/{user}/sites (§12).

Güncelle / Sil / Geri al

PUT    /api/sites/{site}                 # ad, sistem promptu, ayarlar (settings.widget_theme dahil)
DELETE /api/sites/{site}                 # KVKK: anonymize + soft delete (30 gün geri alınabilir)
POST   /api/sites/{site}/restore         # 30 gün içinde geri al
POST   /api/sites/{site}/regenerate-key  # API anahtarını yeniden üret (widget vb.)
POST   /api/sites/{site}/widget-theme/ai # { "prompt": "koyu lacivert, altın vurgulu" } → AI widget tema ÖNERİSİ
POST   /api/sites/{site}/auto-messages/beautify # { "field": "ai_error", "text": "..." } → AI ile güzellenmiş metin ÖNERİSİ

widget-theme/ai yalnız öneri döner ({theme, summary}); kalıcı kayıt için dönen theme objesini PUT /api/sites/{site} ile settings.widget_theme alanına yaz (10 renk #RRGGBB + launcher_icon).

Otomatik mesajlar (2026-06-12): AI cevap veremediğinde müşteriye giden sistem metinleri site bazında özelleştirilebilir: PUT /api/sites/{id} ile settings.auto_messages.{unpaid_notice|ai_error}. Alan boş/yok ise sitenin birincil dilinde hazır metin gönderilir. auto-messages/beautify (field: unpaid_notice | ai_error; text opsiyonel — boşsa varsayılan baz alınır) yalnız öneri döner ({success, text}), kaydetmez.

Site domain'leri (widget origin doğrulama)

GET    /api/sites/{site}/domains
POST   /api/sites/{site}/domains          { "domain": "magaza.com" }
POST   /api/domains/{domain}/verify
DELETE /api/domains/{domain}

2. Mesaj Kutusu (Inbox)

Ne işe yarar: 8+ kanalın tek birleşik listesi. Panelin "Mesaj Kutusu"nun aynısı. Tek istekte tüm kanalların konuşmalarını + okunmamış sayaçlarını verir (yoksa kanal başına ~35 ayrı istek atman gerekir).

Birleşik konuşma listesi ← önerilen giriş noktası

GET /api/inbox/unified
GET /api/inbox/unified?filter=unread     # sadece okunmamışlar
GET /api/inbox/unified?channel=whatsapp  # tek kanal (sayfa boyu 500'e çıkar)
GET /api/inbox/unified?offset=50         # sonraki sayfa (2026-07-14, aşağıya bak)
GET /api/inbox/unified?fresh=1           # cache (15s) bypass
{
  "conversations": [
    { "channel": "whatsapp", "identifier": "+905551112233", "profile_id": 4,
      "display_name": "Örnek Müşteri", "last_message": "...", "unread": 2, "bot_active": true, "timestamp": "..." }
  ],
  "counts": { "whatsapp": 3, "telegram": 1 },
  "totals": { "unread": 4 },
  "hasMore": true,
  "pageSize": 50,
  "offset": 0
}

Sayfalama (2026-07-14): liste kanal başına pageSize konuşma döner (varsayılan 50, channel=X seçiliyken 500). hasMore=true ise sonraki sayfayı offset += pageSize ile iste; offset>0 yanıtlarında counts/totals null gelir (ilk sayfadaki değerler geçerlidir). Sayaç birimi (2026-07-14): counts.*.unread ve totals.unread okunmamış konuşma sayısıdır (mesaj adedi değil) — totals.total ile aynı birim. Konuşma başına mesaj adedi tile'daki unread alanında kalır.

channel + identifier (+ gerekiyorsa profile_id) ikilisi, kanal bölümlerindeki (§3) mesaj/gönder endpoint'lerine girdi olur.

Okundu işaretle (tüm kanallar tek endpoint)

POST /api/inbox/mark-read
{ "channel": "whatsapp", "identifier": "+905551112233", "profile_id": 4 }

Gönderilemeyen cevabı tekrar dene (2026-06-12)

Bot/operatör cevabı dış API'ye anlık reddedilirse (Meta 5xx/429/bağlantı hatası) mesaj failed kalır — kaybolmaz. Arka planda kademeli otomatik retry (5/15/45dk, maks 3) çalışır; ek olarak panelden "Tekrar dene" bu endpoint'i tetikler:

POST /api/inbox/messages/{channel}/{id}/retry     # channel: whatsapp | instagram | messenger | telegram
#   → 200 { "success": true, "delivery_status": "sent", "message_id": "..." }
#   → 422 mesajlaşma penceresi (24 saat) kapalıysa (TG'de pencere yok)

Mesaja ekli medya indir (foto/video/dosya)

GET /api/inbox/media/{channel}/{messageId}            # channel: whatsapp | instagram | telegram | messenger
GET /api/inbox/media/{channel}/{messageId}?download=1
GET /api/inbox/media-signed/{channel}/{messageId}     # imzalı varyant — Authorization header GEREKMEZ

İmzalı varyantın URL'ini kendin üretme: kanal mesaj listesi yanıtlarındaki media_signed_url alanı hazır imzalı gelir (24 saat geçerli, sonra 403). Medyayı kendi arayüzünde <img>/<video>/<audio> src'ına doğrudan koyacaksan bunu kullan — auth'lu /inbox/media/... tarayıcı media etiketlerinde header gönderilemediği için 401 alır. Sunucu tarafı indirme için Bearer'lı /inbox/media/... kullanmaya devam et.


3. Kanallar — mesajlaşma

Tüm mesajlaşma kanalları aynı 4 adımlı pattern'i izler:

  1. Konuşmaları listeleGET {kanal}/conversations
  2. Bir konuşmanın mesajlarıGET {kanal}/messages/{identifier}
  3. Yanıt gönderPOST {kanal}/send (Instagram/X/TikTok/Mail'de farklı isim)
  4. Botu aç/kapa (o konuşmada AI cevap versin mi) → POST {kanal}/toggle-bot

Bot mantığı: toggle-bot ile bir konuşmada AI'yı kapatırsan, o konuşmaya sen (operatör) cevap yazarsın. Açık bırakırsan gelen mesaja AI otomatik cevap verir. toggle-block ile konuşmayı engellersin.

Sayfalama (mesaj listesi) — 2026-06-07: {kanal}/messages/... varsayılanda konuşmanın tüm geçmişini döndürür (geriye dönük uyumlu). Performans için opt-in sayfalama var: ?limit=50 → en yeni 50 mesaj + { messages, has_more, oldest_id } zarfı; daha eski sayfa için ?before_id={oldest_id}&limit=50 (yukarı kaydırma cursor'ı, oldest_id'yi zincirle). limit/before_id vermezsen eski davranış (tüm mesajlar, düz dizi) aynen korunur — mevcut entegrasyonların bozulmaz. Destekleyen kanallar: WhatsApp · Telegram · Messenger · TikTok DM · X. (Instagram/Mail konuşma-gruplu yanıt verir, Widget/Voice tekil konuşmadır → bunlarda sayfalama yoktur.) limit 1–200 clamp.

3.1 WhatsApp — tam örnek

Paneldeki form (profil ekleme): bağlantı tipi (Evolution QR / Meta Cloud API / Coexistence) + ilgili kimlik bilgileri. Profil = bir WhatsApp numarası.

GET  /api/whatsapp/profiles                       # bağlı numaralar (profiller)
GET  /api/whatsapp/conversations                  # ?profile_id=X ile filtrele
GET  /api/whatsapp/messages/{phone}               # mesajlar (?limit=50&before_id=... ile sayfala — opsiyonel)
POST /api/whatsapp/send                           # yanıt gönder
GET  /api/whatsapp/conversation-state             # ?phone=...&profile_id=... → bot durumu
POST /api/whatsapp/toggle-bot
POST /api/whatsapp/toggle-block
DELETE /api/whatsapp/conversations/{phone}

Gönderme:

POST /api/whatsapp/send
{
  "phone": "+905551112233",
  "profile_id": 4,
  "message": "Merhaba, nasıl yardımcı olabilirim?",
  "media_file_id": null            // opsiyonel — önce POST /api/media/upload
}
{ "success": true, "data": { "id": 9001, "direction": "out", "status": "sent", "created_at": "2026-06-01T10:00:00Z" } }

Profil yönetimi (kurulum):

GET    /api/whatsapp-profiles
POST   /api/whatsapp-profiles            # profil oluştur
POST   /api/whatsapp-profiles/{id}/link-site      { "site_id": 12 }     # profili siteye bağla
POST   /api/whatsapp-profiles/{id}/unlink-site
GET    /api/whatsapp-profiles/{id}/linked-sites
# Evolution (QR): POST whatsapp-profiles/evolution/create → GET .../{id}/evolution/qrcode
# Meta Cloud:     GET whatsapp-profiles/platform/phones → POST whatsapp-profiles/platform/connect

3.2 Diğer kanallar — aynı pattern

Endpoint adları kanaldan kanala değişir ama akış aynıdır. Kimlik alanı (identifier) kanala göre farklı:

Kanal Konuşmalar Mesajlar Gönder Bot aç/kapa identifier
WhatsApp GET whatsapp/conversations GET whatsapp/messages/{phone} POST whatsapp/send POST whatsapp/toggle-bot phone
Telegram GET telegram/conversations GET telegram/messages/{chatId} POST telegram/send POST telegram/toggle-bot chat_id
Messenger GET messenger/conversations GET messenger/messages/{senderId} POST messenger/send POST messenger/toggle-bot sender_id
Instagram GET instagram/messages (mesajlar inline döner) POST instagram/messages/send POST instagram/toggle-conversation-bot sender_id
X (Twitter) GET x/conversations GET x/conversations/{conversationId}/messages POST x/reply POST x/conversations/toggle-bot conversation_id
TikTok (yorum) GET tiktok/conversations GET tiktok/conversations/{videoId}/{openId}/messages POST tiktok/reply POST tiktok/toggle-bot videoId+openId
TikTok (DM) GET tiktok/dm/conversations GET tiktok/dm/conversations/{openId}/messages POST tiktok/dm/toggle-bot openId
Mail GET mail/accounts/{id}/messages GET mail/messages/{id}/thread POST mail/reply POST mail/toggle-bot hesap + thread
Web Widget GET widget-chats GET sessions/{session} POST sessions/{session}/reply (yok) session
Google Yorumlar GET google-reviews GET google-reviews/{id} POST google-reviews/{id}/reply POST google-reviews/{id}/toggle-bot review id

Gönderme gövdesi kanala göre kimlik alanını değiştirir, gerisi aynı kalır:

// Telegram
POST /api/telegram/send       { "chat_id": "123456789", "profile_id": 7, "message": "..." }
// Messenger
POST /api/messenger/send      { "sender_id": "...", "profile_id": 3, "message": "..." }
// Instagram
POST /api/instagram/messages/send  { "sender_id": "...", "message": "..." }
// X
POST /api/x/reply             { "conversation_id": "...", "message": "..." }
// Mail
POST /api/mail/reply          { "message_id": 88, "body": "...", "subject": "..." }

Profil kurulumu her kanalda benzer: GET/POST {kanal}/profiles + .../link-site + OAuth (Instagram/Messenger/TikTok/X/Google → {kanal}/oauth/config|save). Bunlar genelde panel UI'ından yapılır. Tam alanlar API Dokümantasyonu'nda.

Ortak: AI ile mesaj iyileştirme & medya yükleme

POST /api/ai-improve-message          { "channel": "whatsapp", "text": "taslak metin" }   # kanal-aware
POST /api/sites/{site}/ai-improve-message  { "text": "..." }                              # site-explicit
#   → taslağı kanala uygun, kibar versiyona çevirir (20/dk)

POST /api/media/upload   (multipart)  → { "id": 555 }   # sonra send'de media_file_id olarak kullan
GET  /api/media
DELETE /api/media/{id}

4. Rehber & Toplu Gönderim

Ne işe yarar: Kişi/grup yönetimi (CRM) + WhatsApp şablonuyla toplu/zamanlı gönderim.

Gruplar & kişiler

GET    /api/contacts/groups
POST   /api/contacts/groups                       { "name": "VIP Müşteriler" }
PUT    /api/contacts/groups/{id}
DELETE /api/contacts/groups/{id}
GET    /api/contacts/groups/{id}/contacts
POST   /api/contacts/groups/{id}/contacts         { "name": "...", "phone": "+90..." }
PUT    /api/contacts/{id}                         # kişi güncelle (ai_instructions dahil — bkz. not)
POST   /api/contacts/delete                       { "ids": [1,2,3] }
DELETE /api/contacts/groups/{id}/contacts         # grubu boşalt — TÜM kişileri tek istekte siler (grup kalır, geri alınamaz)
POST   /api/contacts/groups/{id}/import           (CSV/çoklu)
POST   /api/contacts/quick-add                    # sohbet panelinden hızlı rehbere ekle
GET    /api/contacts/lookup-by-channel            # ?channel=whatsapp&identifier=+90...

Dış URL'den kişi çek (CSV/JSON) — import-url

Kendi web siteniz / CRM'inizden kişi listesini DoWaba rehberine çekin.

POST /api/contacts/groups/import-url
{
  "name": "Web sitesi lead'leri",
  "url": "https://siteniz.com/api/leads.csv",   // CSV veya JSON döndüren PUBLIC adres
  "api_key": "gizli-anahtar",                    // opsiyonel
  "auth_style": "bearer",                        // bearer | x-api-key | query
  "auto_refresh": false                          // true → her kampanyada yeniden çekilir
}

Akıllı segment (yalnız superadmin): POST /api/contacts/segments/owner_reseller/sync → tüm owner+bayi üyeleri (bayi müşterileri & sub-user hariç), kampanya başlarken canlı kullanıcı sorgusundan tazelenir. Diğer kullanıcılara kapalıdır (403); grup yalnız superadmin'in rehberinde görünür.

Müşteri-bazlı AI talimatı: Bir kişiye özel bot davranışı vermek için PUT /api/conversation-ai-overrides { site_id, channel, identifier, instructions, is_active } (9 kanalda geçerli; aynı kişi WhatsApp/IG/Mail'den yazsa da aynı talimat uygulanır).

Toplu WhatsApp gönderimi (şablonla)

POST /api/contacts/groups/{id}/send-template
{ "template_name": "kampanya_haziran", "site_id": 12, "params": { ... } }

⚠️ Uyum: Bu anlık toplu gönderim de scheduled-jobs gibi opt-out + İYS reddini otomatik filtreler (red'li alıcı atlanır; yanıtta skipped + skipped_samples döner). Yine de pazarlama içerikse alıcıların İYS onayını önceden almış olmalısın (§ 0.6).

Zamanlanmış işler

GET    /api/scheduled-jobs
POST   /api/scheduled-jobs                        # zamanlı toplu gönderim tanımı
POST   /api/scheduled-jobs/{id}/toggle
DELETE /api/scheduled-jobs/{id}
GET    /api/scheduled-jobs/whatsapp-stats/{contactGroupId}

⚠️ scheduled-jobs ile gönderim opt-out + İYS reddini otomatik filtreler (§ 0.6). Onaysız/red listesindeki alıcı atlanır (skipped, kotadan düşmez).

Tek seferlik kampanyaya tarih (2026-06-11): schedule_type=once iken opsiyonel scheduled_at ("2026-06-12 14:00", Europe/Istanbul) gönderilebilir — kampanya o anda başlar. Boş bırakılırsa (veya geçmiş tarihse) hemen başlar. hourly/daily tiplerinde yok sayılır.

İYS onay yönetimi (toplu gönderimden önce zorunlu)

İYS (İleti Yönetim Sistemi) onaylarını yükle/sorgula. Toplu pazarlama göndermeden önce gerekli — § 0.6.

Bu endpoint'ler iys modülü gerektirir (allowed_modules). İYS şu an STUB modda olabilir (summary.stub_mode=true) → gerçek İYS API'sine yazma için hesap credential'ı + production mod gerekir.

GET    /api/iys/consents?site_id=12          # kayıtlı onaylar (data, total, sayfalı)
GET    /api/iys/consents/summary             # { stub_mode, counts }
GET    /api/iys/consents/meta                # { sources, types, recipient_types } — geçerli enum değerleri
POST   /api/iys/consents                     # tekil onay kaydı
POST   /api/iys/consents/attest-group        # bir kişi grubuna toplu onay beyanı
DELETE /api/iys/consents/{id}

Tekil onay kaydı:

POST /api/iys/consents
{
  "site_id": 12,
  "recipient": "+905551112233",            // telefon (MESAJ/ARAMA) veya e-posta (EPOSTA)
  "type": "MESAJ",                         // MESAJ | EPOSTA | ARAMA
  "status": "ONAY",                        // ONAY | RET (varsayılan ONAY)
  "source": "HS_WEB",                      // izin kaynağı — geçerli değerler /iys/consents/meta'da
  "recipient_type": "BIREYSEL",            // BIREYSEL | TACIR
  "consent_date": "2026-06-01 10:00:00",   // iznin alındığı GERÇEK an (gelecek tarih reddedilir)
  "consent_confirmed": true                // ZORUNLU — onayın gerçekliğini beyan edersin
}

İYS hesabı (credential) yönetimi:

GET    /api/iys/accounts
POST   /api/iys/accounts   { "iys_code":"...", "brand_code":"...", "iys_username":"...", "iys_password":"...", "environment":"production" }
PUT    /api/iys/accounts/{id}
DELETE /api/iys/accounts/{id}

environment: sandbox | production. Credential'lar şifreli saklanır, yanıtta asla açık dönmez.


5. Şablonlar & Kampanyalar

WhatsApp şablonları (Meta onaylı)

GET    /api/whatsapp/templates
POST   /api/whatsapp/templates           # şablon oluştur (Meta'ya gider)
PUT    /api/whatsapp/templates/{id}
POST   /api/whatsapp/templates/add-optout-button  # şablona otomatik QUICK_REPLY "Durdur" ekler → Meta re-review (PENDING); alıcı butona basınca opt-out suppression
DELETE /api/whatsapp/templates/{name}
POST   /api/whatsapp/upload-header-image
GET    /api/whatsapp/db-columns          # şablon değişkenleri için sütun listesi

Mail şablonları (AI üretimli + manuel)

GET    /api/mail-templates
POST   /api/mail-templates
POST   /api/mail-templates/generate      { "prompt": "..." }   # AI ile şablon üret
POST   /api/mail-templates/{id}/preview
POST   /api/mail-templates/{id}/send-test

Mail kampanyaları (zamanlanmış toplu gönderim)

GET    /api/mail-campaigns
POST   /api/mail-campaigns
POST   /api/mail-campaigns/{id}/start | /pause | /resume | /retry-failed
GET    /api/mail-campaigns/{id}/logs
GET    /api/mail-campaigns/{id}/preview-recipients

Çağrı Kampanyası (toplu sesli AI araması — 2026-06-13)

Ne işe yarar: Rehber grubundaki (CSV import dahil — contacts/groups/{id}/import, § 4) numaraları site'ın sesli botu sırayla arar; sonuç alıcı bazında izlenir. Menü: Kampanyalar → Çağrı Kampanyası.

GET    /api/voice-campaigns?status=running        # liste — status: draft|running|paused|completed|cancelled
POST   /api/voice-campaigns                       # { site_id, contact_group_id, name, initial_message?, prompt_override?, scheduled_at?, consent_source, consent_date, consent_confirmed: true }
GET    /api/voice-campaigns/{id}                  # detay + sayaçlar (total_completed/no_answer/failed/skipped)
PUT    /api/voice-campaigns/{id}                  # yalnız status=draft
DELETE /api/voice-campaigns/{id}                  # running/paused silinemez (önce cancel)
POST   /api/voice-campaigns/{id}/start | /pause | /resume | /cancel
POST   /api/voice-campaigns/{id}/retry-failed     # no_answer + failed → tekrar kuyruğa (skipped ASLA yeniden aranmaz)
GET    /api/voice-campaigns/{id}/recipients       # alıcılar — status: pending|calling|completed|no_answer|failed|skipped

5.5. Reklam Yönetimi (Meta Ads) — module:adManagement

Ne işe yarar: Kullanıcı kendi Meta reklam hesabını bir siteye bağlar; kampanya/görsel oluşturur, performansı izler ve sızıntı koruması kurallarını yönetir. Meta App Review / Advanced Access onayı sonrası 2026-07-15'te tüm kullanıcılara açıldı. Reklam bütçesi DoWaba kredisi değil, kullanıcının Meta hesabından harcanır.

GET    /api/ads/oauth/config                       # browser OAuth URL/config
GET    /api/ads/oauth/pending                      # callback sonrası seçim verisi
GET    /api/ads/accounts
POST   /api/ads/accounts                           # seçilen hesap + Sayfa + IG'yi siteye bağla
GET    /api/ads/accounts/{id}/edit-options
DELETE /api/ads/accounts/{id}

GET    /api/ads/studio/models
POST   /api/ads/studio/chat | /generate | /generate-set
GET    /api/ads/studio/creatives

GET    /api/ads/campaigns?site_id={site}
POST   /api/ads/campaigns/sync | /shell
POST   /api/ads/campaigns                          # yeni kampanya; Meta'da PAUSED doğar
POST   /api/ads/campaigns/{id}/status | /budget
GET    /api/ads/audiences | /interests
POST   /api/ads/audiences

GET    /api/ads/insights?site_id={site}
GET    /api/ads/insights/ads?site_id={site}
GET    /api/ads/advisor?site_id={site}
POST   /api/ads/advisor | /advisor/apply
GET    /api/ads/guard | /guard/actions
PUT    /api/ads/guard
POST   /api/ads/guard/actions/{id}/apply | /dismiss
GET    /api/ads/catalogs?site_id={site}             # Commerce Manager deep-link
GET    /api/ads/pixels?site_id={site}
POST   /api/ads/pixels

Tam alan/payload listesi için Scribe'daki Reklam Yönetimi gruplarını kullan; Meta wire kuralları için meta-ads.md tek otoritedir.


6. Voice / Çağrılar + Outbound

SIP trunk ve çağrı gizlilik politikası

GET    /api/sites/{site}/sip-trunk
PUT    /api/sites/{site}/sip-trunk
DELETE /api/sites/{site}/sip-trunk
POST   /api/sites/{site}/sip-trunk/test

GET yanıtındaki privacy alanı trunk henüz kurulmamışken de site politikasını döndürür. PUT trunk alanlarına ek olarak şu site-bazlı alanları kabul eder:

{
  "voice_privacy_mode": "recorded",
  "voice_legal_basis": "explicit_consent",
  "voice_controller_name": "Örnek İşletme AŞ",
  "voice_privacy_contact": "kvkk@example.com",
  "voice_retention_days": 90,
  "voice_controller_attested": true
}

Çağrı geçmişi (site bazlı)

GET  /api/sites/{site}/voice-conversations
GET  /api/voice-conversations/{id}
GET  /api/voice-conversations/{id}/recording-url     # imzalı kayıt URL'i
POST /api/voice-conversations/{id}/clean             # transkripti AI ile yeniden temizle

Dışarı arama (outbound call)

POST /api/voice/call         { "site_id": 12, "phone": "+90...", ... }    # 5/dk

Bildirim aramaları & re-engagement mesajları (onay akışı)

GET  /api/sites/{site}/outbound-intents           # bekleyen sesli bildirim aramaları
POST /api/sites/{site}/outbound-intents           # manuel bildirim araması oluştur
POST /api/outbound-intents/{intent}/approve | /decline | /cancel

GET  /api/sites/{site}/outbound-messages          # 24h re-engagement mesaj önerileri (WA/TG/Msgr/IG)
POST /api/sites/{site}/outbound-messages          # manuel
POST /api/outbound-messages/{intent}/approve | /decline | /cancel
GET  /api/outbound-messages/all                   # tüm sitelerin bekleyenleri (badge)

Kapasite Paketleri (2026-06-12)

Ne işe yarar: Yüksek hacimli telefon trafiği için site başına ek kapasite (eşzamanlı + günlük + aylık çağrı limitleri). Satın alma abonelik akışında (subscriptions/initiate, plans yanıtındaki capacity_plans planlarıyla); atama buradan:

GET    /api/me/voice-capacity                 # sahip olunan kapasite paketleri + atandıkları site
GET    /api/sites/{site}/voice-capacity       # sitenin paketi + anlık eşzamanlı / bugünkü çağrı sayacı
POST   /api/sites/{site}/voice-capacity       # { "subscription_id": 41 } — paket tek siteye atanır
DELETE /api/sites/{site}/voice-capacity       # atamayı kaldır (site default 20 eşzamanlıya döner)

Santral / Çağrı Aktarımı (2026-06-12)

Ne işe yarar: Telefonu AI karşılar; arayan "teknik ekiple görüşmek istiyorum" deyince AI onay alır ve çağrıyı aktarım rehberindeki kişinin telefonuna canlı bağlar. AI serbest numara çeviremez — yalnızca rehberdeki kişiler (sunucu tarafında da doğrulanır).

GET    /api/sites/{site}/transfer-targets              # rehber + enabled (opt-in) durumu
POST   /api/sites/{site}/transfer-targets              # { "name": "Ahmet Yılmaz", "department": "Teknik Ekip", "phone": "5551112233", "is_active": true }
PUT    /api/sites/{site}/transfer-targets/{target}     # kısmi güncelleme
DELETE /api/sites/{site}/transfer-targets/{target}

7. Müşteri Talepleri (Callback)

Ne işe yarar: AI "sizi geri arayalım" dediğinde / müşteri talep bıraktığında oluşan kayıtlar.

GET /api/callback-requests
PUT /api/callback-requests/{id}/status        { "status": "done" }
PUT /api/callback-requests/{id}/note          { "note": "..." }
POST /api/callback-requests/{id}/beautify-note   # notu AI ile düzelt

Talep atama (2026-06-11)

Talepler site ekibindeki bir kullanıcıya atanabilir; istenirse yeni talepler ekipteki agent rolündeki kullanıcılara en az açık talebi olana otomatik dağıtılır.

GET  /api/callback-requests/assignment-options?site_id=12   # atanabilir ekip + açık talep sayıları + auto_assign durumu
POST /api/callback-requests/{id}/assign       { "user_id": 17 }   # null → atamayı kaldır
PUT  /api/callback-requests/auto-assign       { "site_id": 12, "enabled": true }

7.5. Potansiyel Müşteriler (Leads / CRM) — module:leads

Ne işe yarar: Konuşmalardan / talep / lead-ads kaynaklarından oluşan satış adayları (CRM pipeline: sürüklenebilir aşamalar + ekip ataması). Yalnız KENDİ sitelerinin lead'leri görünür (bayi, müşteri sitelerinin lead'lerini göremez — mesaj/rehber gizlilik kilidiyle tutarlı). module:leads gerektirir.

GET    /api/leads                  # liste (site/aşama filtreli)
POST   /api/leads                  # manuel aday ekle (kaynak: manuel/öneri/talep/lead-ads)
POST   /api/leads/inbox            # Mesaj Kutusu konuşmasından aday ekle (channel + identifier)
GET    /api/leads/inbox-status     # konuşma zaten lead mi? (yazmaz — idempotent probe)
POST   /api/leads/scan             # konuşmaları AI ile tara → aday çıkar (10/dk)
POST   /api/leads/{id}/analyze     # tek adayı AI ile analiz et (10/dk)
GET    /api/leads/{id}/activities  # değişiklik geçmişi: kim ne yaptı (created/stage_changed/assigned/updated)
PATCH  /api/leads/{id}/stage       # pipeline aşaması değiştir   { "stage": "..." }
POST   /api/leads/{id}/assign      # ekip üyesine ata   { "user_id": 17 }   (null → kaldır)
POST   /api/leads/reorder          # sütun içi sıralama (sürükle)
PATCH  /api/leads/{id}             # güncelle (ad / telefon / not ...)
DELETE /api/leads/{id}             # sil
GET    /api/lead-submissions       # Lead Ads form yanıtları (Meta/Facebook lead formları — ham gönderimler)

Lead kaynakları: manuel · konuşma taraması (scan) · Müşteri Talepleri · Lead Ads (Facebook lead formu → POST facebook-page-profiles/{id}/sync-leads, module:publishinglead-submissions + otomatik lead). Alan (body) listeleri için /api-docs (Scribe) → "Potansiyel Müşteriler (Leads)" grubuna bak.

Değişiklik geçmişi: lead yazma uçları (ekleme / düzenleme / aşama / atama) her değişiklikte otomatik geçmiş kaydı üretir; GET /api/leads/{id}/activities en yeni üstte döner. Kayıt: action, actor_name (sistem işlemlerinde null), actor_role (owner | agent | subuser | system ...), alan bazlı changes { alan: {from, to} } (atamada okuma anında çözülen from_label/to_label da eklenir). Sürükleme sırası (reorder) bilinçli olarak geçmişe yazılmaz.


8. Bilgi Tabanı (SSS + Belgeler)

Sitenin AI'ının cevap verirken kullandığı bilgi. Hepsi sites/{site}/... altında.

SSS (FAQ)

GET    /api/sites/{site}/faqs
POST   /api/sites/{site}/faqs                 { "question": "...", "answer": "..." }
PUT    /api/faqs/{faq}
DELETE /api/faqs/{faq}
POST   /api/sites/{site}/faqs/batch           # toplu EKLE (yeni SSS'ler)
POST   /api/sites/{site}/faqs/batch-update    { "updates": [{ "id": 12, "question": "...", "answer": "..." }] }   # toplu DEĞİŞTİR (var olanları)
POST   /api/faqs/{faq}/ai-rewrite             { "instruction": "5551234567 numarasını kaldır" }   # tek SSS'i AI talimatla yeniden yaz
POST   /api/sites/{site}/faqs/extract-from-text   { "text": "..." }    # metinden SSS çıkar (AI)
POST   /api/sites/{site}/faqs/extract-from-image  (görselden)
DELETE /api/sites/{site}/faqs/delete-all

Belgeler

GET    /api/sites/{site}/documents
POST   /api/sites/{site}/documents            (yükle)
POST   /api/documents/{document}/process      # parse + embedding
DELETE /api/documents/{document}

Fonksiyon Gateway (AI'ın çağırdığı dış fonksiyonlar — ileri seviye)

GET  /api/sites/{site}/functions
POST /api/sites/{site}/functions              # DB sorgusu veya HTTP API fonksiyonu tanımla
POST /api/functions/{function}/test
POST /api/functions/{function}/toggle         { "site_id": 159, "is_active": false }
# is_active hedef durumu zorunludur (idempotent). Global fonksiyonda site_id de zorunludur;
# merkezi tanım kopyalanmaz/değişmez, yalnız hedef sitenin izole disable listesi güncellenir.
# Siteye özel fonksiyonda site_id gerekmez. Yasal/lifecycle korumalı sistem fonksiyonları 422 döner.
# Harici bağlantılar (DB/HTTP):
GET  /api/sites/{site}/connections
POST /api/sites/{site}/connections
POST /api/connections/{connection}/generate-functions        # tablodan otomatik fonksiyon üret (AI)
# Hazır modül paketi yükle — connection + TÜM fonksiyonlar tek seferde:
POST /api/sites/{site}/bundles/import   { "manifest": { ...JSON... }, "api_key": "<secret>", "replace": true }
#   veya dış URL'den:                   { "manifest_url": "https://...",  "api_key": "<secret>" }
#   → "manifest" (JSON gövde) ÖNERİLİR: dış URL fetch YOK = SSRF yüzeyi sıfır. manifest|manifest_url biri zorunlu.
#     api_key = connection bearer/api_key sırrı; replace=true mevcut paketi günceller (idempotent re-sync).
#
# Fonksiyon şablon değişkenleri  (url_template / body_template / headers içinde {{...}}):
#   {{arg.X}}                       → AI'ın doldurduğu fonksiyon argümanı (model/kullanıcı girdisi → DOĞRULANMAMIŞ)
#   {{connection.base_url}} ...     → connection'ın kayıtlı (şifreli) değerleri
#   {{user.id|phone|name|email}}    → DOĞRULANMIŞ son-kullanıcı kimliği: web widget'ta imzalı data-user-token'dan
#                                     çözülen kullanıcı, WhatsApp/voice'ta Meta-imzalı telefon. Sistem otomatik
#                                     enjekte eder → AI/kullanıcı SPOOF EDEMEZ. "Giriş yapanın KENDİ verisi"
#                                     isteyen fonksiyonlarda (siparişlerim, derslerim, şifre-sıfırlama) kimliği
#                                     {{arg.*}} parametresi YAPMA → body_template'te {{user.id}}/{{user.phone}}
#                                     kullan. Anonim kullanıcıda boş gelir (istek o alan olmadan gider).

9. Kullanım, Krediler & Abonelik

Kullanım & kredi özeti (panelin "Kullanım" sayfası)

GET /api/me/usage                 # birleşik mesaj+voice snapshot (dashboard)
GET /api/usage                    # detay
GET /api/usage/stats
GET /api/credits                  # bakiye
GET /api/credits/transactions
GET /api/me/credit-summary        # header badge için rol-aware özet

Abonelik

GET  /api/subscriptions/plans
GET  /api/subscriptions/current
GET  /api/subscriptions/usage
GET  /api/subscriptions/price-quote        # ?plan_id=&sites_count= → fiyat
POST /api/subscriptions/initiate           { "plan_id": "business", "sites_count": 5 }
POST /api/subscriptions/change-sites
# Ödeme: PayTR (TR), Stripe (USD, otomatik webhook), PayPal (USD, manuel onay)
POST /api/subscriptions/stripe/checkout
POST /api/subscriptions/paypal/initiate

AI Mesaj Paketi (Gemini kredi cüzdanı — anahtarsız kullanıcı)

GET  /api/ai-credit/catalog
GET  /api/ai-credit/summary               # bakiye
GET  /api/ai-credit/transactions
POST /api/ai-credit/stripe/checkout | /paytr/checkout | /paypal/initiate

Cüzdan neyi fonlar: Kullanıcının (veya bayisinin) kendi Gemini anahtarı yoksa AI maliyetleri bu cüzdandan düşülür — yalnız sohbet mesajları (tüm kanallar) değil, sesli çağrılar (AI'ın karşıladığı telefon, 2026-06-14'ten itibaren) ve canlı çeviri (telefon aktarımında çapraz dil) de. transactions yanıtındaki source alanı kalemi ayırır (ör. voice_conversation, voice_translate). Maliyet gerçek token kullanımından (Gemini usageMetadata) hesaplanır × kâr marjı; sesli kalem metinden pahalıdır (ses çıkışı yüksek oranlı). Bakiye biterse AI yanıt veremez → kullanıcı paket satın almalı (catalogcheckout).


10. Geliştirici paneli (self-service)

Ne işe yarar: Kullanıcının kendi API anahtarı (Sanctum PAT), outbound webhook ve OAuth client'ı. Senin trusted-partner client'ından AYRIDIR — bu, kullanıcının panelden kendi entegrasyonunu açması içindir.

⚠️ Bu endpoint'lere erişim için kullanıcının developer modülü açık olmalı (allowed_modules). Ayrıca token/webhook/oauth oluşturmadan önce geliştirici şartları kabulü zorunlu: GET /api/me/developer-termsPOST /api/me/developer-terms/accept.

Kişisel API anahtarları (Sanctum PAT)

GET    /api/me/tokens
POST   /api/me/tokens          { "name": "Entegrasyonum" }     # 10/dk — plain token bir kez döner
DELETE /api/me/tokens/{id}

Outbound webhook (DoWaba → senin sunucun)

GET    /api/webhook-endpoints
POST   /api/webhook-endpoints  { "url": "https://...", "events": ["message.received"] }   # 20/dk
PATCH  /api/webhook-endpoints/{id}
POST   /api/webhook-endpoints/{id}/test
GET    /api/webhook-endpoints/{id}/deliveries        # gönderim logları
DELETE /api/webhook-endpoints/{id}

Tek event adı message.received'dır ve tüm gelen-mesaj kanallarında tetiklenir (2026-07-24+): WhatsApp (Cloud API + QR + Coexistence), Instagram DM, Messenger, Telegram, TikTok DM, X DM, Mail, site Widget'ı. Kanalı data.channel alanından ayırt et (whatsapp|instagram|messenger|telegram|tiktok|x|mail|widget); data.from kanala göre telefon / platform kullanıcı ID'si / e-posta / widget oturum ID'sidir. Mail ve X-sync geçmiş taramalarında 48 saatten eski mesajlar webhook tetiklemez (backfill koruması).

İmza: gelen webhook'larda X-Dowaba-Signature (HMAC-SHA256) doğrula. ⚠️ Payload müşteri PII'si içerir (telefon/ad/mesaj metni) — varsayılan maskelidir (masked=true: from kısaltılır, ad/metin null); tam içerik yalnız DPA onaylı full_payload=true endpoint'lerde gelir ve bu veriyi yalnız veri sahibine hizmet için kullanabilirsin (§ 0.5).

OAuth client (kendi "Login with DoWaba" entegrasyonu)

GET    /api/me/oauth/clients
POST   /api/me/oauth/clients   # issues_sanctum_token gövdeden AÇILAMAZ (aşağıdaki ayrı uç)
PATCH  /api/me/oauth/clients/{id}
POST   /api/me/oauth/clients/{id}/rotate-secret
DELETE /api/me/oauth/clients/{id}

Güvenilir Ortak (SSO) self-servis — bayiler için

Bayiysen kendi uygulamanı (yan uygulama / white-label panel) kendin Güvenilir Ortak yapabilirsin: kullanıcı senin uygulamandan "DoWaba ile giriş" yapar, token yanıtında standart access_token'ın yanında sanctum_token da gelir ve bu token ile panel API'larını kullanıcı adına çağırırsın (§ 0).

POST   /api/me/oauth/clients/{id}/trusted-partner     # gövde: {"commitment": true}
DELETE /api/me/oauth/clients/{id}/trusted-partner     # kapat

Açılış koşulları (hepsi zorunlu; GET /api/me/oauth/clients yanıtındaki partner_sso.requirements bloğu hangisinin eksik olduğunu gösterir):

  1. Bayi hesabı olmak, 2. aktif ve ödenmiş bayilik aboneliği, 3. güncel bayilik sözleşmelerinin onaylı olması, 4. geliştirici şartlarının kabulü, 5. uygulamanın confidential (sunucu tarafı, secret'lı) ve aktif olması, 6. TP-açık uygulama tavanını (partner_sso.max_trusted_clients) aşmamak, 7. commitment: true ile veri işleme taahhüdünü onaylamak.

Kapsam kilidi: Self-servis açılan uygulamada kapsam daima tenant'tır — bu uygulamaya yalnız senin kendi evrenin (sen + müşterilerin + alt kullanıcıların) giriş yapabilir. Evren dışındaki bir kullanıcı consent ekranında engellenir (out_of_scope_reason=tenant_scope), superadmin hesapları her durumda engellenir. Bayilik aboneliğin/sözleşmen düşerse uygulamana yeni giriş yapılamaz (owner_gate).

Kapatınca ne olur: Uygulamaya ait tüm OAuth token'ları iptal edilir ve bağlı sanctum_token'lar silinir — kullanıcılar yeniden giriş yapmak zorunda kalır. Bayilik aboneliğin/sözleşmen düşerse aynı temizlik otomatik yapılır (günlük partner-sso:sync): uygulaman Güvenilir Ortak modundan çıkar, mevcut sanctum_token'lar da silinir.

Panelden: Geliştirici → OAuth Uygulamaları → ilgili uygulama kartı → "Güvenilir Ortak (SSO)". Panel giriş linki için POST /api/oauth/login-link (§ Trusted Partner SSO).

Giriş ekranını kendi domain'inde açtırma (white-label)

Kendi doğrulanmış özel domain'in varsa (Bayilik → Özel Domain), giriş + izin ekranı senin adresinde açılabilir — kullanıcı URL çubuğunda hep senin markanı görür:

PATCH /api/me/oauth/clients/{id}/branded-host   # gövde: {"host": "destek.firma.com"} — temizlemek için {"host": null}

Yalnız kendi yayındaki (active) domain'lerinden biri seçilebilir; seçilebilir liste GET /api/me/oauth/clients yanıtındaki partner_sso.available_branded_hosts alanındadır. Ayarlandıktan sonra https://dowaba.com/oauth/authorize?... isteği aynı sorgu parametreleriyle https://destek.firma.com/oauth/authorize?... adresine 302 ile taşınır — istemci tarafında yapman gereken bir şey yok (redirect_uri, PKCE, state aynen korunur, iss yine https://dowaba.com). Domain yayından kalkarsa yönlendirme durur, akış dowaba.com üzerinden sorunsuz sürer.

Kullanıcı bir kez onaylar (consent-once)

Kullanıcı uygulamana ilk girişte izin kartını görür; onayı kaydedilir ve sonraki girişlerde kart atlanır. GET /api/oauth/authorize/state yanıtındaki has_prior_consent bunu bildirir. İstediğin scope setini genişletirsen kart yeniden gösterilir. Kullanıcı izni istediği an geri çekebilir (panel → Bağlı uygulamalar; DELETE /api/me/oauth/consents/{clientId}) — o kullanıcının sanctum_token'ı ve OAuth token'ları anında geçersiz olur, uygulaman 401 alır ve kullanıcıyı yeniden "DoWaba ile giriş"e yönlendirmelidir.

🤖 Yapay zekaya yazdır: hazır entegrasyon prompt'u

Entegrasyonu elle kodlamak istemiyorsan aşağıdaki prompt'u olduğu gibi kopyala, en üstteki köşeli parantezli alanları kendi bilgilerinle doldur ve kullandığın yapay zeka asistanına (Claude, ChatGPT, Cursor, Copilot...) yapıştır. Prompt, asistanın senin uygulamana/CRM'ine uçtan uca çalışan bir "DoWaba ile Giriş" entegrasyonu yazması için gereken tüm teknik gerçekleri içerir — asistanın DoWaba'yı önceden tanımasına gerek yoktur.

Ön koşul: Panel → Geliştirici → OAuth Uygulamaları'ndan client'ını oluştur (Confidential işaretli), client_id + client_secret'ı kopyala ve Güvenilir Ortak (SSO) modunu aç (yukarıdaki bölüm). İstersen "giriş adresi" olarak kendi özel domain'ini seç — kullanıcıların gördüğü login ekranı senin markanla açılır.

Uygulamama "DoWaba ile Giriş" (OAuth 2.0 + OIDC, Güvenilir Ortak modu) entegre etmeni
istiyorum. Aşağıda sağlayıcının TÜM teknik detayları var — bunlara birebir uy, uydurma.

BENİM BİLGİLERİM (doldur):
- Uygulamam / stack: [ör. Laravel 11 + Blade / Node.js Express + React / Django ...]
- Uygulamamın adresi: [https://uygulamam.com]
- client_id: [dosc_...]
- client_secret: [dosec_...]  ← YALNIZ sunucu tarafında kullanılacak
- redirect_uri (DoWaba panelinde kayıtlı olanla BİREBİR aynı): [https://uygulamam.com/oauth/dowaba/callback]
- Giriş adresi (white-label seçtiysem, yoksa dowaba.com): [https://hesap.markam.com veya https://dowaba.com]

SAĞLAYICI GERÇEKLERİ (DoWaba):
1. Authorize (tarayıcı yönlendirmesi): GET {GİRİŞ_ADRESİ}/oauth/authorize
   Zorunlu query parametreleri: client_id, redirect_uri, response_type=code,
   scope="openid profile email", state (CSRF için rastgele, session'da sakla ve
   callback'te doğrula), code_challenge, code_challenge_method=S256.
   PKCE ZORUNLUDUR: 64+ karakterlik rastgele code_verifier üret; challenge =
   base64url(sha256(verifier)) (padding'siz). Verifier'ı session'da tut.
2. Token (SUNUCUDAN sunucuya): POST https://dowaba.com/api/oauth/token
   Body (JSON veya form): grant_type=authorization_code, client_id, client_secret,
   code (callback'teki ?code=), redirect_uri (aynısı), code_verifier.
   Başarılı yanıt alanları:
     access_token  ("doat_..." — 1 saat ömürlü, yalnız userinfo için)
     id_token      (RS256 imzalı JWT; doğrulama anahtarı JWKS'te)
     expires_in    (3600)
     refresh_token ("dort_..." — dönerse sakla; rotation vardır: her kullanımda
                    YENİSİ döner, eskisini bir daha KULLANMA — tekrar kullanım tüm
                    token ailesini iptal eder)
     sanctum_token ("12|..." — GÜVENİLİR ORTAK ANAHTARI: süresizdir, kullanıcı
                    adına DoWaba panel API'larını çağırır. ASIL kalıcı erişim budur.)
   sanctum_token'ı kullanıcı kaydında ŞİFRELİ sakla; tarayıcıya/frontend'e ASLA verme.
3. Kimlik: GET https://dowaba.com/api/oauth/userinfo
   Header: Authorization: Bearer {access_token} → { sub, email, name, ... } döner.
   sub = DoWaba kullanıcı ID'si; kendi tablomda kullanıcıyı "dowaba_user_id = sub" ile
   eşle (Trusted Partner deseninde kendi şifre/kimlik sistemimi KURMAM — kullanıcı
   havuzu DoWaba'dadır; bende yalnız sub + sanctum_token + profil kopyası durur).
4. Kullanıcı adına DoWaba API'ları: Authorization: Bearer {sanctum_token} ile
   https://dowaba.com/api/... uçları (örn. GET /api/me, GET /api/sites,
   GET /api/conversations). Yetki otomatik olarak kullanıcının kendi kapsamıdır;
   BAŞKA kullanıcının verisine erişilemez. Not: kullanıcı bir bayinin müşterisiyse
   mesaj İÇERİĞİ uçları bu anahtarla kapalıdır (403/boş) — site/profil yönetimi çalışır.
   Tüm uçların dokümanı: https://dowaba.com/api-docs/ ve https://dowaba.com/gelistirici-rehberi
5. Panele geçiş (SSO): POST https://dowaba.com/api/oauth/login-link
   Header: Authorization: Bearer {sanctum_token} → {"url": "..."} tek kullanımlık,
   5 dk geçerli panel giriş linki döner. Uygulamama "Panele Git" butonu koy, bu URL'e
   yönlendir (her tıklamada YENİ link iste, cache'leme).
6. Çıkış / bağlantıyı kesme: POST https://dowaba.com/api/oauth/revoke
   Body: client_id, client_secret, token (refresh_token ya da access_token).
   Revoke sanctum_token'ı da siler. Kendi session'ımı da temizle.
7. Consent-once: kullanıcı izni İLK girişte bir kez onaylar, sonraki girişler kartsız
   akar. Kullanıcı izni DoWaba panelinden geri çekebilir → elimdeki sanctum_token
   401 dönmeye başlar. HER 401'de: kayıtlı token'ı sil, kullanıcıyı yeniden
   authorize akışına gönder (otomatik dene; kullanıcı hâlâ yetkiliyse kartsız geçer).
8. Kapsam (tenant): Bu client'a YALNIZ benim DoWaba bayi evrenimdeki kullanıcılar
   (ben + müşterilerim + alt kullanıcılarım) giriş yapabilir. Kapsam dışı kullanıcı
   consent ekranında "bu uygulama size açık değil" görür; uygulamam callback'te
   ?error=access_denied alabilir → kibar bir hata sayfası göster.
9. Hata durumları: callback'te ?error= gelirse (access_denied vb.) token isteği
   YAPMA, hata sayfası göster. Token endpoint'i 4xx dönerse response body'deki
   error alanını logla. state uyuşmazsa isteği REDDET (CSRF).

YAPMANI İSTEDİKLERİM:
a) /oauth/dowaba/redirect (giriş başlatma: PKCE + state üret, authorize URL'ine 302)
   ve /oauth/dowaba/callback (state doğrula, kodu token'a çevir, userinfo çek,
   kullanıcıyı upsert et, session aç) rotalarını yaz.
b) Kullanıcı tablosuna/koleksiyonuma dowaba_user_id (unique) + dowaba_sanctum_token
   (şifreli) + email + name alanlarını ekleyen migration/şema değişikliği.
c) "DoWaba ile Giriş" butonu + "Panele Git" (login-link) butonu + "Bağlantıyı kes"
   (revoke + yerel çıkış) akışı.
d) sanctum_token ile DoWaba API çağrısı yapan, 401'de token'ı temizleyip yeniden
   yetkilendirmeye yönlendiren küçük bir API istemci sınıfı/yardımcısı.
e) Güvenlik kuralları: client_secret ve sanctum_token yalnız sunucuda; tüm istekler
   HTTPS; state + PKCE doğrulaması atlanamaz; token'lar loglanmaz.
f) Sonunda elle test adımlarını listele (giriş → panel API çağrısı → login-link →
   izin geri çekme → 401 kurtarma).

Kodun tamamını benim stack'ime uygun, çalışır halde yaz. Bilmediğin bir davranışı
varsayma — yukarıdaki gerçeklerle çelişen hiçbir şey ekleme.

Prompt'taki akış klasik "Authorization Code + PKCE" olduğu için asistanın üreteceği kod standart OAuth kütüphaneleriyle de (Laravel Socialite custom provider, openid-client, authlib...) uyumludur — asistan kütüphane kullanmayı seçerse endpoint'leri ve sanctum_token alanını yukarıdaki gibi elle eşlemesi yeterli.

MCP / ChatGPT — konuşarak sistem promptu, WhatsApp üslubu ve SSS hazırlama

Canonical MCP resource:

https://dowaba.com/mcp

Bu yüzey REST panel API'si değildir; MCP Streamable HTTP + JSON-RPC kullanır. ChatGPT uygulamasında Dowaba hesabı OAuth Authorization Code + PKCE ile bağlanır. MCP client public/predefined'dır, resource=https://dowaba.com/mcp ile audience-bound token alır ve Trusted Partner sanctum_token kullanmaz.

Akış:

  1. Kullanıcının bizzat sahibi olduğu düzenlenebilir siteler listelenir.
  2. Mevcut sistem promptu, WhatsApp promptu ve sayfalı yerel SSS okunur.
  3. expected_revision + idempotency_key ile canlı olmayan taslak oluşturulur.
  4. Tam fark kullanıcıya gösterilir; bu preview çağrısı server tarafında damgalanır.
  5. İşletme gerçekleri kullanıcı tarafından doğrulanmışsa ve kullanıcı açıkça “yayınla” derse prompt + en fazla 25 SSS değişikliği atomik uygulanır.
  6. Geri alma istenirse ayrı rollback-preview aracı tam geri alma farkını gösterir.
  7. Sonradan çakışan değişiklik yoksa aynı MCP yayını bu fark için verilen ayrı açık onayla geri alınabilir.

MCP mesaj/konuşma okumaz, mesaj göndermez, bot açıp kapatmaz, credential döndürmez ve site/kanal lifecycle'ına dokunmaz. ChatGPT belleği Dowaba'nın eriştiği bir API değildir; ChatGPT yalnız kullanıcının konuşmada gördüğü yapılandırılmış persona ve doğrulanmış SSS sonucunu gönderir. Ham bellek veya ham konuşma saklanmaz.

Discovery:

GET /.well-known/oauth-protected-resource/mcp
GET /.well-known/oauth-authorization-server
GET /.well-known/openai-apps-challenge

Araç adları, OAuth scope'ları, publish/rollback gate'leri ve üretim runbook'unun iç operasyon tek otoritesi repo kökündeki MCP.md dosyasıdır. Public entegrasyon desteği için aydin@dowaba.com ile iletişime geç.


11. Profil & Ayarlar

GET /api/auth/user                 # kimlik + role + allowed_modules (§0.3)
GET /api/user                      # profil detay
PUT /api/user                      # profil güncelle
PUT /api/user/locale               { "locale": "tr" }
PUT /api/user/menu-settings        # sol menü görünürlüğü

GET /api/settings                  # kullanıcı global ayarları (key-value)
PUT /api/settings
GET/PUT /api/settings/gemini-models    # AI model tercihi
GET/PUT /api/settings/whatsapp | /sms | /sip

12. Fatura & Bayi

Müşterinin kendi cari hesabı (read-only)

GET /api/me/billing                          # bayiden gelen borç + taksit + ödeme
GET /api/customer-invoices/{invoice}/data    # fatura JSON (PDF client-side üretilir)

Bayi paneli (sadece role=reseller)

GET  /api/reseller/summary                   # KPI kartları
GET  /api/reseller/customers
POST /api/reseller/customers                 { "name": "...", "email": "...", "phone": "+90..." }
GET  /api/reseller/customers/{user}
POST /api/reseller/customers/{user}/send-password-link   # müşteriye şifre kurulum bağlantısı (3/saat)
POST /api/reseller/customers/{user}/sites    # müşteri adına site + lisans
GET  /api/reseller/invoices                  # bekleyen/ödenmiş lisans faturaları
POST /api/reseller/customers/{user}/billing/invoices    # müşteriye fatura kes (cari hesap)
GET  /api/reseller/branding                  # white-label marka

⚠️ Müşteri şifresi (2026-06-11): POST/PUT /reseller/customers* artık password parametresi kabul etmez (gönderilirse yok sayılır). Müşteri şifresini, kendisine e-posta/SMS ile giden kurulum bağlantısıyla kendisi belirler (send-password-link; yeni müşteri oluşturmada otomatik gönderilir — yanıtta password_link_sent + password_link_channel). Bayi müşterisinin konuşma/mesaj içeriğine de erişemez (§ 0.2).

Bayilik sözleşme paketi — API davranışı (requires_reseller_agreement)

Bayi yazma uçları (müşteri ekleme, müşteriye site açma/atama/geri alma, kendi sitesini açma, DID yönlendirme yönetimi, bayilik planı satın alma) güncel bayilik sözleşme paketinin (3 belge) onaylı olmasını şart koşar. Okuma uçları sözleşme sormaz — listeler/raporlar kesilmez.

Sözleşme paketi güncellenirse (yeni sürüm yayınlanır ve yürürlük tarihi ileri çekilirse) eski onaylar geçersiz sayılır ve bu yazma uçları — panel de API de — yeniden onaya kadar şu yanıtı döner:

403 { "success": false, "requires_reseller_agreement": true, "message": "..." }

Entegrasyon tarafında yapılması gereken: 403 + requires_reseller_agreement: true yakalandığında işlemi kuyruklamayı bırakıp bayi kullanıcıyı onaya yönlendirin. İki yol:

  1. Panel: kullanıcı https://<panel>/reseller/agreement sayfasında tek tıkla onaylar (önerilen).
  2. API ile onay akışı (uygulamanız belgeleri kendisi gösterecekse):
GET  /api/reseller/agreement    # 3 belgenin metni + sha256 + accepted / requires_reacceptance
POST /api/reseller/agreement    { "accepted": true, "acknowledged_hashes": { "reseller-agreement": "<sha>", "reseller-mesafeli": "<sha>", "reseller-dpa": "<sha>" } }

⚠️ Onay bayinin bilinçli, insan eylemi olmalıdır — belgeleri kullanıcıya göstermeden arka planda otomatik POST etmeyin. Her kabul audit_logs'a sürüm + sha256 ile kanıt satırı yazar; onayın kim tarafından, hangi metne verildiği yasal delil zinciridir. Not: Referans Programı katılımı aynı ön koşulu tek akışta çözer (422 requires_reseller_bundle, aşağıdaki bölüm).

Referans Programı — Menü: Bayi > Referans Programı (role=reseller)

Ne işe yarar: Bayi, dowaba'ya müşteri yönlendirir; müşteri dowaba'ya öder, bayi hakediş alır (brütün %20'si). Toptan/white-label modelin TERSİ para yönü: müşteri dowaba'nın kendi müşterisidir, bayi onun panel/mesaj verisine erişemez.

Ön koşullar (API'de de geçerli): role=reseller + hesap aktif + bayilik sözleşme paketi (3 belge) güncel sürümle onaylı + programa katılım (opt-in). Sözleşme eksikse opt-in 422 requires_reseller_bundle döner ve belgeler aynı yanıtta gelir — ayrı sayfaya gitmeden, tek istekte onaylanır.

Panel sekmesi Endpoint(ler)
(durum/ön koşul) GET /api/reseller/referral
Programa Katıl POST /api/reseller/referral/opt-in · POST /api/reseller/referral/opt-out
Genel Bakış GET /api/reseller/referral · PATCH /api/reseller/referral/settings
Kanal Kodları POST /api/reseller/referral/codes · PATCH /api/reseller/referral/codes/{id}
Referans Paketlerim GET/POST /api/reseller/referral/packages · PATCH .../packages/{id} · GET .../packages/modules
Müşteriler & Hakedişler GET /api/reseller/referral/customers · GET /api/reseller/referral/commissions
Ödemeler GET/POST /api/reseller/referral/payouts · GET .../payouts/{id}/invoice
GET   /api/reseller/referral                  # durum + oranlar + sözleşme metinleri + kodlar + sayaçlar
POST  /api/reseller/referral/opt-in           { "accepted": true, "acknowledged_sha256": "<sha>" }
POST  /api/reseller/referral/opt-out          # yeni tahakkuk durur; paketler pasifleşir
PATCH /api/reseller/referral/settings         { "public_listed": true, "payout_iban": "TR33...", "payout_iban_holder": "..." }

POST  /api/reseller/referral/codes            { "label": "Instagram", "code": "INSTA2026" }   # link: /register?ref=KOD
PATCH /api/reseller/referral/codes/{id}       { "is_active": false }                          # silme YOK, pasifleştirme

GET   /api/reseller/referral/packages/modules # allowed_modules için geçerli anahtar seti
GET   /api/reseller/referral/packages         # paketler + sales sayaçları + limits (taban katsayıları)
POST  /api/reseller/referral/packages         { "name","message_quota","voice_quota","sites","allowed_modules"[],"prices"{},"page"{} }
PATCH /api/reseller/referral/packages/{id}    { "name"?, "is_active"?, "page"? }               # ÇEKİRDEK IMMUTABLE

GET   /api/reseller/referral/customers        # ad + tarih + abonelik durumu (KVKK-minimal, iletişim bilgisi YOK)
GET   /api/reseller/referral/commissions      # hakediş defteri (accrued/approved/paid/reversed)
GET   /api/reseller/referral/payouts          # ödeme talepleri/geçmişi
POST  /api/reseller/referral/payouts          # multipart: invoice=@fatura.pdf  VEYA  issue_einvoice=true
GET   /api/reseller/referral/payouts/{id}/invoice   # yüklenen faturayı indir (binary)

Sözleşme eksikse tek adımda katılım:

POST /api/reseller/referral/opt-in
{
  "accepted": true,
  "acknowledged_sha256": "<data.agreement.sha256>",
  "accept_reseller_bundle": true,
  "bundle_acknowledged_hashes": {
    "reseller-agreement": "<sha>", "reseller-mesafeli": "<sha>", "reseller-dpa": "<sha>"
  }
}

Hash'ler GET /api/reseller/referral yanıtındaki data.agreement.sha256 ve data.reseller_bundle[].sha256 alanlarından gelir. Metin bu arada güncellenmişse 409 stale_document alırsın → yeniden oku, yeni hash'le gönder.

Gizli satış paketi (partner paketi): Paket hiçbir fiyat listesinde görünmez; satış yalnız POST yanıtındaki data.sale_url (https://dowaba.com/paket/{uuid}) ile açılır. Müşteri o sayfadan önce öder, sonra şifresini belirler; dowaba'nın OWNER müşterisi olur (fatura dowaba'dan). Kota/fiyat/site/modül oluşturulduktan sonra değiştirilemez — değişiklik için yeni paket açıp eskisini is_active:false yap.

Taban fiyat (422 yemeden önce hesapla): aylık taban = mesaj × 0,20₺ + çağrı × 0,20₺ + (site − 1) × 150₺, dönem tabanı = aylık taban × çarpan (monthly 1 · quarterly 3 · semiannual 5 · yearly 10). Örn. 1.000 mesaj + 100 çağrı + 1 site → aylık 220₺, yıllık 2.200₺. Katsayılar GET .../packagesdata.limits.

Ödeme talebi: kısmi tutar YOK — birikmiş tüm hakediş tek talepte toplanır. Ön koşullar: IBAN kayıtlı + eşik (data.min_payout_try, varsayılan 500₺) aşılmış + açık talep yok + fatura (dosya veya kendi Nilvera hesabından issue_einvoice=true).

⚠️ Müşteri verisi sınırı: customers ucu yalnız ad + tarih + abonelik durumu döner. E-posta/telefon/site/konuşma bilinçli olarak yoktur ve bunları veren başka bir uç da yok (bayi müşterisi ≠ referans müşterisi). Tam alan listesi: API Dokümantasyonu → "Bayi — Referans Programı".

Özel Entegrasyonlar (2026-07-13) — Menü: "Özel Entegrasyonlar" + Bayi > Marka & Tanıtım > "Özel Entegrasyonlarım"

Onaylı bayi demo modülleri kataloğu (tüm kullanıcılar) + bayi ilan yönetimi (role=reseller ve aktif ödenmiş abonelik — yazma uçları abonelik pasifken 403 subscription_required):

GET  /api/custom-integrations                          # katalog (approved + published)
GET  /api/custom-integrations/{slug}                   # detay (galeri + demo erişimi)
GET  /api/reseller/custom-integrations                 # kendi ilanların + can_manage + limits
POST /api/reseller/custom-integrations                 { "title", "short_description", "description", "demo_url", "price"?, "gallery_media_ids"?[] }
PUT  /api/reseller/custom-integrations/{id}            # onaylı ilanda içerik değişirse yeniden onaya düşer
POST /api/reseller/custom-integrations/{id}/submit     # taslağı onaya gönder
PATCH /api/reseller/custom-integrations/{id}/publish   { "is_published": true }   # yalnız approved
GET/POST /api/reseller/custom-integrations/{id}/faqs   # SSS önerileri (onaylanınca dowaba.com SSS'ine eklenir)
DELETE /api/reseller/custom-integration-faqs/{id}      # yalnız pending/rejected öneri

Görseller mevcut POST /api/media/upload ile yüklenir; gallery_media_ids en fazla 20, yalnız kendi medya havuzundan. Yayın kararı superadmin onayına bağlıdır (durumlar: draft → pending → approved/rejected).


Ek — pratik notlar


13. Sürüm Notları + Kararlı API Sözleşmeleri

Sürüm notları artık panelde (dinamik, sürümlü): Geliştirici → Sürüm Notları sekmesi (/admin/developer). Her deploy otomatik bir taslak üretir (eklenen/çıkan endpoint + git değişiklikleri); ekip bunu geliştirici-dostu dille düzenleyip yayınlar. Geçmiş ve yeni tüm API değişikliklerini oradan takip et — eski statik liste kaldırıldı (her sürümde elle güncellenmiyordu, geride kalıyordu).

Değişmeyen kararlı sözleşmeler (stable)

Bunlara güvenebilirsin — kırıcı değişiklik olursa burada duyurulur: