Allin — internal
Enter the password to continue.
Allin — Data, CRM & Comms

Allin — data, CRM & comms

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.

How this list was built

So you can judge whether it's actually complete, not just trust it
Every claim below was re-verified, not carried over: 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.
How to read this: Each item below is either something already true and verified (green, top section) or something that needs a decision or an action (High/Medium/Low). "High" means a real legal exposure or a silently-broken mechanism right now. "Low" means genuinely fine to leave for later, no live risk in the meantime.

Already confirmed done

Amplitude no longer receives your email

Done — live
The Segment mapping that sent it as a User Property was removed and re-verified directly against the app's analytics code, not just the config screen. Live since 2026-08-16 — the only item on this whole page confirmed live in production, not just committed.

Customer.io is live and working

Done
Site ID + track API key confirmed by a real API call (workspace 216147, EU region).

Segment plan tier confirmed — plus a new caveat worth knowing

DoneSource: Segment dashboard + delete-user-data's own code
Free tier, 101 of 1,000 monthly tracked users (Aug 5–Sep 5 window) — as previously confirmed; not re-checked live tonight. New, found while reading 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).

Apple's DPA is already covered

Done
Governed by the Apple Developer Program License Agreement, already accepted. Nothing to do here.

Subscriptions are tracked — and also feed Segment server-side

DoneSource: supabase/functions/revenuecat-webhook/index.ts
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.

Confirmed, not assumed: no email/name/account id is sent to Anthropic

Done
Checked the exact code that builds every request to Anthropic (classify-affect, classify-crisis, recommend-plan) — only check-in/intake free text goes across, nothing identifying.

High priority

real exposure or a silently broken mechanism

Two backend functions sent real analytics with zero consent check — now gated

recommend-plan + revenuecat-webhook, entirely server-side, entirely missed by the app's own consent gate
Claude found + built it, Marie deploys
What was actually happening
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.
Why it slipped through
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.
What was built
A shared _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).
Status
Built and committed — backend branch 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.
Open question — not decided here
Every existing user has zero rows in 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.
See it
allin-backend · branch fix/server-side-analytics-consent-gate · supabase/functions/_shared/consent.ts
supabase/functions/recommend-plan/index.ts · supabase/functions/revenuecat-webhook/index.ts

Meta was receiving real events regardless of advertising consent — now fixed

