Guide · Published · Updated

How to fix an App Store Guideline 1.2 user-generated content rejection

The four safeguards Apple asks for, what reviewers have asked for in recent rejections, code for the filtering part, and a reply you can adapt for App Review.

Apple rejects an app under Guideline 1.2 when people can post content in it and any of four safeguards is missing. The four are a way to filter objectionable material before it is posted, a way to report offensive content with timely responses, a way to block abusive users, and published contact information. Build all four, then tell App Review (the Apple team that reviews each app) where each one is. A moderation API can do the filtering; the other three are features you build.

What Guideline 1.2 requires

Guideline 1.2 covers "apps with user-generated content or social networking services". User-generated content (UGC) is anything one user posts that other users can see: posts, comments, reviews, chat messages, usernames, bios and photos. Apple's text, as read on 16 Oct 2026:

To prevent abuse, apps with user-generated content or social networking services must include:

  • A method for filtering objectionable material from being posted to the app
  • A mechanism to report offensive content and timely responses to concerns
  • The ability to block abusive users from the service
  • Published contact information so users can easily reach you

The guideline adds: "It is your responsibility to remove content that violates this guideline, your terms of service, or your community standards." Two related rules reach further:

  • Creator content (1.2.1). Content that a community of "creators" makes in your app, such as video, articles, audio or casual games, is treated as user-generated content. Creator apps must also let users identify content that exceeds the app's age rating, and use "an age restriction mechanism based on verified or declared age" (1.2.1(a)).
  • Mini apps, chatbots and plug-ins (4.7.1). Software your app offers that is not embedded in its binary (Apple lists HTML5 and JavaScript mini apps and mini games, streaming games, chatbots and plug-ins) must "include a method for filtering objectionable material, a mechanism to report content and timely responses to concerns, and the ability to block abusive users".
Guideline 1.2's four safeguards, and who builds eachAs read on 16 October 2026
SafeguardApple's wordsWhat to buildCan a moderation API do it?
Filtering"A method for filtering objectionable material from being posted to the app"A check on your server of every post, comment, profile and image before other users see it. Flagged content is rejected or held for a personYes. The API says which flags the text or image raises; your server rejects or holds it
Reporting"A mechanism to report offensive content and timely responses to concerns"A Report option on every post, comment, message and profile, a review queue, and a person who works through it every dayIn part. A moderator can open the post's check in the dashboard's Logs and take one of your actions; the moderation.action webhook tells your server. The Report button, the queue of reports and the people are yours
Blocking"The ability to block abusive users from the service"A Block option that hides the blocked user's posts and messages from the person who blocked themNo
Contact"Published contact information so users can easily reach you"A support address inside the app and on the page your Support URL points toNo

What reviewers asked for in recent rejections

Recent rejections are headed "Guideline 1.2 - Safety - User-Generated Content" and list what the reviewer found missing. Developers have posted these messages on Apple's Developer Forums. The lines below are App Review's words as those developers quoted them, not the published guideline, and they ask for more than its four points. The 2017 message and the first November 2025 message start with an EULA (end-user licence agreement: the terms each user accepts) that rules out objectionable content and abusive users.

Guideline 1.2 rejection messages posted by developers on Apple's forumsAs read on 16 October 2026
Forum threadPostedWhat the message asked for
78288May 2017Terms (EULA) that "make it clear that there is no tolerance for objectionable content or abusive users"; filtering; a way for users to flag content; blocking; acting on reports "within 24 hours by removing the content and ejecting the user who provided the offending content"
688227August 2021Filtering, flagging, and the same 24-hour line
805586October 2025Only "A mechanism for users to block abusive users"
807358November 2025The same five points as in 2017. After a resubmission, the developer listed three new points: "Age rating must reflect 18+", "A mechanism for users to immediately remove posts from the feed", and contact information "in the app itself"

