Ana içeriğe geç

02 · POST_CONSENT — Onay Kayıt Sözleşmesi

Görev: Kullanıcının onay ekranındaki seçimini (Tümünü Kabul Et / Zorunlu Olanlar / Tercihleri Düzenle) sunucuya iletir. Sunucu karşılığında onay kaydını oluşturur/günceller, etkinleşen onay kararlarını döndürür ve yeni cihaz durumunu (localState) verir.

Ne zaman çalışır: Kullanıcı onay ekranında bir seçim yaptığında (AUTH → config → ekran → burada).

Bu dosya iki şeyi kapsar: (A) sözleşme (istek/yanıt, doğrulama, hata) ve (B) onayın etiketlere (GA4/GTM) dağıtımı + iOS ATT katmanı.


1. Genel Bakış

POST_CONSENT, açılış döngüsünün son halkasıdır ve döngüyü kapatır: kullanıcının seçimi kaydedilir, sunucu otoriter son durumu döndürür, SDK bu durumu etiketlerine uygular ve yeni localState'ı önbelleğe yazar — bir sonraki açılışta AUTH'a geri beslenir.

Tasarım felsefesi: Onay, grubun kimliği (groupId) seviyesinde verilir. Bir grup onaylanınca, config'deki o grubun consentKeys listesindeki etiketlerin tümü birlikte açılır; grup kapalıysa tümü birlikte kapanır. Item/teknoloji seviyesinde ayrı onay durumu yoktur. Sunucu yalnız grubun kimliği + son durumu ({ [groupId]: state }) taşır; bu haritanın etiketlere çevrilmesi SDK/entegrasyon tarafında yapılır.

AUTH (00) GET /config (01) POST_CONSENT (BU)
kimlik + localState ▶ token ─▶ tam config ────▶ onay kaydı
│ token │ parse → ekrandaki seçim │ request: actionType + consent map
└── decision.updateRequired ┘ (aynı token) │

bir sonraki AUTH ◀─────────── response: consentRecordId + consent + localState

2. Endpoint & Güvenlik

2.1 Endpoint

ÖzellikDeğer
MethodPOST
Path/v2/consent/{consentId}/consent
Base URL{{SDK_BASE_URL}}
Gövde formatıapplication/json (UTF-8)
Timeout15 sn
IdempotentEvet — aynı eventId/onay eylemi tekrar gönderilirse aynı kaydı günceller, mükerrer kayıt üretmez.

2.2 Güvenlik (imza + token)

POST_CONSENT bir YAZMA işlemidir (durumu değiştirir) → bu yüzden hem AUTH'tan alınan token hem HMAC imza birlikte kullanılır:

