The store now tells you your users' ages. Google's terms forbid putting it in analytics.
Play's Age Signals terms limit the signal to age-appropriate experiences and name advertising, marketing, profiling and analytics as prohibited uses, with app takedown as the stated penalty. Apple's version hands you the age plus how it was proven. Both are live now in a widening list of regions, and the default engineering reflex is wrong for both.
There's a new field arriving in mobile apps this year and almost nobody is treating it as a product decision. It's the user's age, handed to you by the store instead of asked for in your onboarding.
Here are the dates, from the platforms' own developer docs rather than the coverage. Apple says that for new Apple Accounts in Utah as of 6 May 2026 and in Louisiana as of 1 July 2026, age categories are shared with your app when you ask through the Declared Age Range API. Separately, since 24 February 2026 Apple blocks users in Australia, Brazil and Singapore from downloading apps rated 18+ unless they've been confirmed as adults. Google's Play Age Signals API started returning signals for Brazil on 17 March 2026 and for eligible Texas users who created their accounts after 28 May 2026. More states are queued behind those.
Two things in there that I think will bite people.
The one that's a takedown risk.
Google's terms of service for the Age Signals API say you may only use the information to provide age-appropriate content and experiences in compliance with law, and then names what you may not use it for: advertising, marketing, user profiling, or analytics. The stated consequence for a prohibited use is termination of API access and suspension or takedown of your apps.
Now think about how a new user property actually enters a codebase. Someone adds it to the user object. The analytics SDK is configured to forward user properties. It's in Mixpanel by Thursday and in the warehouse by Friday, and nobody made a decision at any point, because forwarding user properties is what the pipeline is for. Then in the next planning cycle a perfectly reasonable person breaks retention down by age band, because it's sitting right there in the dropdown.
That path has no decision point in it. It's a default. And it lands you squarely in a clause with app removal attached to it. If you integrate this API, the work isn't the integration, it's the wiring you have to actively prevent.
The one that's more interesting.
Apple's API doesn't just tell you an age band. It tells you how the age was established. The declaration comes back as self-declared, guardian-declared, checked by another method, government-ID checked, or payment checked, with guardian equivalents for each. There's also a signal for whether regulatory requirements apply to this user at all, and whether they're required to share.
So age arrives with a provenance attached. And I'd bet almost every implementation collapses it to a single isMinor boolean on the first day and throws the rest away, because that's the shape the rest of the code wants.
I want to be honest that for a lot of apps, discarding it is the right call. If your only use is "don't show this content to a 13-year-old," you behave identically whether the 13 was typed in or proven by ID. But there's a class of product where the difference is the whole thing: anything where a wrong answer is expensive, or where you're deciding how cautious to be rather than what to hide. Self-declared 18+ on a device with parental controls off is a different fact from ID-verified 18+, and the API is telling you which one you have. Losing that in a cast is a decision. Make it on purpose.
The part that touches your roadmap.
Both platforms now have a mechanism for a "significant app update" that requires a parent's approval before a child can keep using your app. Apple is explicit that until the parent consents, the child must be prevented from accessing the update, which may include all app and account data or specific features, and that if a parent revokes consent, Apple will prevent the app from launching. You get a RESCIND_CONSENT server notification when that happens. Google has the equivalent: notify Play of significant changes, and handle revoked approvals.
Neither of them defines significant. Apple says plainly that you determine what constitutes a significant app update, based on applicable law, and to consult your counsel.
That's the sentence I'd sit with. A definition you write yourself now controls whether shipping a given release locks a cohort of your users out of their own data pending someone else's tap. For an AI product it's genuinely unclear where the line sits. Is swapping the underlying model a significant change to what the app does? Is broadening what the assistant will discuss? I don't think anyone knows yet, and I'd rather have written down my answer before a release forces one.
Where I actually am on this.
Murror is an emotional-reflection product, so the age of the person on the other side is not a compliance field to me, it's the most load-bearing context I could have. And I still don't have a clean answer on the verification-method question, because the honest version is that I don't yet know what I'd do differently with a government-ID-checked 16 versus a self-declared one. I'd rather say that than write a paragraph pretending I've resolved it.
The thing I am confident about is narrower. The signal is sparse for a long while yet. Texas only covers accounts created after a date in May, Apple's state coverage is new accounts only, and users can decline to share. So any product logic that assumes you now know everyone's age is wrong, and the interesting engineering question is what your app does in the large unknown bucket, which is where most of your users will sit for the foreseeable future.
Four things I'd do this week if you ship a mobile app. Check whether your analytics SDK auto-forwards new user properties, before you add one you're contractually barred from analysing. Decide, in writing, whether verification method changes your behaviour. Write your own definition of a significant update while it's cheap. And implement the revocation notification, because an app that can be prevented from launching is a support-load event you'd much rather see coming than hear about.
None of that is hard. It's just that none of it happens by default, and the default here has a penalty clause attached.


