Pushizo Docs
Rehberler

WhatsApp (Meta Cloud API): kurulum, şablonlar, oturum penceresi ve gelen kutusu

Pushizo, WhatsApp mesajlarını Meta'nın Cloud API'si üzerinden doğrudan gönderir; BSP veya ek barındırma gerekmez (ADR-0029). Kanal, plan özelliği olarak SMS gerektirir. Dış hesap olmadan geliştirme ve test için mock…

Pushizo, WhatsApp mesajlarını Meta'nın Cloud API'si üzerinden doğrudan gönderir; BSP veya ek barındırma gerekmez (docs/adr/0029-whatsapp-rcs-iki-yonlu-sms-eposta-isindirma.md). Kanal, plan özelliği olarak SMS gerektirir. Dış hesap olmadan geliştirme ve test için mock sağlayıcı aynı akışı (şablon senkronu, gelen webhook, gelen kutusu) uçtan uca çalıştırır.

1. Meta tarafında hazırlık

  1. Meta Business hesabında bir WhatsApp Business Account (WABA) ve telefon numarası oluşturun.
  2. Business Settings → System users altında kalıcı bir sistem kullanıcısı belirteci üretin (whatsapp_business_messaging ve whatsapp_business_management izinleri).
  3. Uygulamanızın App Secret değerini (App Settings → Basic; 32 karakterlik onaltılık) not edin. Gelen webhook imzası (X-Hub-Signature-256) bu anahtarla doğrulanır; gerçek kanalda zorunludur.

2. Kanalı bağlama

Dashboard'da App → Kanallar → WhatsApp kartından veya API ile:

PUT /internal/v1/apps/{appId}/channels/whatsapp
{
  "provider": "meta_whatsapp",
  "waba_id": "102030405",
  "phone_number_id": "1098765432",
  "graph_api_version": "v23.0",
  "access_token": "EAAG…",
  "app_secret": "0123456789abcdef0123456789abcdef"
}
  • Belirteç ve app secret zarf şifreli saklanır, yanıtta asla dönmez (has_secret: true). Güncellemede gizli alanları boş bırakırsanız mevcut değerler korunur.
  • app_secret olmadan yeni kayıt reddedilir (400). Bu kural öncesinde app secret'sız kaydedilmiş bir kanal silinmez; Doğrula işleminde WHATSAPP_APP_SECRET_MISSING hatasıyla invalid işaretlenir ve gönderime kapanır — kanalı app secret ile yeniden kaydedin.
  • Kayıt sonrası doğrulama telefon numarasını okur (görünen numara, doğrulanmış ad, kalite puanı).

Webhook: Kanal kartındaki Webhook adresi (/hooks/whatsapp/meta_whatsapp/{token}) ve doğrulama belirteci değerlerini Meta App Dashboard → WhatsApp → Configuration → Webhook alanına girin ve messages alanına abone olun. Meta'nın GET el sıkışması belirteç eşleşirse hub.challenge ile yanıtlanır. Adres sızarsa Yenile ile yeni belirteç üretin; eski adres hemen geçersiz olur.

3. Açık rıza (opt-in) zorunludur

WhatsApp aboneliğine rıza kaydı olmadan hiçbir mesaj gönderilmez:

POST /v1/apps/{appId}/users
{ "identity": { "external_id": "elif-1" },
  "subscriptions": [{ "channel": "whatsapp", "token": "+905321234567", "opt_in": { "source": "web_form" } }] }
  • opt_in verilmeyen yeni WhatsApp aboneliği pending başlar; kampanyalar ona gitmez (no_opt_in bastırması).
  • Size yalnızca yazmış (gelen mesajla oluşan) kişi de pendingdir. Rıza sonradan verildiğinde subscribed olur.
  • opt_in eylemli anahtar sözcük ve yasal BAŞLA/START da rıza kaydeder (kaynak keyword), bkz. İki yönlü SMS ve anahtar sözcükler.

4. Şablonlar ve 24 saatlik oturum penceresi