Two things follow:

  • The 24-hour line is not in Guideline 1.2, which asks for "timely responses to concerns". Reviewers have used it since at least 2017, so plan to act on every report within a day.
  • A rejection names only what the reviewer found missing. The October 2025 message asked for blocking alone, and the second rejection in thread 807358 added three points the first did not list. Fix every listed point, then check that the other safeguards work too.

How the pieces fit together

The flow below uses the Detectivision API for the checks. The call and event names are the ones in its docs.

StepWhat happensWho does itCall or event
1. PostA user posts. Your server checks the text and each image before anyone else can see themYour server and DetectivisionPOST /api/v1/moderation with "mode": "detect"
2. DecideYour server publishes the post, holds it for a moderator, or rejects it, by the flags raisedYour serverflagged, flags and status in the answer
3. ReportA user reports a post, a message or a profileYour appyour own report feature
4. ReviewA moderator looks at held and reported items and picks one of your actionsYour moderators, in the dashboard's Logs or your admin toolPOST /api/v1/moderation/{id}/actions
5. ActYour server removes the content and closes the account that posted itYour serverthe moderation.action webhook
6. BlockA user blocks another, and the blocked user's content disappears for themYour app and servernone

The fix, step by step

1. Filter text and images before they are posted

Run the check on your server, between "the user pressed Post" and "other users can see it". This is pre-moderation; types of content moderation compares it with the other approaches. Keep the API key on the server: a key inside an app can be read by anyone, who can then spend your credits (quickstart).

  • Text. Send it as the user wrote it: posts, comments, chat messages, usernames and bios.
  • Images. Upload to private storage first, send the API a signed URL (it works while it is valid), and make the image public only after the check (image moderation).
  • Mode. Use "mode": "detect". It returns the flags raised and their confidence, and it never costs more than the other modes. Today's flags judge the whole text or image: the answer says that something was found, not where. So the filter rejects or holds the whole post; it cannot hide one word or one part of a picture.

On 16 Oct 2026 the public status endpoint listed these text flags: bad_habits, hate, insult, profanity, racism, self_harm, sexism and sexual; and these image flags: hate, scam, sexual and violence. Your own account's list comes from GET /api/v1/flags (flags). Toxicity detection and NSFW detection show how the text flags and the sexual flag are used.

Example response (format from the API reference; values illustrative):

Request
curl https://api.detectivision.ai/api/v1/moderation \
  -H "Api-Key: $DETECTIVISION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "text_moderation",
    "input": "Nobody wants you here. Delete your account, idiot.",
    "mode": "detect",
    "metadata": { "postId": "p_20931", "userId": "u_4417" }
  }'
Response
{
  "id": 58213,
  "type": "text_moderation",
  "mode": "detect",
  "status": "completed",
  "flagged": true,
  "flags": [{ "flag": "insult", "confidence": 0.94 }],
  "checked": [
    "bad_habits",
    "hate",
    "insult",
    "profanity",
    "racism",
    "self_harm",
    "sexism",
    "sexual"
  ],
  "unchecked": [],
  "tookMs": 640,
  "usage": { "charged": 2, "remaining": 8731 },
  "metadata": { "postId": "p_20931", "userId": "u_4417" }
}

flagged is the verdict. checked lists every flag that was looked for; status is completed when all of them were. metadata comes back unchanged, so the result names the post it belongs to. tookMs and the other numbers are illustrative, not measurements.

The same check as a pre-post filter for a whole post, with its text and photos. Each request matches the API reference:

Node.js
// On your server: the app sends the new post here, and the API key stays here.
const API = "https://api.detectivision.ai/api/v1/moderation";

// Your rules. This is an example: decide your own, flag by flag.
const REJECT = new Set(["hate", "racism", "sexism", "sexual", "violence"]);
const HOLD = new Set(["insult", "profanity", "bad_habits", "self_harm", "scam"]);

async function check(type, input, metadata) {
  try {
    const res = await fetch(API, {
      method: "POST",
      headers: { "Api-Key": process.env.DETECTIVISION_API_KEY, "Content-Type": "application/json" },
      body: JSON.stringify({ type, input, mode: "detect", metadata }),
      signal: AbortSignal.timeout(30_000),
    });
    return res.ok ? await res.json() : null; // errors are never charged
  } catch {
    return null; // network error or timeout
  }
}

