Kategoriler, kullanıcı tercihleri ve tercih merkezi (Faz 12)
Kullanıcıların hangi mesaj kategorilerini hangi kanaldan almak istediğini yönetir; kategoriye kapalı kullanıcıya o kategorideki kampanya/mesaj gönderilmez. Tasarım kararları: ADR-0019 §4, §13. Ortak kod:…
Kullanıcıların hangi mesaj kategorilerini hangi kanaldan almak istediğini yönetir; kategoriye kapalı kullanıcıya o kategorideki kampanya/mesaj gönderilmez. Tasarım kararları: docs/adr/0019-ab-test-ve-tercihler.md. Ortak kod: packages/db/src/preferences.ts.
1. Veri modeli
message_categories: app başınakey(ör.marketing), çok dilliname/description,channels(null = tüm kanallar),default_opted_in,position.user_preferences:user_id × channel (null = tümü) × category → opted_in,source(api,sdk,preference_center,dashboard,import). Tek kayıt:(app_id, user_id, coalesce(channel,''), category).- Kampanya ve mesaj
categoryalanı taşır (campaigns.category,messages.category); journey gönderimleri şablonun/kampanyanın kategorisini devralmaz, kategori mesaj oluşturulurken verilir.
Etkin tercih (effectiveOptIn): kanal bazlı açık kayıt > "tüm kanallar" kaydı > kategorinin default_opted_in değeri.
2. Bastırma
Kitle çözümlemede (apps/worker/src/delivery/audience.ts) mesajın kategorisi varsa categoryOptOuts o kategoriye kapalı kullanıcıları (kanal bazında) getirir; eşleşen teslimat status = suppressed, suppression_reason = category_opt_out. Kabul ölçütü: kategori opt-out'u o kategorideki kampanyayı bastırır (ab-testing.int.test.ts, phase12.int.test.ts).
3. API
| Yol | Yetki | Açıklama |
|---|---|---|
GET/POST /v1/apps/{app_id}/categories | users:read / admin | Kategori listesi (her biri opted_out_users sayısıyla) ve oluşturma |
PATCH/DELETE /v1/apps/{app_id}/categories/{id} | admin | Güncelleme; silme kullanıcı tercihlerini de siler (varsayılana dönerler) |
GET /v1/apps/{app_id}/users/by/{label}/{value}/preferences | users:read | Kullanıcının etkin tercihleri (data[]: category, channel, opted_in, explicit, source) + kategoriler |
PUT …/preferences | users:write | { "preferences": [{ "category", "channel"?, "opted_in" }] }; tanımsız kategori 404, kategorinin kapsamadığı kanal 400 |
| Internal | /internal/v1/apps/{appId}/categories[…], /users/{userId}/preferences | Dashboard (Kitle → Kategoriler, kullanıcı detayı → Tercihler kartı) |
| SDK | GET/PUT /sdk/v1/apps/{appId}/preferences | subscription_id + token_hash (ve login'li kullanıcıda identity_hash) ile sahiplik kanıtı; Web Pushizo.User.getPreferences()/setPreferences([...])/getCategories(), Android/iOS/Flutter eşdeğerleri (docs/sdk/*.md) |
Node: hs.preferences.listCategories/createCategory/updateCategory/deleteCategory/getUser/setUser; PHP: $hs->preferences->….
4. Tercih merkezi sayfası
E-posta render'ında {{ preference_center_url }} Liquid değişkeni, teslimat başına imzalı GET /t/p/{delivery_id}?a=&s= adresine çözülür (imza türü p, packages/render/src/tracking.ts). Sayfa tr/en, açık/koyu temalıdır; kategorileri onay kutularıyla listeler.
POST …/t/p/{delivery_id}action=save+cat_<key>alanları: işaretlenmeyen kategoriler kapatılır (source = preference_center).action=unsubscribe_all: tüm kategoriler kapatılır ve teslimatın e-posta aboneliğiunsubscribedolur.- Yanlış imza 404; GET hiçbir şeyi değiştirmez (bağlantı tarayıcıları için güvenli).
Markalama apps.settings.preferenceCenter: enabled, brandName, accentColor, logoUrl, customUnsubscribeUrl. Özel çıkış URL'si verilmişse abonelikten çıkma onay formu (POST /t/u/{dlv} confirm=1) işlemi yaptıktan sonra customUnsubscribeUrl?delivery_id=… adresine 302 yönlendirir; RFC 8058 tek tık (List-Unsubscribe=One-Click) yönlendirmesiz 200 döner.
5. SMS HELP / STOP otomatik yanıtı
Gelen SMS'te HELP, INFO, YARDIM, BILGI/BİLGİ (SMS_HELP_KEYWORDS) → help; STOP sözcükleri → stop. apps.settings.sms.autoReply açıksa helpReply / stopReply metni tek aboneliğe işlemsel SMS olarak gönderilir (messages.source = api, include_unsubscribed: true, STOP sonrası da iletilebilir). Twilio gelen webhook'u, Netgsm gelen kutusu ve genel webhook (type: "help") aynı yolu kullanır (apps/api/src/hooks/hooks.service.ts autoReply). Dashboard: App ayarları → SMS yanıtları.
6. Dashboard
- Kitle → Kategoriler: liste (opt-out sayısı), oluştur/düzenle/sil; tr/en ad ve açıklama, kanal seçimi, varsayılan opt-in.
- Kullanıcı detayı → Tercihler: kategori × kanal matrisi, kaynağı ve son güncellemeyi gösterir; dashboard'dan değişiklik
source = dashboard. - Kampanya / bildirim editörü:
categoryseçimi; kitle tahmini kategori bastırmasını hesaba katar.
7. Testler
apps/api/src/__tests__/phase12.int.test.ts (kategori CRUD, tercih yazma/okuma, SDK tercih ucu, tercih merkezi sayfası ve özel çıkış URL'si, kiracı izolasyonu), apps/worker/src/__tests__/ab-testing.int.test.ts (kategori opt-out bastırması), sdk/web/src/__tests__/faz12.test.ts (Web SDK tercihleri ve toplama istemi).
Segmentler
Dinamik ve statik segmentler, koşul AST'si (all/any/none), alan ve operatör listesi, davranış ve konum koşulları, önizleme/tahmin uçları ve kampanya kullanımı.
Suppression list
E-posta ve SMS gönderim engeli listesi — nedenler (bounce, şikâyet, STOP, elle, tek tık), maskeli saklama, API ve dashboard yönetimi, include_unsubscribed istisnası.