Age rating and content rights
The two review declarations that are single answers rather than a generated set, both read from declarations.config.json.
Both are in Apple’s public API, so the App Store Connect key is enough for them. Both are also statements you stand behind, so pushing them is gated the same way the privacy declarations are.
{
"humanConfirmed": false,
"contentRights": null,
"ageRating": {
"all-none": true,
"user-generated-content": false,
"messaging-and-chat": false,
"social-media": false
}
}Prerequisites
- Store credentials: the App Store Connect
key, and
ascinstalled - An App Store Connect app record, so there is something to declare against
Answer content rights yourself
contentRights ships as null and the script will not fill it in. There is no safe
default, because both answers assert something.
| Answer | What you are saying |
|---|---|
DOES_NOT_USE_THIRD_PARTY_CONTENT | Nothing in the app is licensed from someone else |
USES_THIRD_PARTY_CONTENT | There is third-party content, and you hold the rights to it |
The second answer is a claim, not a disclaimer. Picking it because it sounds like the cautious option asserts a licence you may not have.
Whoever owns the app decides this, once. Write the answer into declarations.config.json
and it stays there for every later release.
The age-rating answers that ship
The default is all-none, every question answered NONE or false, with three answers
written out as false. Apple defines all three by what happens between users, in its
age rating definitions :
| Answer | Apple’s definition | Set it to true when |
|---|---|---|
user-generated-content | “the broad distribution of content created by users” | Users can publish content that other users see |
messaging-and-chat | “Users can directly communicate with one another” | Users can message each other |
social-media | “Redistribution, amplification, or interaction with user-generated content through a social feed or similar discovery method that visibly spreads content to many users” | The app has a feed, or something like one, that spreads user content. App Store Connect accepts it only with user-generated-content also true |
One user talking to a model is none of these. The app as shipped has no feed, no public
profiles and no messages between users, so chat, create and scan leave all three
false. Answering true to be safe is also a misdeclaration: it declares capabilities
the app does not have, and it raises the rating in regions that rate them.
Apple has required the social media answer on every new app and update
since September 2026 , which is why it
is written out and not left to all-none. Its flag needs asc 3.1.0 or newer. Run
brew upgrade asc if asc age-rating edit --help does not list --social-media.
There is no question about AI or chatbots. What the AI features affect is the content
questions that all-none answers NONE: profanity, mature themes, violence and the
rest. Those cover what the models return as well as what you wrote, so raise the ones
your models and prompts can produce.
asc age-rating edit --help lists every question. In the ageRating block,
"all-none": true is a bare switch. Every other key takes its answer, true, false
or a level such as "INFREQUENT_OR_MILD", and is sent as --key value, so adding
"gambling": true sends --gambling true and an explicit false overrides what
all-none set. null leaves a question unanswered.
Push both
Read the answers
node .claude/skills/submit/scripts/declarations.mjsIt prints the age-rating flags it would send and the content-rights answer, or the two
options if it is still unanswered. Nothing is sent. Under the flags it adds a note when
social-media is unanswered, when an answer is one App Store Connect would refuse, and
for each of the three between-users answers set to true, with what that answer
claims. While chat, create or scan still ship, it also notes that all-none covers
what the models return.
Confirm
Answer contentRights, then set the flag:
"humanConfirmed": true--push fails on an unanswered contentRights before it even looks at the flag, because
only you can say whether the app uses third-party content.
Push
node .claude/skills/submit/scripts/declarations.mjs --push --dry-run # see the asc commands
node .claude/skills/submit/scripts/declarations.mjs --pushVerify
asc age-rating view --app "$ASC_APP_ID" --output tableAge-rating completeness is also one of the conditions
asc validate aggregates before a
submission, so a missing answer shows up there too.
Re-push on a later release only if the answers changed.
Google’s content rating is a Play Console job
Google’s equivalent is the IARC questionnaire, and it is not in the API. Neither are target
audience, ads, the news and financial-app flags, or the
account-deletion link. play.config.json’s playConsoleOnly
block records those answers so they are decided once in the repo rather than improvised at
the last minute, and the submit preflight lists whichever are still unanswered. Entering
them is Play Console → Policy → App content.
"playConsoleOnly": {
"humanConfirmed": false,
"targetAudience": null,
"containsAds": null,
"isNewsApp": null,
"contentRatingSubmitted": null,
"accountDeletionUrl": null
}Answer the questionnaire by the same reading as Apple’s. IARC’s interaction question is about users exchanging content with each other, and one user talking to a model is not that.