// Returns "publish", "hold" (a moderator decides) or "reject".
export async function screenPost(post) {
  const metadata = { postId: post.id, userId: post.userId };
  const checks = post.imageUrls.map((url) => check("image_moderation", url, metadata));
  if (post.text.trim()) checks.push(check("text_moderation", post.text, metadata));
  let verdict = "publish";
  for (const result of await Promise.all(checks)) {
    if (!result || result.status !== "completed") {
      verdict = "hold"; // not fully checked: never publish it unseen
      continue;
    }
    for (const { flag } of result.flags) {
      if (REJECT.has(flag)) return "reject";
      if (HOLD.has(flag)) verdict = "hold";
    }
  }
  return verdict;
}
Python
import os
import requests

API = "https://api.detectivision.ai/api/v1/moderation"

# Your rules. This is an example: decide your own, flag by flag.
REJECT = {"hate", "racism", "sexism", "sexual", "violence"}
HOLD = {"insult", "profanity", "bad_habits", "self_harm", "scam"}


def check(type_, input_, metadata):
    try:
        r = requests.post(
            API,
            headers={"Api-Key": os.environ["DETECTIVISION_API_KEY"]},
            json={"type": type_, "input": input_, "mode": "detect", "metadata": metadata},
            timeout=30,
        )
    except requests.RequestException:
        return None  # network error or timeout
    return r.json() if r.ok else None  # errors are never charged


def screen_post(post):
    """Return "publish", "hold" (a moderator decides) or "reject"."""
    metadata = {"postId": post["id"], "userId": post["user_id"]}
    checks = [("image_moderation", url) for url in post["image_urls"]]
    if post["text"].strip():
        checks.append(("text_moderation", post["text"]))
    verdict = "publish"
    for type_, input_ in checks:
        result = check(type_, input_, metadata)
        if result is None or result["status"] != "completed":
            verdict = "hold"  # not fully checked: never publish it unseen
            continue
        raised = {f["flag"] for f in result["flags"]}
        if raised & REJECT:
            return "reject"
        if raised & HOLD:
            verdict = "hold"
    return verdict
  • The two sets are your policy. Which flags reject a post and which hold it is your decision; the code shows one choice. self_harm is held, not rejected, so a person can reach out to the user.
  • Never publish unseen. An error, a timeout or a partial result (a flag that could not be checked right now) holds the post. Errors and partial results cost nothing (errors).
  • Act above your own line. A flag is raised from a confidence of 0.5, and findings below that never come back. What you can choose is what each confidence leads to: for example, hold flags under 0.7 for a person and reject above it.
  • Languages. Text checks are reliable in English, Spanish, Portuguese, French, German, Italian, Turkish, Arabic, Russian and Hindi. Text in other languages is checked, but the results are not reliable (text moderation). If your users write in other languages, reports and human review carry more of the load.
  • Posts that can wait. Save the post as pending, send the check with "async": true, and decide when the result arrives by webhook; keep image URLs valid until then. It costs half of an answer your app waits for (async requests).
  • Video, audio and live streams. These checks are coming soon. Until then, hold uploaded videos for a person.

For the flags that fit each kind of app, see online communities, dating apps and marketplaces.

2. Add a report button and a review queue

Put a Report option on every post, comment, message and profile. Save who reported what and when. Let users hide any post from their own feed at once, and hide what they report: the second rejection in thread 807358 listed "a mechanism for users to immediately remove posts from the feed".

