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
- Meta Business hesabında bir WhatsApp Business Account (WABA) ve telefon numarası oluşturun.
- Business Settings → System users altında kalıcı bir sistem kullanıcısı belirteci üretin
(
whatsapp_business_messagingvewhatsapp_business_managementizinleri). - 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_secretolmadan yeni kayıt reddedilir (400). Bu kural öncesinde app secret'sız kaydedilmiş bir kanal silinmez; Doğrula işlemindeWHATSAPP_APP_SECRET_MISSINGhatasıylainvalidiş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_inverilmeyen yeni WhatsApp aboneliğipendingbaşlar; kampanyalar ona gitmez (no_opt_inbastırması).- Size yalnızca yazmış (gelen mesajla oluşan) kişi de
pendingdir. Rıza sonradan verildiğindesubscribedolur. opt_ineylemli anahtar sözcük ve yasal BAŞLA/START da rıza kaydeder (kaynakkeyword), 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 şablonla422 HS_CONTENT_INVALID(details.code = WHATSAPP_TEMPLATE_NOT_APPROVED) döner; gövde değişken sayısı ({{1}},{{2}}…)body_paramsile eşleşmezseWHATSAPP_TEMPLATE_PARAMS(expected). Journey yayınında aynı sorunlar WhatsApp adımındacontent_invalidissue 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);contentsbaşlık olur. - Etkileşimli:
whatsapp_interactiveile yanıt düğmeleri veya liste; gövde metnicontentsalanıdır (en fazla 1024 karakter). Meta sınırları API'de doğrulanır:
| Tür | Sınır |
|---|---|
button | 1–3 düğme; düğme başlığı ≤ 20, kimliği ≤ 256 karakter; başlıklar ve kimlikler benzersiz |
list | Liste 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) |
| Ortak | header 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=trueek olarak indirir). İndirme adresi yalnızca Meta medya alan adlarından (lookaside.fbsbx.com,*.fbcdn.net,*.whatsapp.net) kabul edilir; 16 MB üstü dosyalarMEDIA_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ıt422 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.
Konum tabanlı hedefleme
Kullanıcının izniyle cihaz konumunu alıp "şu noktanın X km yakınındakiler" segmentleri oluşturabilirsiniz (ADR-0025).
RCS: Google RBM ve Twilio RCS, zengin kartlar ve SMS geri düşüşü
RCS (Rich Communication Services) kanalı Android mesajlaşma uygulamasına zengin kart, görsel ve öneri çipleri gönderir. RCS desteklemeyen cihazlar aynı mesajın metin karşılığını app'in SMS kanalından alır (ADR-0029 §3).…