Everything touching Segment → Amplitude / Customer.io / Meta, plus RevenueCat and Anthropic as data processors — re-checked tonight against the actual live code, not the intended design or the previous version of this page. Three previously-undocumented gaps turned up that way, two of them already fixed. Sorted by how much each actually matters, not by when it was found.
git branch -a plus a diff against main on both repos (iOS branch fix/apple-signin-email-scope; the backend has 7 open branches touching consent, erasure, export, and pseudonymization), and a direct read of every consent-adjacent surface — Allin/Home/SettingsView.swift, ConsentSheet.swift, AnalyticsClient.swift, MetaAdsClient.swift, and every backend function that talks to Segment (recommend-plan, revenuecat-webhook, delete-user-data). One claim — "Meta advertising is properly gated" — got checked against the actual linked FBSDKCoreKit SDK source (a real local checkout under Xcode's DerivedData, not the docs or this app's own comment asserting it), because an unverified claim about consent gating is exactly the kind of thing that's easy to assert once and never check again. That check is what surfaced the finding below. Three real, previously-undocumented things came out of this pass — two already fixed, one already resolved on a branch: (1) two backend functions were forwarding real analytics events — for one of them, the user's actual check-in areas and 1–10 ratings — to Segment with zero consent check, because they run entirely server-side, outside the app's own consent gate; (2) Meta was receiving real purchase and registration events regardless of advertising consent, because the setting this app's own code (and this page) claimed gated that only gates Meta's automatic events, not the explicit ones actually being sent; (3) the recommend-plan repo/live drift flagged last time as an active, unresolved risk has, since, actually been fixed and committed — it just hasn't been pushed. Terms like "edge function," "migration," "committed vs. deployed" are the same vocabulary as the GDPR page's glossary — not repeated here.delete-user-data's own code comments rather than assumed: Segment's user-deletion/suppression API ("Regulations," the one that would actually erase a person's record inside Segment itself) requires a Team/Business-tier workspace, not Free. So even once the missing SEGMENT_ACCESS_TOKEN/SEGMENT_SOURCE_ID are resolved, Segment-side erasure may still not work on the current plan — worth knowing before spending more effort chasing the token alone. Not urgent: Amplitude and Customer.io erasure already work independently of this (see the medium-priority item below).user_entitlements table live, linked to RevenueCat via revenuecat_app_user_id, updated by the RevenueCat webhook — as before. Newly confirmed by reading the actual webhook code (not assumed from the table existing): it also forwards every subscription lifecycle event — started, renewed, cancelled, billing issue, expired — to Segment → Amplitude/Customer.io itself, server-side, on RevenueCat's own schedule. That's a separate, direct integration from anything the iOS app's AnalyticsClient sends from the device — see the consent-gate finding below for why that distinction mattered.classify-affect, classify-crisis, recommend-plan) — only check-in/intake free text goes across, nothing identifying.recommend-plan forwards a plan_block_generated Segment event and a Customer.io identify() call carrying the user's real selected areas and 1–10 ratings — literally which belief areas they're least satisfied with — every time a plan is generated. revenuecat-webhook forwards every subscription lifecycle event the same way. Both fire completely unconditionally: no check against the user's analytics-consent choice, ever, at either site.AnalyticsClient's consent gate (hasAnalyticsConsent, checked before .configure()/.identify()/.track()) only covers events the iOS app decides to send from the device. Both of these run server-side — one triggered by an HTTP call, one by RevenueCat's own webhook — so there was never a client-side check to bypass; nothing stood in for it on the server either._shared/consent.ts helper that looks up the most recent analytics row in user_consents (the same audit-trail table from the consent-RPC fix below) for a given user id, and gates both call sites on it. Fails closed on purpose: no row on record, the migration not deployed yet, or a lookup error all resolve to "don't send" — matching what GDPR actually requires (affirmative consent, not silence read as yes).fix/server-side-analytics-consent-gate (merges combined-fixes-2026-08-15 + feat/gdpr-consent-record). Depends on the user_consents table actually being live — until then this just means the events stop firing, not that anything errors.user_consents (it didn't exist until tonight), so the moment this deploys, subscription/plan analytics for every current user goes quiet in Amplitude/Customer.io until they revisit the consent screen or Settings → Privacy. Whether to backfill a one-time default for pre-existing users — and if so, granted or not — is a real product call for Marie + Isabel, not made here.MetaAdsClient's enable/disableTrackingSettings() toggle two SDK settings (isAutoLogAppEventsEnabled, isAdvertiserIDCollectionEnabled) based on the user's advertising-consent choice — that part was real and correct. But trackPurchase() and trackCompleteRegistration() — called unconditionally, no consent check at either call site (SubscriptionManager.swift on every successful subscribe, AuthenticationManager.swift on every Apple sign-in) — call Meta's SDK through a different path (AppEvents.shared.logPurchase / .logEvent) that those two settings don't actually gate at all.isAutoLogAppEventsEnabled only gates logInternalEvent — the SDK's own automatic events. The explicit logEvent/logPurchase path (doLogEvent:) has no consent-style check in it at all, only a remote kill-switch. So a user who explicitly declined advertising consent was still generating a real Purchase event — actual amount and currency — and a CompleteRegistration event inside Meta's system.MetaAdsClient itself — one hasConsent gate reused by track, trackPurchase, trackStartTrial, and trackCompleteRegistration — instead of relying on every call site to remember to check first, since that's the exact pattern that had already failed twice.fix/apple-signin-email-scope (not yet pushed). No visual or copy change — this only changes which network calls fire.record_consent_granted every time someone completes the onboarding consent sheet. That function didn't exist anywhere in the backend — not in a migration, not deployed. The call is wrapped in a silent try?, so it failed with zero error and nobody would have noticed. There was no durable, provable record anywhere that a person had ever consented to anything.user_consents table (one row per grant/withdrawal: user id, which consent, on/off, timestamp) plus record_consent_granted (the 3-way call the onboarding sheet makes) and record_consent_change (a second function for single-toggle changes). The onboarding side was wired first; Settings → Privacy's two toggles were wired to record_consent_change this session — before that, changing your mind after the fact wasn't recorded at all, only the very first choice was (once the RPC itself existed).feat/gdpr-consent-record. iOS wired on both entry points — branch fix/apple-signin-email-scope. Not yet deployed/pushed on either side.recommend-plan tracked on main was FTS-only (no Anthropic call) while the function actually running in production used a Claude/Anthropic composition path — meaning the next unrelated push to main would have silently redeployed the stale version over the correct live one.supabase functions download, not guessed from a binary bundle) and every function now matches what's really running — recommend-plan, plus three previously-untracked functions (recommend-audio, classify-affect, classify-crisis) that existed live but nowhere in the repo. CI also changed so each function only redeploys when its own folder changes, closing the exact mechanism that made this dangerous.fix/recommend-plan-repo-drift (also present in combined-fixes-2026-08-15). What's left isn't technical — it's approval to push, since main today still doesn't reflect any of it.delete_user()) is written, committed, and confirmed to have the secrets it needs already live. It's just never been deployed.fix/apple-signin-email-scope branch's deleteAccount() already calls delete-user-data's URL instead of the raw delete_user RPC. The live App Store build still correctly calls the raw RPC (confirmed separately on main), so nothing is broken today — but it means delete-user-data has to be deployed before or in the same release as that iOS branch ships. If the iOS update goes out first, every account deletion hits a 404 against a function that doesn't exist yet.user_entitlements already works independently of this.We want to make this practice better,
your choice, for each of these.
record_consent_granted. Source: Allin/Home/ConsentSheet.swiftprivacy & data
record_consent_change. Source: Allin/Home/SettingsView.swift — PrivacyConsentViewCross-references: the fuller GDPR picture (pseudonymization, encryption, consent copy sign-off, the new "download my data" Settings entry point) lives on the GDPR & onboarding wiki. The DPA checklist itself is ~/Documents/allin-gdpr/DPA_CHECKLIST.md.
One CRM-specific item below has a ready prompt — paste it into your own Claude. The rest of your open items touching this workstream (consent-screen sign-off, the age-gate call, marketing claims) already live on the GDPR page's own "who does what" — not duplicated here.
user_consents migration together with the new server-side consent gate (one doesn't do much without the other live) · decide the pre-existing-user backfill question for that gate, with Isabel · regenerate the RevenueCat v2 API key · re-check Segment's plan tier before investing more in the missing access token, given the Team/Business-tier requirement found tonight.