HeaderDeğer / Formül
Content-Typeapplication/json
X-Consent-TokenAUTH yanıtındaki token (imzalı, oturum bazlı). Bu yazma çağrısıyla oturum kapanır/tüketilir (bkz. 01 §6).
X-Consent-SignatureHMAC-SHA256(token, consentId + actionType + requestTimestamp + nonce) — hex kodlanmış
X-Request-Timestampepoch milliseconds
X-NonceTek kullanımlık rastgele string
  • Algoritma: HMAC-SHA256 (anahtar = AUTH token'ı)
  • İmza girdisi (canonical): consentId + actionType + requestTimestamp + nonce
  • Replay penceresi: ±300 sn; dışında kalan istek → 401 REPLAY_DETECTED.
  • Token geçersiz/süresi dolmuşsa → 401 INVALID_TOKEN (SDK yeniden AUTH yapar).

Neden token + imza birlikte? Token, kimliği (hangi consent oturumu) taşır; HMAC imzası ise bu yazma isteğinin o token sahibi tarafından, taze ve tam olarak üretildiğini kanıtlar — sahte/mükerrer yazma karşı koruma sağlar.


3. İstek (request)

Kullanıcı onay eylemini temsil eden gövde. camelCase zorunludur.

AlanTipZorunluAçıklama
consentIdstringEvetOnay kaydı kimliği (örn. ck_mobildev_0001). Hangi consent setine yazılacağını belirtir; path'tekine birebir eşit olmalıdır.
actionTypeenum accept_all | reject_all | modifyEvetBasılan buton / onay tipi. Config'deki buton id'leriyle (banner.buttons.list, manager.footer.actions) birebir örtüşür.
consentobjectEvetGRUBUN kimliği seviyesinde onay haritası: { [groupId]: state } düz map (aşağıda detay).
freshUserbooleanEvetBu cihaz/kullanıcı için ilk onay mı? Telemetri + raporlama (ilk-onay oranı).
contentVersionstringEvetOnayın verildiği config içerik sürümü (config.meta.contentVersion). Bir sonraki AUTH'ta remote contentVersion'la karşılaştırılıp reprompt kararı üretilir.
localestring (BCP-47)HayırOnayın verildiği kullanıcı dili (örn. tr-TR). Audit/çeviri referansı.
sdkVersionstring (semver)HayırSDK sürümü. Audit + uyumluluk korelasyonu.
deviceTimestring (ISO 8601 UTC)HayırCihaz saati. Drift/manipülasyon kontrolü.
attentionobject | nullHayıriOS ATT durumu. Android'de null (uygulanmaz). iOS'ta ve promptATTdialog: true iken dolu olur (§7).

consent, düz bir haritadır: anahtar = groupId, değer = state (örn. {"essential":"granted","targeting":"denied"}).

BileşenTipAçıklama
Anahtar (groupId)stringconfig.manager.groups[].id (örn. essential, functionality, targeting, tracking). Hangi grubun durumunun iletildiğini belirtir.
Değer (state)enum granted | deniedGrubun son durumu (yalnız bu iki değer). granted → grubun tüm consentKeys'i etkinleşir; denied → tümü kapatılır (çeviri SDK tarafında, §6).

Neden item-seviye state yok? GTM/etiket API'leri key bazlı çalışır; eski item-bazlı {title, state} yapısı bu modelle çelişiyordu. Bu sözleşmede onay durumu yalnızca grup (groupId) seviyesinde düz bir map olarak taşınır; item'lar (teknoloji/vendor) config tarafında yalnız bilgilendirme amaçlıdır ve grupla birlikte açılıp kapanır.

3.2 Örnek İstek (modify + karma durum)

{
"consentId": "ck_mobildev_0001",
"actionType": "modify",
"freshUser": true,
"contentVersion": "2",
"locale": "tr-TR",
"sdkVersion": "1.4.0",
"deviceTime": "2026-09-20T11:33:29Z",
"attention": null,
"consent": {
"essential": "granted",
"functionality": "granted",
"targeting": "denied",
"tracking": "denied"
}
}

Ayrıca header'da: X-Consent-Token + X-Consent-Signature + X-Nonce + X-Request-Timestamp (§2.2). attention örnekte null çünkü platform Android. iOS'ta ATT'ye tabi bir grup onaylanacaksa dolu gönderilir (§7). Bu örneğin aynısı ayrıca [02-postconsent.request.json](./02-postconsent.request.json) dosyasında yer alır.


4. Yanıt (response)

4.1 Gövde Alanları (200)

AlanTipAçıklama
successbooleanOnay eyleminin başarılı işlendiği.
codestringDurum kodu (başarıda OK).
consentRecordIdstringOluşturulmuş/güncellenmiş onay kaydının benzersiz kimliği. Telemetri/raporlama + audit korelasyonunun anahtarıdır.
storedAtstring (ISO 8601 UTC)Kaydın işlendiği sunucu zamanı.
actionTypestringÇözülen onay tipi (eko; audit).
consentobjectOtoriter onay haritası: { [groupId]: granted | denied } — sunucunun doğruladığı son sonuç. SDK bunu §6'daki eşlemeyle etiket key'lerine yayarak uygular.
localStateobjectYeni cihaz durumu. SDK bunu önbelleğe yazar; bir sonraki AUTH istek girdisi olarak geri besler (retrigger zinciri).

4.2 localState Alt Alanları

localState, 00 · AUTH isteğindeki localState ile birebir aynı yapıdadır — döngünün geri beslemesidir:

AlanTipAçıklama
hasConsentbooleanArtık true (bu cihazda onay saklı).
installedUiVersionstringCihaza yazılan UI versiyonu.
installedLegalVersionstringCihaza yazılan legal/metin versiyonu.
installedContentVersionstringOnayın verildiği içerik versiyonu (request.contentVersion).
lastShownAtstring (ISO 8601)Onay ekranının gösterildiği/saklandığı an (interval retrigger için).
consentobjectGövdedeki consent haritasının aynısı (groupIdstate); bir sonraki AUTH'ın localState.consent'i.

İki consent alanı neden var? Gövdedeki consent, SDK'ın etiketlere uygulayacağı sonuç; localState.consent, depolanacak geri besleme. İkisi de { [groupId]: state } haritasıdır ve içerik aynıdır; ayrım, "uygula" ile "hatırla" amaçlarının farklı olmasındandır.

4.3 Örnek Yanıt (200)

{
"success": true,
"code": "OK",
"consentRecordId": "cr_9f2e6a4b8c1d",
"storedAt": "2026-09-20T11:33:31Z",
"actionType": "modify",
"consent": {
"essential": "granted",
"functionality": "granted",
"targeting": "denied",
"tracking": "denied"
},
"localState": {
"hasConsent": true,
"installedUiVersion": "2.4",
"installedLegalVersion": "1.0",
"installedContentVersion": "2",
"lastShownAt": "2026-09-20T11:33:31Z",
"consent": {
"essential": "granted",
"functionality": "granted",
"targeting": "denied",
"tracking": "denied"
}
}
}

Bu örneğin aynısı ayrıca [02-postconsent.response.json](./02-postconsent.response.json) dosyasında yer alır.


5. actionType → Karar Matrisi

actionType, grup durumlarını belirler. modify dışında durumlar implicittir (buton mantığı); modify explicittir (kullanıcının grup bazlı toggle'ları). essential her durumda granted'tir (kilitli, §6).

actionTypeessentialfunctionalitytargetingtrackingconsent map
accept_allgrantedgrantedgrantedgrantedimplicit (hepsi granted)
reject_allgranteddenieddenieddeniedimplicit (yalnız essential granted)
modifygranted*kullanıcıkullanıcıkullanıcıexplicit (kullanıcının state'leri)

* essential içindeki security_storage asla denied yapılamaz — kilitlidir.

Config buton eşlemesi: banner.buttons.listaccept_all / reject_all / modify; manager.footer.actionsaccept_all / save (save ≡ modify); detail.actionssave (≡ modify). Yani üç davranış: hepsi, yalnız zorunlu, düzenle.


6. Onay → Etiket (GA4 / GTM) Dağıtımı

Bu bölüm, onayın gerçek etkisini tanımlar: kullanıcının grup bazlı tercihinin, uygulamanızdaki etiketlere granted/denied olarak nasıl yansıtılacağı. Dağıtımın sorumluluğu SDK/entegrasyon tarafındadır; sunucu yalnız { [groupId]: state } döndürür.

6.1 Config'deki grup → etiket key eşleşmesi

Config'deki her grup, bir consentKeys[] listesi taşır. Tipik dört grup ve karşılık gelen etiket key'leri:

Config group.idKategoriconsentKeys[] (etiket key'leri)Varsayılan
essentialZorunlusecurity_storagegranted (kilitli)
functionalityİşlevsellikpersonalization_storage, functionality_storagedenied
targetingHedefleme / reklamad_storage, ad_user_data, ad_personalizationdenied
trackingİzleme / performansanalytics_storagedenied

7 key'in tamamı, GTM Consent Mode v2'de built-in'dir. ad_user_data ve ad_personalization, v2'de zorunludur (default state'leri denied). Bu 7 key için ek "custom" Consent Setting tanımlamak gerekmez — yalnızca §6.4'teki Consent Command + tetikleyici trigger'ları kurmanız yeterlidir.

İsim farkına dikkat: personalization_storage (içerik/deneyim kişiselleştirme, reklam dışı) ≠ ad_personalization (reklam kişiselleştirme/yeniden pazarlama). İkisi farklı key'lerdir; isim benzerliğine düşmeyin.

6.2 Dağıtım mantığı (SDK tarafında)

SDK, response consent haritasındaki her groupId'yi, yerel config'deki o grubun consentKeys[] kümesine yayarak etiketlere uygular:

// groupId → GTM consentKeys dağıtımı (SDK / entegrasyon tarafı)
function applyConsentToGTM(config, consentMap) {
const consent = {};
for (const group of config.manager.groups) {
const state = consentMap[group.id]; // "granted" | "denied"
const effective = group.lockState === true ? "granted" : (state || "denied");
for (const key of group.consentKeys) {
consent[key] = effective; // grup state'i → o grubun tüm key'lerine
}
}
// security_storage (lock) her zaman granted; diğerleri kullanıcı tercihine göre
// Config'de yer almayan bir key için varsayılan "denied" (güvenli taraf)
window.dataLayer.push({ event: 'consent_update', ...consent });
}

Kural seti:

  • Grup onaylı (granted) → o grubun consentKeys'indeki tüm key'ler granted.
  • Grup kapalı (denied) → o gruptaki tüm key'ler denied.
  • essential (→ security_storage) → her zaman granted (kilitli; lockState: true). Kullanıcı kapatamaz.
  • Config'de yer almayan key → varsayılan denied (güvenli taraf).
  • Önemli ayrım: Onay durumu grupta tutulur; item'lar etiket key taşımaz. Dağıtım her zaman group.consentKeys[] üzerinden yapılır.

6.3 Durum değerleri & gönderim

Her key yalnız granted veya denied değerini alır — ara durum yoktur. Gönderim, projenize göre iki eşdeğer yoldan biridir:

YolKomutNot
GA4 gtag.jsgtag('consent', 'default' | 'update', <states>)wait_for_update yalnız default komutundadır.
GTM dataLayerwindow.dataLayer.push({ 'event': 'default_consent' | 'consent_update', ...flat keys })Eşdeğer; GTM tag/trigger'ları consent state'lerine bakar.

<states> objesi iki yolda da aynıdır: 7 key'in tamamı + her birinin granted/denied değeri.

Komutlar:

  • default (ilk yükleme): Varsayılan state'leri set eder. Non-essential key'ler denied, security_storage granted. wait_for_update (ms) eklenirse etiketler, gerçek tercih gelene dek bu süre kadar bekletilir → rızasız veri işlemesi önlenir, config gecikmesine karşı denge kurulur (pratik başlangıç 300–1000 ms).
  • update (kullanıcı tercihini verince veya iznini geri çekince): State'leri günceller. Opt-out (izin geri çekme) da update + ilgili key'leri denied yapılır. remove diye bir komut yoktur.

Örnek state gövdeleri:

// default: ilk yükleme (kullanıcı tercih bildirmedi)
{ ad_storage:'denied', analytics_storage:'denied', ad_user_data:'denied',
ad_personalization:'denied', functionality_storage:'denied',
personalization_storage:'denied', security_storage:'granted' }

// update · "Tümünü Kabul Et": hepsi granted
{ ad_storage:'granted', analytics_storage:'granted', ad_user_data:'granted',
ad_personalization:'granted', functionality_storage:'granted',
personalization_storage:'granted', security_storage:'granted' }

// update · "Zorunlu Olanlar": yalnız security_storage granted, diğerleri denied
{ ad_storage:'denied', analytics_storage:'denied', ad_user_data:'denied',
ad_personalization:'denied', functionality_storage:'denied',
personalization_storage:'denied', security_storage:'granted' }

Çerez silme uyarısı: Bir key'i denied yapmak, Google etiketlerinin o andan itibaren çerez okuma/yazmasını durdurur; tarayıcıda daha önce oluşturulmuş çerezleri ise silmez. Çerezleri tamamen temizlemek gerekiyorsa bu, SDK tarafında document.cookie üzerinden manuel yapılır — Consent Mode bunu yapmaz.

6.4 GTM Console'da kurulacaklar (özet)

  • 7 key built-in → ek custom Consent Setting gerekmez.
  • dataLayer yolunu seçtiyseniz: Console'da default_consentDefault ve consent_updateUpdate Consent Command'larını ve bunları dinleyen trigger'ları tanımlayın. Event ↔ Command eşleşmesi kurulmazsa dataLayer push'ı sonuçsuz gider. (GA4 gtag yolunu kullanıyorsanız bu gerekmez — gtag('consent', …) doğrudan çalışır.)
  • Key adları birebir aynı olmalı (küçük harf): ad_storage, analytics_storage, ad_user_data, ad_personalization, functionality_storage, personalization_storage, security_storage. Büyük harf veya eski/farklı ad kullanmayın.

7. Doğrulama (Sunucu tarafında)

Sunucu kaydı işlemekten önce aşağıdaki kontrolleri yapar; ihlalde ilgili hata kodu döner:

#Kuralİhlal → Hata
V1essential'in statedenied (kilit)ESSENTIAL_LOCKED (sunucu granted'e zorlar + uyarı)
V2Her consent anahtarı (= groupId), config groups[].id içinde olmalıUNKNOWN_GROUP
V3statedeniedINVALID_STATE
V4actionType === "modify" ise consent haritası non-empty olmalıMISSING_CUSTOM_STATES
V5actionType ∈ {accept_all, reject_all} + explicit grup state'leri aksi yönde ise tutarsızACTION_STATE_MISMATCH
V6iOS + promptATTdialog === true + onay ≥1 ATT'ye tabi gruba granted + attention.status == notDetermined → kayıt REDDEDİLİR (SDK önce ATT'yi çözmeli). denied/restricted reddedilmez (terminal/kullanıcı tercihi) → ATT'ye tabi gruplar GTM'de AND gate ile denied (§7.4)ATT_REQUIRED

contentVersion kontrolü: request.contentVersion, config'in mevcut contentVersion'ından farklı (daha eski) bir sürümse kayıt tutulur, ancak çıktı reprompt gerektirebileceğini işaret eder (kullanıcı eski içeriğe onay verdi, içerik değişti → bir sonraki açılışta tekrar sor).


8. Hata Kodları

Hatalarda ortak gövde döner: { success:false, code, message, retryable, requestId }. message teşhis metnidir; UI'da gösterilmez (kullanıcıya gösterilecek metin config localization'ından gelir).

codeHTTPretryableAçıklama / SDK müdahalesi
OK200Onay eylemi başarılı işlendi.
INVALID_TOKEN401Evettoken geçersiz/süresi dolmuş → yeniden AUTH.
SIGNATURE_MISMATCH401HayırHMAC imza doğrulanamadı → imza girdisi/token kontrolü.
REPLAY_DETECTED401HayırNonce tekrar/timestamp replay penceresi dışı → cihaz saati + nonce.
CONSENT_NOT_FOUND404HayırconsentId kaydı yok.
ESSENTIAL_LOCKED400Hayıressential denied olarak gönderildi → sunucu granted'e zorlar.
UNKNOWN_GROUP400HayırGeçersiz groupId → config groups[].id'lerle karşılaştır.
INVALID_STATE400Hayırstate granted/denied dışı.
MISSING_CUSTOM_STATES400Hayırmodify gönderildi ama consent haritası boş → SDK grup durumlarını üretmeli.
ACTION_STATE_MISMATCH400HayıractionType ile explicit group state'leri tutarsız.
ATT_REQUIRED400EvetiOS + ATT'ye tabi grup onaylanıyor + attention.status == notDetermined. → SDK ATT'yi çöz (requestTrackingAuthorization) ve yeniden post. denied/restricted bu kodu üretmez (§7.3/§7.4).
RATE_LIMITED429EvetHız sınırı → Retry-After kadar bekle.
SERVER_ERROR500EvetSunucu iç hata → onay önbellekte bekletilir, arka planda yeniden denenebilir.

Önemli (KRİTİK): Onay eylemi başarısız olursa (500/429/ağ), SDK onay kararını yalnızca cihazda (localState) saklar ve bağlantı gelince yeniden post eder — kullanıcının onayı kaybolmaz. Sunucu 500 dönerse mükerrer kayıt riski eventId-tabanlı idempotency ile önlenir.


9. iOS ATT Katmanı (kısa)

iOS'ta reklam/hedefleme takibi, cookie consent yanında işletim sisteminin ATT (App Tracking Transparency) iznini de gerektirir. Bu bölüm, ana akışın iOS özel yan dallarını özetler.

Android'de ATT karşılığı yokturattention: null, bu bölüm devre dışıdır (no-op). ATT bir HTTP endpoint'i değil, iOS sistem iznidir. Sunucuyla tek temas noktası, isteğe attention alanı eklemektir; sistem prompt'u SDK tarafından AppTrackingTransparency (ATTrackingManager) ile atılır.

9.1 İki ayrı izin katmanı

KatmanNe izlerKim yönetir
Cookie consent (bu SDK)Depolama + teknoloji kullanımıSDK / CMP
ATT (iOS sistemi)IDFA ile çapraz-uygulama reklam takibiİşletim sistemi

Kullanıcı consent'te targeting'i onaylasa bile ATT verilmediyse reklam sağlayıcıları IDFA'yı okuyamaz → takip fiilen yapılamaz. Bu yüzden onay akışıyla ATT uyumlu çalıştırılır ve sonuç GTM'e doğru yansıtılır.

9.2 Durum modeli (4 durum)

SDK, ham ATT sonucunu 4 duruma haritalar. İstekteki attention.status:

statusAnlamOnaya etkisi
notDeterminedHenüz sorulmadıATT'ye tabi grup onaylanacaksa sistem prompt atılmalıdır; çözülmeden takip onayı kaydolunmaz (V6).
authorizedKullanıcı takibi izinlediATT'ye tabi gruplar onaylanabilir → takip çalışır.
deniedKullanıcı takibi reddettiKayıt engelLENMEZ; ATT'ye tabi gruplar GTM'de denied (§9.4) + danışma mesajı.
restrictedEbeveyn/MDM sistemi takibi engellediKullanıcı kendi başına değiştiremez; kayıt engelLENMEZ; ATT'ye tabi gruplar GTM'de denied + bilgilendirme.

9.3 Ne zaman sistem prompt atılır? (just-in-time)

Sistem dialog'u açılışta herkese değil, gerektiği anda atılır (çift-sorma yorgunluğunu önler). Prompt atılır yalnızca aşağıdakilerin tümü sağlanırsa:

  1. platform === "iOS" VE
  2. behavior.promptATTdialog === true VE
  3. attention.status === "notDetermined" VE
  4. Kullanıcının onayı ≥1 ATT'ye tabi grubu granted yapıyor (§9.5'teki ATT'ye tabi key setiyle kesişimi var).

Bu koşullar sağlanmıyorsa (örn. reject_all, ya da targeting kapalı modify) prompt atılmaz; ATT notDetermined kalır ve bu sorun değildir (takip istenmedi, V6 tetiklenmez).

9.4 AND gate (onay ↔ ATT birleşimi)

ATT'ye tabi bir grubun GTM'de fiilen geçerli olması için kullanıcının tercihi VE sistem ATT izni birlikte granted olmalıdır:

effective(grup) = granted → user_choice(grup)==granted VE attention.status==authorized
effective(grup) = denied → yukarıdaki koşul sağlanmazsa
(ATT'ye tabi olmayan gruplar: effective = user_choice; ATT'den etkilenmez)
Kullanıcı (targeting)attention.statusGTM'de etkiliKullanıcıya gösterilen
deniedherhangidenied(normal)
grantedauthorizedgranted(normal)
granteddenieddeniedDanışma: "Ayarlar'da takibi kapattınız; tercih kaydınızda ama takip sistemi gereği devre dışı." + Settings kısayolu
grantedrestricteddeniedBilgi: "Takip, ebeveyn/sistem ayarı nedeniyle engelleniyor."

Böylece kullanıcı "Tümünü Kabul Et" dese bile ATT verilmediyse reklam key'leri GTM'de denied kalır → izinsiz takip gerçekleşmez. Tercihi kaydetmek (audit) ile takibi fiilen bloklamak (ATT) ayrı halledilir; displayATTStatus: true ise bunu kullanıcıya şeffafça gösterir.

9.5 ATT'ye tabi key'ler (kod tarafı)

ATT yalnız IDFA/çapraz-uygulama reklam takibini kapsar:

const ATT_GATED_KEYS = new Set(["ad_storage", "ad_user_data", "ad_personalization"]);
function isATTGated(group) {
return group.consentKeys.some(k => ATT_GATED_KEYS.has(k));
}

Kural: "ATT'ye tabi grup" = consentKeys ∩ {ad_storage, ad_user_data, ad_personalization} ≠ ∅ olan gruptur (tipik olarak targeting). tracking (analitik), functionality ve essential ATT'den bağımsızdır — kullanıcı analitiği onaylasa bile ATT reddetse analitik çalışır. Bu ayrım, ATT prompt'unu yalnızca gerçekten reklam takibi gerektiğinde atmak için kritiktir.

9.6 iOS kurulum notları

  • @import / import AppTrackingTransparency; durum ATTrackingManager.trackingAuthorizationStatus, prompt ATTrackingManager.requestTrackingAuthorization(completion:).
  • NSTrackingUsageDescription (Info.plist) ZORUNLUDUR — yoksa requestTrackingAuthorization() uygulama crash'er. Kullanıcıya "neden soruyoruz" metnidir.
  • Apple once-only kuralı: sistem dialog'u yalnız bir kez (durum notDetermined iken) gösterilir. Kullanıcı reddettiyse dialog yeniden açılmaz; bu durumda SDK kendi yeniden-soru UI'ı + Settings derin bağlantısı ile kullanıcıyı yönlendirir.
  • restricted türetimi: restricted bir Apple enum'ı değildir; ebeveyn/MDM engeli ham .denied'i verir. SDK bu durumu yardımcı sinyalle ayırarak restricted olarak modeler (kullanıcı geri alamaz → kayıt bloklamaz).
  • ATT durumunu cihazda kalıcı saklayın; sonraki her POST_CONSENT'te attention.status'u taşıyın (audit + V6 doğruluğu + retrigger tutarlılığı).

10. Developer Notları

  1. Onay, grubun kimliğinde (groupId) taşınır — istekte ve yanıttan aynı düz map formatı: { [groupId]: state }. Item/teknoloji bazında ayrı state YOKTUR. Etiketlere çevirimi SDK tarafında yapar (6.2).
  2. state yalnız granted / denied. Ara durumlar desteklenmez.
  3. essential kilitli. security_storage her zaman granted; kullanıcı kapatamaz. Sunucu gerekirse granted'e zorlar (V1).
  4. Kimlik = token + HMAC. Her onay post'unda ayrı API key taşımak yerine, açılışta alınan tek token kullanılır (§2.2).
  5. Geri besleme döngüsü: Yanıt localState, bir sonraki AUTH'ın girdisidir. installedContentVersion ≠ remote contentVersion ise reprompt tetiklenir. Bu zincir "onay ver → içerik değişsin → yeniden sor" davranışının omurgasıdır.
  6. Idempotency: Aynı onay eylemi (ağ tekrarında) eventId/nonce ile tek kayıt olarak işlenir; mükerrer consent record üretilmez. consentRecordId raporlama/audit akışına akar.
  7. groupId → consentKeys çevirimi SDK sorumluluğudur: Her groupId'yi yerel config'deki groups[].consentKeys kümesine yayın. Bir groupId yerel config'de karşılık bulamazsa (config/içerik sürüm farkı) o key'i uygulayamazsınız → güvenli yığın/uyarı üretin; kullanıcının onayı localState sayesinde kaybolmaz.
  8. IOS'ta ATT'yi çöz, sonra post: ATT_REQUIRED alırsanız requestTrackingAuthorization ile ATT'yi çözün (notDeterminedauthorized/denied) ve yeniden post edin. denied/restricted kaydı bloklamaz; yalnızca AND gate (9.4) reklam key'lerini kapatır.