Meta kuralı: kişinin son gelen mesajından sonraki 24 saat içinde serbest metin, medya ve etkileşimli mesaj gönderilebilir; pencere dışında yalnızca onaylı şablon gider.

  • Senkron: Şablonlar Meta'dan saatlik ve Gelen kutusu → WhatsApp şablonları → Senkronla ile anında okunur (POST /v1/apps/{appId}/whatsapp/templates/sync). Durumlar: APPROVED, PENDING, REJECTED…
  • Gönderim yolu denetimi: Bildirim (POST /v1/notifications), kampanya gönderimi/zamanlaması ve gelen kutusu yanıtı onaylı olmayan şablonla 422 HS_CONTENT_INVALID (details.code = WHATSAPP_TEMPLATE_NOT_APPROVED) döner; gövde değişken sayısı ({{1}}, {{2}}…) body_params ile eşleşmezse WHATSAPP_TEMPLATE_PARAMS (expected). Journey yayınında aynı sorunlar WhatsApp adımında content_invalid issue olarak raporlanır.
  • Kanal açıkça verilmeli: Yalnızca WhatsApp içeriği (şablon/medya) olan bildirim ve kampanyalarda hedefte channels: ["whatsapp"] zorunludur; aksi halde 422 (push abonelerine metinsiz mesaj gitmez).
{
  "app_id": "app_…",
  "whatsapp_template": { "name": "siparis_durumu", "language": "tr", "body_params": ["{{ user.tags.order }}"] },
  "target": { "included_segments": ["seg_…"], "channels": ["whatsapp"] }
}

Şablon parametreleri Liquid ile kişiselleştirilir; boş parametre veya 1024 karakteri aşan değer gönderimde reddedilir. Pencere dışındaki alıcılara giden serbest metin/medya/etkileşimli teslimatlar whatsapp_window_closed ile bastırılır.

5. Medya ve etkileşimli mesajlar (oturum içi)

  • Medya: whatsapp_media (image, video, document, audio; yalnızca https); contents başlık olur.
  • Etkileşimli: whatsapp_interactive ile yanıt düğmeleri veya liste; gövde metni contents alanıdır (en fazla 1024 karakter). Meta sınırları API'de doğrulanır:
TürSınır
button1–3 düğme; düğme başlığı ≤ 20, kimliği ≤ 256 karakter; başlıklar ve kimlikler benzersiz
listListe düğmesi metni ≤ 20; toplam 1–10 satır; satır başlığı ≤ 24, açıklama ≤ 72, kimliği ≤ 200; birden çok bölümde bölüm başlığı zorunlu (≤ 24)
Ortakheader ve footer ≤ 60 karakter (Liquid desteklenir)
{
  "contents": { "tr": "Teslimat saatini seçin" },
  "whatsapp_interactive": {
    "type": "button",
    "header": "Sipariş {{ user.tags.order }}",
    "buttons": [{ "id": "sabah", "title": "Sabah" }, { "id": "aksam", "title": "Akşam" }]
  },
  "target": { "include_aliases": { "external_id": ["elif-1"] }, "channels": ["whatsapp"] }
}

Kişinin seçtiği düğme/satır gelen kutusuna yanıt olarak düşer; seçilen id mesajın content.payload alanındadır. Dashboard'da mesaj/şablon/journey editörünün Oturum mesajı modunda ve gelen kutusu yanıtında Etkileşimli sekmesinde hazırlanır.

6. Gelen kutusu

  • Gelen mesajlar (metin, düğme/liste yanıtı, medya, konum, tepki) Gelen kutusunda kişi bazlı konuşma olarak görünür; başlıkta rıza durumu ve pencerenin bitiş zamanı gösterilir.
  • Gelen medya: Görseller konuşmada önizlenir, tüm türler indir bağlantısıyla alınır. Dosya, kanalın belirteciyle Meta'dan yetkili bir proxy üzerinden akıtılır (GET /internal/v1/apps/{appId}/conversations/{id}/messages/{messageId}/media, ?download=true ek olarak indirir). İndirme adresi yalnızca Meta medya alan adlarından (lookaside.fbsbx.com, *.fbcdn.net, *.whatsapp.net) kabul edilir; 16 MB üstü dosyalar MEDIA_TOO_LARGE, süresi dolmuş medya 404 döner.
  • Yanıt: Pencere açıkken metin, şablon veya etkileşimli mesaj; kapalıyken yalnızca onaylı şablon (serbest metin 422 WHATSAPP_WINDOW_CLOSED). Rıza kaydı olmayan kişiye yanıt 422 WHATSAPP_NO_OPT_IN.

7. Durumlar ve hatalar

Meta durum bildirimleri teslimata işlenir: delivered → teslim edildi, read → açıldı, failed → başarısız. Numara WhatsApp kullanmıyorsa (131026) abonelik geçersiz yapılır; belirteç/izin hataları kanalı invalid yapar; hız sınırları Retry-After ile yeniden denenir. Ayrıntılı eşleme ADR-0029 §2.3'tedir.

8. Mock ile yerel geliştirme ve E2E

provider: "mock" (isteğe bağlı app_secret): şablon senkronu örnek onaylı/bekleyen/reddedilmiş şablonlar yükler, /hooks/whatsapp/mock/{token} Meta biçimindeki webhook'u kabul eder (anahtar tanımlıysa imza zorunlu), gelen medya proxy'si küçük bir örnek dosya döndürür. Mock webhook üretimde imzasız kabul edilmez.

Bu sayfada