Then a person has to act on reports, quickly. With Detectivision:

  • Logs. A moderator can open a check in the dashboard's Logs and see its flags, your metadata and your actions. Store the check's id with the post, so a report leads straight to it.
  • Your actions. Define your own actions in Logs, such as Remove post and Ban user; an account can have up to 20. A moderator takes one in Logs, or your admin tool calls POST /api/v1/moderation/{id}/actions.
  • The webhook. Each action sends a moderation.action webhook to your endpoint (subscribe it to that event on the dashboard's Webhooks page), and your server carries the action out. Detectivision never removes content or bans anyone itself (metadata and actions).
  • Alerts. Subscribe to moderation.flagged to tell a moderator when a check raised a flag (webhooks).

The receiving end in Node.js, with the Standard Webhooks library the docs point to (npm install standardwebhooks express). remove and ban stand for the action keys you set up in Logs:

import express from "express";
import { Webhook } from "standardwebhooks";

const app = express();
const webhook = new Webhook(process.env.DETECTIVISION_WEBHOOK_SECRET); // whsec_...
const done = new Set(); // in production, keep these ids in your database

app.post("/webhooks/detectivision", express.raw({ type: "application/json" }), (req, res) => {
  let event;
  try {
    event = webhook.verify(req.body, req.headers); // signature and timestamp
  } catch {
    return res.status(400).end();
  }
  res.status(204).end(); // answer at once, then do the work

  const id = req.headers["webhook-id"]; // the same on every retry
  if (done.has(id) || event.type !== "moderation.action") return;
  done.add(id);

  const { action, item } = event.data;
  const { postId, userId } = item.metadata ?? {};
  if (action.key === "remove" && postId) removePost(postId); // your own functions
  if (action.key === "ban" && userId) ejectUser(userId);
});

The handler checks the signature on the raw body before it trusts the message. It answers well within the 10 seconds the docs allow, and it skips retries of a message it has already handled. Taking actions and sending their webhooks costs no credits.

3. Let users block each other

Add Block to every profile and every chat. Once a user blocks someone, hide that person's posts, comments and messages from them at once, and stop new messages between the two. Enforce it on your server, so it holds on every device, and let users undo it in their settings. A moderation API plays no part here: blocking is your feature.

4. Publish contact details and make users accept your terms

  • Contact. Put a support email address or a contact form inside the app, for example under Settings, and on the page your Support URL points to. Guideline 1.5 asks that "your app and its Support URL include an easy way to contact you", and the second rejection in thread 807358 listed contact information "in the app itself".
  • Terms. Show your terms before a user can post, and require acceptance. Reviewers' messages ask that the terms "make it clear that there is no tolerance for objectionable content or abusive users". Say what happens to people who break them: their content is removed and their account is closed.
  • Age rating. Set an age rating that matches what users can post. The second rejection in thread 807358 listed "Age rating must reflect 18+". Creator apps also need the age restriction that 1.2.1(a) describes.

What to write to App Review

Reply in App Store Connect. Apple's guidelines say that if your app was rejected and you "would like to provide additional information", you should "use App Store Connect to communicate directly with the App Review team". Describe each safeguard and where the reviewer can find it. Give the reviewer a demo account too. Guideline 2.1 asks you to "include demo account info (and turn on your back-end service!) if your app includes a login", and the rejection quoted in thread 807358 said that a demo video "is not sufficient".

Write only what the build does today. Fill in the brackets with what is true for your app:

Hello,

Thank you for the review. Version [1.4.0] (build [58]) adds the safeguards
that Guideline 1.2 asks for. Each one can be tried with the demo account
below.

1. Filtering: every [post, comment, profile and photo] is checked on our
   server before other users can see it. Flagged content is [rejected /
   held until a moderator reviews it]. To try it: [steps].
2. Reporting: every [post, comment, message and profile] has a Report
   option ([where to find it]). Reports go to our moderation queue, which
   [who] reviews [how often]. [What happens next, for example: content
   that breaks our terms is removed, and the account that posted it is
   closed.]
3. Blocking: every [profile and chat] has a Block option ([where to find
   it]). The blocked user's posts and messages disappear at once for the
   user who blocked them.
4. Contact: [Settings > Contact us] in the app, and [support URL].
5. Terms: new users must accept our Terms of Use before they can post.
   The terms say there is no tolerance for objectionable content or
   abusive users.

Demo account: [user name] / [password]

[Your name]

If you disagree with the outcome, Apple's guidelines point to an appeal.

Google Play asks for the same, and a little more

Google Play's User Generated Content policy covers apps that contain or feature UGC, including specialized browsers or clients that direct users to a UGC platform. It defines UGC as "content that users contribute to an app, and which is visible to or accessible by at least a subset of the app's users". Google asks those apps for moderation that is effective and ongoing, and spells out what that means:

Google Play's User Generated Content policy next to Apple's Guideline 1.2As read on 16 October 2026
Google Play asks forIn Apple's 1.2?What to build
Users accept the app's terms of use or user policy "before users can create or upload UGC"Not in the guideline text; App Review's messages ask for an EULAA terms screen before the first post (step 4)
Terms that define objectionable content and behaviours, and prohibit themNot in the guideline text; the EULA reviewers ask for covers itA clear list in your terms
UGC moderation "as is reasonable and consistent with the type of UGC hosted by the app", with in-app reporting and blocking, and action against content or usersYes: filtering, reporting and blockingSteps 1 to 3
In-app reporting of content and users, even in apps for a closed group such as one school or companyYes: reportingStep 2
In-app blocking for one-to-one features such as direct messages, tagging and mentionsYes: blockingStep 3
Reporting of users and content, and blocking of users, in apps with publicly accessible UGC such as social and blogger appsYesSteps 2 and 3
Safeguards so that in-app monetization does not encourage objectionable behaviourNoNo paid features that reward objectionable posts
If incidental sexual content can appear: hidden by default behind filters that take at least two user actions to turn off, children kept out with an age screen, and accurate answers to the content rating questionnaireApple has its own rule for UGC from a web-based service: mature content hidden by default and turned on only via your websiteA setting, an age screen, and honest rating answers
Moderation of augmented reality (AR) content, and of where it is anchoredNoOnly if your app has AR

Apps "whose primary purpose is featuring objectionable UGC" are removed from Google Play, and so are apps that end up used mainly for it. The filtering in step 1 serves both stores; the other rows are features you build once.

Kids Category apps: ask App Review first

The Kids Category is the App Store category for apps designed for children. Guideline 1.3 says: "Kids Category apps may not send personally identifiable information or device information to third parties." Guideline 5.1.4 counts "photos, videos, drawings, the ability to chat" as personal information: an app that collects, transmits or can share it from a minor must have a privacy policy and follow children's privacy laws (both as read on 16 Oct 2026).

A moderation API is a third party: checking a child's message or photo means sending it to an outside service. If your app is in the Kids Category, ask App Review before you send children's content to any outside service, ours included. We cannot tell you how App Review will answer.

What this does not do

What a moderation API, ours included, will not do for you:

  • Get your app through review. App Review decides. The API covers one safeguard out of four.
  • Build the rest. Report and block buttons, contact details and terms are your features.
  • Review reports. Detectivision gives you Logs, your own actions and webhooks, not moderators. A person on your side works the queue.
  • Act on your platform. It never removes a post or closes an account. Your server does, when the moderation.action webhook arrives.
  • Mark or mask part of a post. Today's flags judge the whole text or image. There are no word positions, no boxes, and no masked or blurred copies.
  • Check every language equally. Text checks are reliable in English, Spanish, Portuguese, French, German, Italian, Turkish, Arabic, Russian and Hindi only.
  • Check video, audio or live streams. Those checks are coming soon.
  • Report what scores below 0.5. Findings under a flag's threshold never come back.
  • Verify ages or match known abuse images. Detectivision does no age verification and no hash matching.
  • Make any kind of app acceptable. Apple says apps used primarily for "pornographic content, Chatroulette-style experiences, random or anonymous chat, objectification of real people (e.g. 'hot-or-not' voting), making physical threats, or bullying" do not belong on the App Store. A filter does not change that.

Plans and the free trial are on the pricing page.

Start the free trial

Or read the quickstart first: an API key and your first check, in a few steps.

Questions

Frequently asked

Will a moderation API get my app through App Review?

No tool can promise that; App Review decides. A moderation API covers one of the four safeguards, the filtering. The report and block features, the contact details and the terms reviewers ask for are yours to build, and your reply to App Review should say where each one is.

Does Apple require AI moderation?

No. Guideline 1.2 asks for "a method for filtering objectionable material from being posted to the app" and does not say how. A word list, a person who checks each post before it goes live and an API are all ways to filter. Choose the one that keeps up with your volume and your users' languages.

Do I need to check images too?

If users can post images, yes. The guideline speaks of objectionable material, not only text, and a filter that reads captions misses what is in the picture. The image flags are hate, scam, sexual and violence (status, 16 Oct 2026). Video checks are coming soon.

Do I have to act on reports within 24 hours?

Guideline 1.2 asks for "timely responses to concerns" and gives no number. The 24 hours comes from App Review rejection messages that developers posted on Apple's forums in 2017, 2021 and 2025, which ask the developer to remove the content and eject the user within 24 hours. Plan for a person to work the queue every day.

How many credits does pre-post filtering use?

Text is counted in units of 2,500 characters, where a started unit counts, and each image is one unit. The price table on the Credits page of the docs gives the credits per unit for each mode; detect never costs more than the others. That table is the price of a result sent later by webhook; an answer your app waits for costs twice as much. Illustrative: 300 posts a day, each with a short text and one photo, make 300 text units and 300 image units a day.

Can a post go live first and be checked afterwards?

Apple's words are "filtering objectionable material from being posted", so check before other users can see a post. If a post can wait, save it as pending, send the check with "async": true and decide when the moderation.completed webhook arrives. That costs half as much as an answer your app waits for.

Related articles
  • Moderate forum posts, comments, usernames and avatars for insults, hate, sexual content and self-harm, in ten languages, with one API.

  • Check profile photos, bios and chat messages on your dating app for nudity, harassment, hate and scams, in ten languages, with one API.

  • Check listing photos, titles, descriptions, reviews and buyer messages for nudity, hate, violence and scam images, with one API.

  • Check comments and chat messages for insults, hate, harassment and self-harm in ten languages, with a flag for each kind of toxicity.

  • Detect nudity and sexual content in images, and sexual language in text written in ten languages, with one API call and a confidence for each finding.

Sources
  1. App Review Guidelines: 1.2 User-Generated Content, 1.2.1 Creator Content, 1.3 Kids Category, 1.5 Developer Information, 2.1 App Completeness, 4.7.1, 5.1.4 Kids (page last updated 8 June 2026), Apple, read on 16 October 2026
  2. Guideline 1.2 - Safety - User-Generated Content (Apple Developer Forums thread 807358, rejection text posted by a developer, November 2025), Apple Developer Forums, read on 16 October 2026
  3. Unreasonable Rejection (Apple Developer Forums thread 805586, rejection text posted by a developer, October 2025), Apple Developer Forums, read on 16 October 2026
  4. Guideline 1.2 - Safety - User Generated Content (Apple Developer Forums thread 688227, rejection text posted by a developer, August 2021), Apple Developer Forums, read on 16 October 2026
  5. iOS App rejected: 1.2 Safety: User Generated Content (Apple Developer Forums thread 78288, rejection text posted by a developer, May 2017), Apple Developer Forums, read on 16 October 2026
  6. User Generated Content (Play Console Help, Google Play Developer Program Policy), Google, read on 16 October 2026
  7. Detectivision API status (GET /api/v1/status), Detectivision, read on 16 October 2026
  8. Metadata and actions, Detectivision docs, read on 16 October 2026
  9. Webhooks, Detectivision docs, read on 16 October 2026
  10. Credits and billing, Detectivision docs, read on 16 October 2026
  11. Flags, Detectivision docs, read on 16 October 2026
  12. Text moderation, Detectivision docs, read on 16 October 2026
  13. Image moderation, Detectivision docs, read on 16 October 2026