Replies
The analytics part is where I want to push back a little. "Check if your SDK auto-forwards user properties" is a one-time check. Checks like that get old. The next person who adds a property has no idea the check ever happened.
The version that lasts is different: never put age on the user object. Read it right where you need it, use it, and let it go. Then the pipeline has nothing to forward, and there is no field in the dropdown for someone to break retention down by three months later.
For Murror you said age is your most important context, so you cannot keep it out of the user object, and you are back to the check. Then I would at least make the check fail loudly. We have a test tht greps the code for old claims we dropped, and it fails in CI when one comes back. The same idea works for a field you are not allowed to log: one test that fails if the age key shows up near the analytics payload.
Your point about writing your own definition of a significant update is the same story. I agree with it. I do not know what that test looks like for a model swap
@siarheihamanovich The "make it fail loudly" framing is the correction I needed. You're right that a one-time audit is a fact about one afternoon, not a fact about the codebase, and I wrote it as if it were the latter. The grep-in-CI trick is the part I'm taking: a test that fails when a key you're contractually barred from logging turns up near an analytics payload is cheap, and it survives the person who wrote it leaving.
On not putting it on the user object at all, I think you're right and I let myself off too easily. Saying age is load-bearing context for Murror doesn't actually mean it has to live on a long-lived user record. It means it has to be available at the moment we're deciding how to respond. Read at point of use and let it go is compatible with that, and I was conflating "I need this" with "I need to store this."
No idea what the test looks like for a model swap either. The closest I've got is that it isn't a test at all, it's a written rule someone has to consciously override, which is a much weaker instrument and I don't love it.
@monatruong_murror The written rule is weak, but you can make it act like a test. Keep the model id in one place, then write a test that checks that exact id. Now you can't change the model without changing the test too, and the rule can sit right there in the test file. You still decide yourself, however you can't walk past it by accident. It doesn't help with your other case. "What the assistant will discuss" is not one line you can pin, so I have no good answer for that one either.
@siarheihamanovich Pinning the model id and asserting it in a test is better than what I had, and the reason is subtle: it doesn't stop you doing the thing, it just makes the thing impossible to do silently. That's the actual failure mode I was worried about. The rule sitting in the test file next to the assertion is the part I'd have missed, because that's where the person changing the id is already looking.
Agreed there's no clean pin for what the assistant will discuss. The closest I've got is to stop trying to pin the rule and pin the behaviour instead: a fixed set of prompts near the edge of what we want it to engage with, run on every model or prompt change, where the thing that fails is a change in whether it engages rather than a change in wording. That's a snapshot test with fuzzy matching and it's uglier than yours. But it does catch the case where nobody touched the rule and the behaviour moved anyway, which is the case I actually can't see today.
The significant update question reaches me and I don't even ship a mobile app. Swapping which model answers mid conversation is the entire product on our side, so if that counts as a significant change to what the app does, our normal Tuesday is a consent event. My guess is the line sits on what the assistant will discuss rather than which weights produced it, but it's a guess and I'd rather have written it down than discover it in review. Where I'd push back: you say collapsing provenance to isMinor is often the right call, and I think it's almost never right anywhere refusing is an option. Self declared isn't the case where you behave identically, it's the case where you should be more cautious, and throwing the field away removes your ability to be.
@asadmalik901Â You've moved me on the provenance point. I was writing about apps whose only lever is hide or show, and for those I still think the collapse is defensible. But you're right that the moment refusing, hedging or asking again is available, self-declared and ID-checked should produce different behaviour, and the direction is the opposite of the intuitive one: weaker evidence should make you more cautious, not less. Collapsing to a boolean deletes exactly the input you'd need for that. "Almost never right anywhere refusing is an option" is a better rule than the one I wrote.
On where the significant-update line sits, I've landed in the same place as you, and I think the reasoning that supports it is that the consent a parent gave was about what the app does to their child, not about which vendor's weights are behind it. Swap the model and keep the behaviour inside the envelope you described and nothing the parent agreed to has changed. But I notice that conclusion is convenient for both of us, which is a reason to hold it loosely rather than a reason to doubt it. What I'd actually do is write it down with the reasoning attached, so if it's ever tested you can show it was a judgement someone made deliberately rather than a gap nobody noticed.
@monatruong_murror Fair flag on the convenient conclusion, so here's the version that isn't convenient for me. If consent was about behaviour and not weights, then a routing change that quietly widens what the assistant will say is significant even though no model got swapped, and routing changes are the ones nobody announces. A model swap is at least visible in our UI. The route landing on a model with a looser refusal profile isn't visible anywhere, which is backwards. So the thing I'm writing down is the envelope, plus an alarm for when a route starts answering things the old one declined.
@asadmalik901Â That inverts my framing and I think you're right. I was treating the model swap as the risky event because it's the one that feels like a change. But the events that feel like changes are exactly the ones that get announced, reviewed and noticed. The ones that don't feel like changes are where behaviour actually drifts, and a route landing on a looser refusal profile has no artifact attached to it anywhere.
The alarm sounds hard until you notice you don't have to judge whether the new answer is good. You only have to notice that something previously declined is now answered, per route, against a fixed set of edge prompts. Direction of change is enough to page a human. I'd been trying to build the version that decides whether the change was acceptable, which is exactly why I never built it.
@monatruong_murror The trap in the fixed prompt set is that it goes stale in exactly the direction you care about. Ours started at 40 prompts and maybe a third of them stopped being edges within two months, so the suite keeps passing while the real boundary moved somewhere nobody's looking. What's worked better is refreshing the set from actual refusals in the logs, so users decide what counts as an edge instead of us guessing. Still nothing for the route that gets looser on something no one has tried yet.