Most app rejections are not about the app. They are about the paperwork around it — a declaration that does not match behaviour, a policy page that does not exist, a promise the store requires and nobody made.

The uncomfortable part is that almost every declaration is answered from memory, by whoever is filling in the form, under time pressure, about a codebase they did not entirely write.

We went through this recently and answered the questions differently: by checking each one against the built application rather than recalling it. Several answers changed.

The declarations are the review

Google Play’s Data safety section asks what data your app collects, what it is used for, whether it is shared, whether it is encrypted, and whether users can request deletion. Apple’s equivalent asks a similar set.

These are binding statements. A mismatch between what you declared and what the app does is a policy violation, and it is the kind that gets found later — after release, when someone runs an automated scan or a user complains.

Our set ran to eight declarations. Getting them right took longer than expected, and the reason is instructive: the honest answer to several of them was not knowable without looking.

Three that were answered wrong from memory

“Does the app collect an advertising identifier?” The remembered answer was no, because nobody had written advertising code. The verified answer required checking the built manifest, because a dependency can request that permission without anyone deciding to. It was no — but confirmed against the build, not the intention.

“Does the app collect IP addresses?” This one took real work. The answer depends on whether anything stores one, which meant checking the database schema for an address column, checking whether any application code logs one, and checking the rate limiter — because a rate limiter has to key on something, and if it is backed by a store rather than memory, that key is persisted.

The verified answer was that nothing stores one: no column exists, no code logs one, and every rate limiter is constructed without a backing store so the key lives in memory for the window only. That is a defensible “no”.

But it came with a caveat we wrote down rather than buried: edge and load-balancer access logs are outside the application’s control. They may record addresses, and that is a hosting question rather than an app question. A declaration that does not name its own boundary is a declaration that will be wrong the day somebody enables logging.

“What is the target age group?” The remembered answer was general audience. The correct answer was 18+ with minors restricted at the store, which is a different declaration with different obligations attached.

★ Insight ------------------------------------- The pattern in all three: the remembered answer described what the team intended, and the verified answer described what the software does. Those diverge because dependencies carry behaviour nobody chose, and because infrastructure sits outside the code. A declaration is a factual claim about a built artefact, so the only defensible way to answer it is to inspect the artefact — the manifest, the schema, the actual configuration — rather than the codebase you remember writing. -------------------------------------------------

The pages the store requires

Both stores require reachable, working pages, and each is a common rejection:

A privacy policy at a public URL. Reachable without logging in, and actually describing your app. A generic template naming a different product is a rejection.

Account deletion, if the app has accounts. Both stores now require a way to request deletion, and Google requires it to be reachable from outside the app — a web page, not only a button behind a login. The reasoning is that someone who has uninstalled must still be able to ask.

Terms, where you have them. Less consistently enforced, and it is the page that carries your actual obligations.

One thing we got wrong and fixed: these pages need the same gating as the app. Our verification tool held a hardcoded list of pages to check, so when we added translated versions of the legal pages, those were outside the list entirely. A translated page could have lost its draft banner or its noindex and gone live while the English was still held back — the exact ungated surface the tool exists to prevent.

The translation trap

Worth its own note for anyone shipping in more than one language.

If your app offers another language, the single-language rule does not stop at the app’s edge. A user who selected Hindi and tapped the privacy link should not land on an English page — and in India, the notice is expected to be available in one of the scheduled languages.

Two things we would do again:

Every translated legal page carries a clause naming the English authoritative. Otherwise you have created a second source of truth that can diverge from the first, and neither is clearly the one that governs.

Translating unconfirmed content doubles the unconfirmed content. Our count of statements awaiting legal review went from 38 to 75 when the Hindi pages landed. That is arithmetically correct and it is the right way to count it — the same facts are now unconfirmed twice, and a reviewer should see both.

The rejections that are actually about the app

Declarations are the bulk of it, but three app-level things account for most of the remainder, and they are worth knowing because each has an easy fix.

A crash on the reviewer’s device. Reviewers test on current hardware and, frequently, on the newest OS — sometimes before your users are on it. An app that has not been launched on the current OS version is the most common avoidable rejection, and it is avoidable in the literal sense that launching it once would have found it.

A feature the reviewer cannot reach. If your app needs an account, the reviewer needs working credentials, and they need to still work weeks later when an update is reviewed. A demo account that expired is a rejection that looks like a policy problem and is an admin problem.

Placeholder content. Lorem text, a template icon, a screenshot from a different app. This sounds too obvious to mention and it is one of the most cited rejection reasons, because these artefacts survive from the first build and nobody looks at the store listing again.

The pre-submission checklist

Before you submit, for each declaration:

  • Answer it from the built artefact, not from memory. Manifest, schema, configuration.
  • Write down how you verified it. One line. It is what you will need when the answer is challenged in a year.
  • Name the boundary. If the answer is true of your app but not of your hosting, say so.
  • Open every legal URL in a private window. Not logged in, not cached, on a phone.
  • Confirm deletion is reachable without the app installed.
  • If you ship a second language, check its legal pages have the same gating as the first.

None of this is difficult. All of it is skipped, because the form is the last thing between you and a release, and every answer feels like something you already know.


Awesome Apps is an app developer in Sydney building and publishing iOS and Android apps for Australian businesses. How we work, including store submission, is set out here. This is general information about store policy, not legal advice.

Ready to build your app?

Talk to a Sydney app developer — free.

30 minutes. We'll tell you what your app needs, how long it takes, and what it costs. Real answers, no sales pitch.

Book Free App Strategy Call →

Free · 30 minutes · No obligation