Push bildirim, uygulamanız kapalıyken bile kullanıcının ekranına düşen kısa mesajdır ve teknik olarak üç parçadan oluşur: cihazda üretilen bir token, o tokenı saklayan sizin sunucunuz ve mesajı gerçekten telefona ileten platform servisi — Android’de FCM, iOS’ta APNs. Uygulamanız bildirimi doğrudan telefona göndermez; token ile platforma gönderir, teslimi platform yapar. Bu tek cümle, bu işin neden “bir buton koyduk gönderiyoruz” kadar basit olmadığını da anlatır: tokenlar ölür, kullanıcı izni geri alır, saat dilimi kayar ve bir hatalı kampanya bir günde uygulama silinmesine yol açar. Aşağıda tokenın yaşam döngüsünü, gönderim mimarisini, sessiz saat ve segmentasyon kodunu, ölü token temizliğini, izin isteme zamanlamasını, KVKK tarafını ve bildirimi spama çeviren beş hatayı bulacaksınız.
Bildirim gerçekte nasıl gidiyor?
Akış her platformda aynıdır ve dört adımdır. Uygulama açıldığında cihaz platformdan bir token ister; uygulama bu tokenı sizin sunucunuza kaydeder; siz göndermek istediğinizde mesajı token listesiyle birlikte platforma verirsiniz; platform telefona teslim eder ya da hata döner. Kritik olan son adımdır: teslim garantisi yoktur. Telefon kapalıysa, pil tasarrufu agresifse ya da kullanıcı bildirimleri kapattıysa mesaj düşmez. Bu yüzden push, kritik bilgi taşımak için tek kanal olarak kullanılmaz — parola sıfırlama, sipariş onayı ya da ödeme bilgisi gibi şeyler e-posta veya SMS ile de gitmelidir.
Push bir teslim garantisi değil, bir dikkat çekme kanalıdır. “Bildirim gitti” demek “kullanıcı gördü” demek değildir; bu yüzden hiçbir iş akışı yalnızca push’a bağlanmaz.
Token yaşam döngüsü: en çok atlanan kısım
Projelerin çoğu tokenı bir kez kaydedip unutur ve altı ay sonra gönderim listesinin yarısı çöp olur. Token beş durumda değişir ya da geçersizleşir:
- Uygulama yeniden kurulduğunda token değişir. Eski token artık kimseye ulaşmaz ama listenizde durur.
- Kullanıcı çıkış yaptığında token o kullanıcıdan ayrılmalıdır. Aksi hâlde aynı telefonu kullanan bir başkası önceki kullanıcının bildirimlerini görür — en sık görülen gizlilik kazasıdır.
- Aynı kullanıcının birden fazla cihazı olur. Token kullanıcıya değil cihaza aittir; veri modelinde ilişki bire-çok olmalıdır.
- Platform tokenı kendi başına yeniler. Uygulama bunu bir olayla haber verir; o olayda sunucuyu güncellemiyorsanız kullanıcı sessizce ulaşılamaz hâle gelir.
- Uygulama silindiğinde token “kayıtsız” olur. Bunu ancak gönderim sırasında dönen hatadan anlarsınız — ve o hatayı yazmıyorsanız hiç anlamazsınız.
Pratik kural: token tablosunda kullanıcı kimliği, cihaz kimliği, platform, dil, saat dilimi farkı ve son görülme tarihi tutulur. Bu altı alan olmadan ne segmentasyon ne sessiz saat ne de temizlik yapılabilir.
Gönderim kodu: parti, sessiz saat, ölü token
Gönderim fonksiyonunun üç işi vardır: tokenları platformun kabul ettiği büyüklükte partilere bölmek, kullanıcının yerel saatine göre sessiz saatte olanları atlamak ve dönen hatalara göre ölü tokenları işaretlemek. Aşağıdaki fonksiyon üçünü birden yapar:
interface DeviceToken {
token: string;
userId: string;
platform: 'android' | 'ios';
/** Kullanıcının UTC'ye göre dakika cinsinden farkı, ör. Türkiye için 180 */
utcOffsetMinutes: number;
}
interface SendResult {
token: string;
ok: boolean;
/** Platformun döndürdüğü hata kodu */
error?: 'unregistered' | 'invalid' | 'rate-limited' | 'transient';
}
/** Kullanıcının yerel saati sessiz aralıkta mı? (ör. 22:00-08:00) */
function inQuietHours(
offsetMinutes: number,
nowUtc: Date,
fromHour = 22,
toHour = 8,
): boolean {
const localMinutes =
(nowUtc.getUTCHours() * 60 + nowUtc.getUTCMinutes() + offsetMinutes + 1440) %
1440;
const hour = Math.floor(localMinutes / 60);
// Aralık gece yarısını geçiyorsa iki parça hâlinde kontrol edilir.
return fromHour <= toHour
? hour >= fromHour && hour < toHour
: hour >= fromHour || hour < toHour;
}
function chunk<T>(items: T[], size: number): T[][] {
const out: T[][] = [];
for (let i = 0; i < items.length; i += size) out.push(items.slice(i, i + size));
return out;
}
export async function sendCampaign(
tokens: DeviceToken[],
message: { title: string; body: string; data?: Record<string, string> },
deps: {
now: Date;
push: (batch: DeviceToken[], msg: typeof message) => Promise<SendResult[]>;
deleteTokens: (tokens: string[]) => Promise<void>;
},
): Promise<{ sent: number; deferred: number; pruned: number; retry: string[] }> {
// 1) Sessiz saatte olanları hiç göndermeyiz — sonraki pencereye ertelenir.
const sendable = tokens.filter(
(t) => !inQuietHours(t.utcOffsetMinutes, deps.now),
);
const deferred = tokens.length - sendable.length;
const dead: string[] = [];
const retry: string[] = [];
let sent = 0;
// 2) FCM tek istekte 500 token kabul eder; APNs bağlantı başına akış yönetir.
for (const batch of chunk(sendable, 500)) {
const results = await deps.push(batch, message);
for (const r of results) {
if (r.ok) {
sent += 1;
} else if (r.error === 'unregistered' || r.error === 'invalid') {
dead.push(r.token); // uygulama silinmiş ya da token bozuk → sil
} else {
retry.push(r.token); // geçici hata → geri çekilmeli kuyruğa
}
}
}
// 3) Ölü tokenları hemen temizle; yoksa liste her kampanyada daha da kirlenir.
if (dead.length > 0) await deps.deleteTokens(dead);
return { sent, deferred, pruned: dead.length, retry };
}Fonksiyonun döndürdüğü dört sayı tesadüfen orada değil: gönderilen, ertelenen, silinen ve tekrar denenecek. Bu dördünü kaydetmeyen bir sistemde “neden kimse görmedi” sorusuna cevap veremezsiniz. Geçici hatalar için tekrar denemenin de geri çekilmeli (exponential backoff) olması gerekir; aynı listeyi hemen yeniden göndermek platform tarafında hız sınırına takılmanın en hızlı yoludur.
Token mu, konu (topic) mu?
İki gönderim modeli var ve seçim, mesajın kişiye mi kitleye mi ait olduğuna bağlıdır:
- Token bazlı gönderim — belirli cihazlara. Kişiye özel her şey böyle gider: siparişiniz yolda, mesajınız var, randevunuz yarın. Segmentasyon sizin veritabanınızda yapılır, dolayısıyla sınır yoktur.
- Konu (topic) bazlı gönderim — bir konuya abone olan herkese. Kampanya, duyuru, maç skoru gibi kitlesel içerik için doğrudur; tek istekle milyonlara gider ve token listesi tutmanıza gerek kalmaz. Karşılığında kime gittiğini bilemezsiniz.
- Karma model pratikte en yaygını: kitlesel duyurular konuyla, kişisel bildirimler tokenla. Kullanıcının hangi konulara abone olduğunu da bir tercih ekranından yönetmesi gerekir.
İzni ne zaman istemeli?
Bu, bildirim projelerinin en pahalı tek kararıdır. iOS’ta ve yeni Android sürümlerinde bildirim izni açık onay gerektirir ve kullanıcı bir kez reddettiğinde onu geri çevirmek neredeyse imkânsızdır — ayarlara gitmesi gerekir, ki gitmez. Bu yüzden izin, uygulama ilk açıldığında istenmez.
- İzni bir değere bağlayın. “Bildirimlere izin ver” yerine “siparişin yola çıktığında haber verelim mi” diye sorun; izin ekranını kullanıcının o özelliği kullandığı anda gösterin.
- Önce kendi ekranınızı gösterin. Sistem penceresinden önce ne göndereceğinizi anlatan bir ön ekran, kabul oranını belirgin şekilde yükseltir; reddeden kullanıcı sistem iznini hiç harcamamış olur.
- Reddi kaydedin ve saygı gösterin. Aynı soruyu her açılışta sormak hem mağaza politikalarıyla sorun çıkarır hem uygulamanın silinme sebebi olur.
- Uygulama içi bildirim merkezi kurun. Push kapalı olsa bile mesaj uygulama içinde bir listede görünmeli; aksi hâlde izin vermeyen kullanıcı hiçbir duyuruyu görmez.
Ticari bildirim ve KVKK tarafı
Burada iki ayrı izin var ve karıştırılıyor: işletim sisteminin bildirim izni teknik bir izindir, ticari elektronik ileti için gereken onay ise hukuki bir izindir. Kullanıcının telefonunda bildirim açık olması, ona kampanya gönderebileceğiniz anlamına gelmez. Pratikte üç şey gerekir: işlemsel bildirimler (sipariş, randevu, güvenlik) ile ticari bildirimlerin veri modelinde ayrı tutulması, ticari onayın tarih ve kanal bilgisiyle kaydedilmesi ve kullanıcıya bildirim türü bazında kapatma imkânı verilmesi. Tek bir “bildirimleri kapat” anahtarı, sipariş bilgisini de kapatır ve kullanıcıyı iki kere kaybettirir. Kişisel veri tarafının genel çerçevesini KVKK uyumlu web sitesi yazımızda anlattık.
Ne göndermeli, ne göndermemeli?
Bildirimin değeri sıklıkla değil isabetle ölçülür. İşe yarayan bildirimlerin hepsinde tek bir özellik vardır: kullanıcının zaten beklediği bir olayı haber verir.
- İşlemsel — sipariş durumu, kargo hareketi, randevu hatırlatması, ödeme bilgisi. En yüksek açılma oranı bunlarda; kullanıcı bunları bekliyor.
- Tetiklenmiş — kullanıcının davranışına bağlı olan: sepette bıraktığın ürün, takip ettiğin ilan güncellendi, aradığın kriterde yeni kayıt var. İyi kurulduğunda kampanyadan çok daha verimli.
- Kitlesel kampanya — en düşük dönüşüm ve en yüksek silinme riski buradadır. Segmentsiz gönderilen kampanya, uygulamanın bildirim iznini yakan şeydir.
- Sessiz (silent) bildirim — kullanıcıya görünmez, uygulamanın arka planda veri güncellemesi için. Faydalıdır ama pil tasarrufu kısıtlarına takılır; teslim oranı en düşük tür.
Bildirimin gelir tarafındaki yerini abonelik ve kampanya modelleriyle birlikte kurmak gerekir; seçenekleri mobil uygulama gelir modelleri yazımızda karşılaştırdık. Mağaza tarafındaki görünürlük ise ayrı bir iştir, onu da ASO nedir yazımızda ele aldık.
Ölçüm: hangi sayıya bakılır?
Gönderilen bildirim sayısı bir başarı ölçüsü değildir. Dört sayıya bakılır ve dördü birlikte okunur: teslim oranı (gönderilenin kaçı telefona ulaştı), açılma oranı (kaçı tıklandı), izin oranı (kullanıcıların kaçında bildirim açık) ve silinme/iptal oranı. Dördüncüsü diğer üçünü dengeler: açılma oranı yüksek görünen agresif bir kampanya, aynı ay izin oranını düşürüyorsa net zarardadır.
Maliyet ve teknik yük
- Platform servisleri (FCM, APNs) gönderim başına ücret almaz; maliyet tarafı sizin sunucunuzda ve kampanya aracındadır.
- Hazır araç (bildirim gönderme paneli, segmentasyon, A/B testi) aylık abonelikle gelir ve ekipteki pazarlama tarafı için doğru seçimdir; teknik ekip olmadan kampanya kurulabilir.
- Kendi altyapınızı kurmak token tablosu, kuyruk, geri çekilmeli tekrar deneme ve raporlama demektir. Kişisel veriyi dışarı çıkarmak istemiyorsanız ya da segmentasyon kendi veritabanınıza sıkı bağlıysa doğru yol budur.
- Devam eden yük: bildirim özelliği bir kere yazılıp bitmiyor; platform sürümleri izin davranışını değiştiriyor. Bakım kalemini baştan hesaplayın, kalemleri mobil uygulama bakım maliyeti yazımızda açtık.
Bildirimi spama çeviren beş hata
- Herkese aynı mesajı göndermek. Segmentsiz kampanya, izin oranını en hızlı düşüren şeydir; bir kez kapatan kullanıcı geri gelmez.
- Saat dilimini hesaplamamak. Sunucu saatiyle gönderilen “kampanya” bir kısım kullanıcıya gece üçte düşer ve o gönderim doğrudan silinme üretir.
- Ölü tokenları temizlememek. Liste şişer, raporlar yanlış çıkar ve platform hız sınırına gereksiz yaklaşırsınız.
- Tek bir kapatma anahtarı koymak. Kullanıcı kampanyayı kapatmak isterken sipariş bildirimini de kapatır; bildirim türü bazında tercih şart.
- Bildirimin tıklandığında gittiği yeri atlamak. Uygulamayı açıp ana ekranda bırakan bildirim, mesajın kendisini boşa çıkarır; her bildirim ilgili ekrana derin bağlantıyla açılmalıdır.
Sonuç
Push bildirim teknik olarak küçük, operasyonel olarak büyük bir özelliktir: zor kısmı mesajı göndermek değil, tokenı canlı tutmak, doğru kişiye doğru saatte göndermek ve izni yakmamaktır. Bu yüzden işe token yaşam döngüsünden ve bildirim türü bazında tercih ekranından başlayın, kampanyayı en sona bırakın. Kitlesel duyuru ihtiyacınız basitse hazır bir panel yeterlidir; segmentasyon kendi verinize sıkı bağlıysa ya da kişisel veriyi dışarı çıkarmak istemiyorsanız kendi altyapınızı kurmanız gerekir. Uygulamanıza bildirim eklemeyi planlıyorsanız mobil uygulama hizmetimize bakabilir ya da kullanıcı sayınızı ve göndermek istediğiniz bildirim türlerini yazıp teklif alabilirsiniz.