This page (and the code's own comments) previously overstated how gated this was
Claude found + built it, Marie deploys
What was actually happening
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.
How this was checked, not assumed
Read the actual linked FBSDKCoreKit source directly (a local checkout under DerivedData, not the docs or this app's own comment): 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.
What was built
Moved the consent check inside 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.
Status
Built and committed — iOS branch fix/apple-signin-email-scope (not yet pushed). No visual or copy change — this only changes which network calls fire.
See it
Allin/Core/MetaAdsClient.swift

Consent wasn't actually being recorded anywhere — now built, on both entry points

Every "record_consent_granted" call was silently failing — and until tonight, so was every change made later
Claude built it, Marie deploys
What was actually happening
The app calls a Supabase function called 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.
Why it matters
GDPR Article 7(1) requires being able to demonstrate that someone consented — not just that the app currently behaves as if they did. The actual protection (not sending analytics/ads events if someone said no) is enforced separately, client-side, so that part was fine — this gap was specifically about having no audit trail to prove it.
What was built, and what's new tonight
A 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).
Status
Migration + both RPCs built and committed — backend branch feat/gdpr-consent-record. iOS wired on both entry points — branch fix/apple-signin-email-scope. Not yet deployed/pushed on either side.

Anthropic DPA — not signed

Newest-discovered processor, handles special-category text
Marie — active request, not a click-through
What's open
Anthropic processes real check-in and plan-intake text (memory, belief, trigger, reflection) to classify emotion/crisis risk and compose plans. Their standard terms say this isn't used to train models, but a formal signed agreement confirming that isn't in place yet. Unlike some processors, this likely needs an actual support/Console request, not just accepting a checkbox.
Who
Marie — I can't execute this, it's a real external ask.

Supabase DPA — not confirmed

The primary database, holding everything directly
Marie
What's open
Supabase hosts every table Allin has, including all the special-category check-in/plan text, directly and unencrypted. Of every processor on this page, this one holds the most, so an unconfirmed DPA here is the highest-blast-radius gap even though Supabase is infrastructure, not a "third party" in the way Segment/Amplitude feel.
Who
Marie — check Supabase's DPA acceptance/terms directly in their dashboard or with support.

recommend-plan repo/live drift — the fix itself is done, what's left is getting it live

Previously flagged as an active, unresolved redeploy risk — that part is now resolved
Marie — approve the push
What was wrong
The version of 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.
What's changed since
This has actually been fixed: the real live source was pulled directly (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.
Status
All of the above is committed — backend branch 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-data function — built, not deployed, now a real release-sequencing risk

iOS already calls it; deploying it late means every account deletion 404s
Marie — explicit go to deploy
What's open
The function that fans erasure out to Amplitude + Customer.io (in addition to the existing Supabase-side delete_user()) is written, committed, and confirmed to have the secrets it needs already live. It's just never been deployed.
Why this is now more urgent than "nice to have"
Confirmed directly in the code: iOS's 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.
Who
Needs your explicit go to deploy — this touches live infrastructure. Flagging the ordering now so whoever preps that release doesn't miss it.

Medium priority

real, but not urgent

Amplitude DPA — unconfirmed

Risk just went down, not away
Marie
Was "medium-high" on the original checklist specifically because of the email it used to receive — now that's fixed, the data flowing there is lower-sensitivity, but a signed DPA is still required regardless of how sensitive the data is. Worth confirming, not urgent.

Customer.io DPA — unconfirmed

EU-region workspace may not even need a transfer mechanism
Marie
Workspace 216147 is EU-region, so the usual cross-border-transfer concern may not apply at all — but that needs explicit confirmation rather than assuming, since "probably fine" isn't the same as verified.

RevenueCat API key needs regenerating

Legacy format, incompatible with the current API
Marie
The current secret key is legacy-format and doesn't work against RevenueCat's v2 API — needed for any direct entitlement/subscription auditing. Generate a new v2 secret key in the RevenueCat dashboard when convenient; not urgent since the webhook-based sync into user_entitlements already works independently of this.

Segment access token/source id — still missing

Blocks Segment-side erasure specifically; already deprioritized once
Dead end, per your earlier call
Amplitude/Customer.io erasure work independently of this and don't need it. This only affects deleting a person's raw record inside Segment itself, and there's no accessible UI path to get these credentials in this workspace. Restating its real priority (medium, not high) rather than re-opening it — you already called this one. Also see the plan-tier note in the Done section above: the API this would unlock needs a Team/Business-tier workspace, not just the credentials — worth checking both at once if this is ever revisited.

Low priority

genuinely fine to leave for now

Meta DPA — unconfirmed

Lowest-sensitivity data of any processor here
Marie, whenever
Only device/advertiser id ever reaches Meta, and only when both ATT and the advertising toggle are on (as of tonight's fix, that's now actually true for every Meta call, not just the ID itself). No special-category data involved.

RevenueCat DPA — unconfirmed

Contract-necessity basis, not consent
Marie, whenever
Purchases require processing subscription data as a matter of fulfilling the contract — no special-category data, lowest legal exposure of the group.

The two screens that actually gate this page

real mockups live on the GDPR page — shown here because this page is what they gate
Both screens below decide whether anything on this page fires at all — the onboarding consent sheet sets the initial grant; Settings → Privacy is where someone comes back to change it, which is also the half of the flow that only became provably recorded tonight (see the consent-record card above). Full copy, decision-tracking, and the "no separate decline button" open question live on the GDPR page — not rebuilt here, just shown so this page is self-contained to glance at.

We want to make this practice better,

your choice, for each of these.

🔔
reminders
📈
improve the app
📣
ads
continue
Onboarding consent sheet — sets record_consent_granted. Source: Allin/Home/ConsentSheet.swift

privacy & data

📈
improve the app
so we can see which sessions help
📣
ads
so we can show relevant ads elsewhere
Settings → Privacy — the revisit screen, now wired to record_consent_change. Source: Allin/Home/SettingsView.swiftPrivacyConsentView

Cross-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.

Who does what — pulled out on its own

I

Isabel

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.

Confirm the consent-screen wording matches what each toggle actually does
A copy-accuracy question, not a design one — the two toggles route to real, specific places
Prompt for your Claude
Open Allin's consent screen and Settings > Privacy screen copy (I can show you the mockups, or open https://allin-crm.pages.dev password isma2026 for the current wording). "Improve the app" sends data to Amplitude and Customer.io via Segment; "ads" sends data to Meta, and only when Apple's own tracking prompt is also allowed. Walk me through whether the current one-line descriptions for each toggle honestly represent that, in plain English a user would actually understand — and suggest better wording if not, so I can bring it to Marie.
M

Marie

DPA checks in progress (Anthropic, Supabase, Amplitude, Customer.io, Meta, RevenueCat) · approve pushing the recommend-plan repo-drift fix · explicit go to deploy delete-user-data — mind the release-sequencing dependency with the iOS branch · approve deploying the 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.