To get a ChatGPT app through review, fix the four things OpenAI names as common rejection reasons before you submit: reviewers can’t log in, a test case gives the wrong result, a tool returns user data your privacy policy doesn’t mention, or a tool’s annotations don’t match what it does. Then check the hard requirements that block submission outright: a verified identity, a domain challenge file, exactly five positive and three negative test cases, a demo video, and explicit readOnlyHint, destructiveHint, and openWorldHint values on every tool.
Who this is for: a team with a working MCP server that wants a public listing in the plugin directory shared by ChatGPT and Codex. ChatGPT apps are now published as plugins. Who should skip this: if only your own staff will use the server, you don’t need review. OpenAI says to use a custom MCP server connection for “private or workspace-only use” (review requirements). If you haven’t built the server yet, start with how to build a ChatGPT app.
All requirements below are quoted from OpenAI’s plugin docs, read on 2026-10-14: the submission guide, the review requirements, the plugin guidelines, and the submission errors reference. The forum posts we read about rejections were written before the ZIP-based flow, so check dates on any advice you find.
Run the preflight first
We wrote a small script that checks the parts of review you can test from outside: the endpoint, the handshake, the three annotations on every tool, the UI resource MIME type and CSP, inputs that ask for chat history or precise location, output fields that look like internal IDs, and the domain challenge file. It only calls initialize, tools/list, and resources/read. It never calls your tools. No dependencies, Node 18 or later.
curl -O https://dawloom.com/blog/chatgpt-app-submission-checklist/preflight.mjs
node preflight.mjs https://your-server.example.com/mcp
Download preflight.mjs. Here’s the real output when we ran it on 2026-10-14 against our own public server:
$ node preflight.mjs https://dawloom.com/mcp
PASS Public HTTPS endpoint.
PASS initialize OK: dawloom-tools 2.0.0, protocol 2025-06-18.
PASS convert_document: all three annotations set.
PASS list_supported_formats: all three annotations set.
PASS No UI resources, so no CSP or screenshots needed.
WARN /.well-known/openai-apps-challenge not found. Host the token the portal gives you before you submit.
2 tools, 0 fail, 1 warn.
The warning is expected: we haven’t started a submission, so there is no token to host yet. And this is the same script against a copy of our demo order server with the annotations and CSP deleted, run locally:
$ node preflight.mjs http://localhost:8790/mcp
FAIL Endpoint is http: Review needs a public HTTPS URL.
PASS initialize OK: order-status-demo 1.0.0, protocol 2025-06-18.
FAIL get_order_status: missing explicit readOnlyHint, destructiveHint, openWorldHint (error code annotations_required).
FAIL show_order_card: missing explicit readOnlyHint, destructiveHint, openWorldHint (error code annotations_required).
FAIL ui://order-card/v1.html: no CSP declared. Review requires the exact domains the UI fetches from.
WARN /.well-known/openai-apps-challenge not found. Host the token the portal gives you before you submit.
2 tools, 4 fail, 1 warn.
The script can’t judge whether an annotation is correct, whether your test cases pass, or whether your privacy policy covers your fields. Those need a person, and they’re in the checklist below. For transport, CORS, OAuth discovery, and speed, run the server through our MCP server checker too.
The four rejection reasons OpenAI lists
OpenAI’s review page has a FAQ titled “What are common rejection reasons and how can I resolve them?” (review requirements). Here are the four, with the fix each one needs.
| Rejection | What OpenAI says | Fix before you submit |
|---|---|---|
| Reviewers can’t connect | ”our review team must be able to log into a demo account with no further configuration required” | A dedicated test account with sample data. No MFA, SMS or email codes, magic links, or private network. Test the login from outside your office network |
| A test case fails | ”Ensure that all test cases pass on the supported ChatGPT and Codex surfaces” | Rerun all eight cases on web and mobile with the reviewer account the day you submit. Check the UI loads its images and content |
| Undisclosed user data | Remove “unnecessary PII, telemetry/internal identifiers (for example, session, trace, or request IDs; timestamps; internal account IDs; or logs)“ | List every field each tool returns, nested ones too. Delete what the answer doesn’t need, and disclose the rest in your privacy policy |
| Annotations don’t match | readOnlyHint is false if a tool can “send emails/messages, run jobs, enqueue tasks, write logs, start workflows” | Read each handler as well as its name. A “read” tool that writes an audit row the user can see is a write |
The annotation rules are stricter than most people expect. From the plugin guidelines: destructiveHint is true for deletion, overwriting, cancellation, or irreversible sends, and “Being able to undo an action does not, by itself, justify setting destructiveHint to false.” openWorldHint is true for “read-only web search and arbitrary destinations.” That’s why our own convert_document tool sets openWorldHint: true: it fetches any URL you give it.
The full checklist
Thirty checks, in the order you’ll meet them. Items marked blocks stop the submit button. The rest are review risks.
Account and identity
- Blocks. You’re an organization owner, or have Apps Management Write (submission guide).
- Blocks. Individual or business verification is complete for the name you’ll publish under. “Publishing under an unverified individual or business name will result in rejection” (review requirements).
- Blocks. The project uses global data residency. “For now, projects with EU data residency cannot submit plugins with MCP servers for review” (same page).
The package
- Blocks. The MCP server is in the first ZIP. “Adding an MCP server to an existing skills-only plugin is not currently supported” (submission guide).
- Blocks. No lifecycle hooks and no
apps/.app.jsonreferences in the ZIP. Both “cannot currently be submitted” (same page). - Blocks. Display name and short description are 30 characters or fewer each. Long description is 4,000 or fewer (submission errors).
- Blocks. Category is one of: Productivity, Creativity, Developer Tools, Business & Operations, Data & Analytics, Communication, Education & Research, Security, Finance, Healthcare, Travel, Entertainment, or Other (same page).
- At most three starter prompts, each 128 characters or fewer, with no
@mentions(same page). - The name doesn’t end in “MCP”, “MCP Server”, or “Plugin”, and isn’t a generic dictionary word (plugin guidelines).
- Descriptions don’t mention pricing, trials, discounts, or compare you with other products (same page).
- Blocks. Website, support, privacy policy, and terms URLs are HTTPS and live (submission errors).
- No secrets in the ZIP. The manifest rejects
clientSecret,test_credentials, andreviewer_instructions(submission guide).
The server
- Blocks. A stable public HTTPS endpoint. Not a temporary tunnel, not localhost.
- Blocks. The domain challenge token is served as plain text at
https://<host>/.well-known/openai-apps-challenge. Serve only the exact token, with no JSON and no list of tokens (domain verification details). - Blocks. Every tool sets all three annotations as explicit booleans (
annotations_required). - Every annotation matches the handler’s real behavior. See the table above.
- One tool per action. No generic “execute” tool that runs operations “not individually exposed for review” (plugin guidelines).
- Inputs ask for the minimum. No “full conversation history, raw chat transcripts”, and no precise location such as GPS coordinates (same page).
- Outputs carry no session, trace, or request IDs, timestamps, or internal account IDs unless the answer needs them.
- Auth is none, OAuth, or mixed. “The portal doesn’t provide an API-key connection flow”, and “public submission rejects nonempty server headers” (submission guide). Our OAuth 2.1 guide covers the server side.
- Errors return a clear message. “Errors, including unexpected ones, must be handled with clear messaging or fallback behaviors” (plugin guidelines).
UI, if you have it
- Blocks if you have UI. Each UI resource declares a CSP with the exact domains it calls and loads from (review requirements).
- Each framed domain has a written justification (
frame_domain_explanation_required). - Screenshots only if the scan finds a UI template: one PNG or JPEG per starter prompt, exactly 706 pixels wide and 400 to 860 tall (submission errors). The guidelines add that “Screenshots are no longer shown in the Directory”, so treat them as review material.
- The UI works on ChatGPT desktop and mobile. “Plugins must function reliably in ChatGPT on both desktop and mobile” (plugin guidelines).
Review materials
- Blocks. Exactly five positive and three negative test cases. Positive cases need
tools_triggeredandexpected_behavior(submission guide). Our filled-in template shows the shape. - Blocks. A video walkthrough URL that “shows the main use cases and tools across supported platforms” (submission errors).
- Blocks. Release notes for the version.
- Reviewer credentials, entered in the dashboard, never in the ZIP. The account “should work immediately without MFA approval”.
Policy
- No digital goods for sale. “Currently, plugins may conduct commerce only for physical goods”, and plugins “must not display subscription plans, initiate new subscriptions, or promote upgrades” (plugin guidelines). Also: “Trial or demo plugins will not be accepted.”
One conflict to know about. The submission errors page lists justification_required: “Every MCP tool annotation must include a justification.” The guidelines page says “Annotation justifications are no longer required.” Both were live on 2026-10-14. Write a one-line justification per annotation anyway. It costs five minutes.
How long review takes
OpenAI publishes no timeline. Its FAQ: “Review timelines may vary as we continue to build and scale our processes. Please do not contact support to request expedited review” (review requirements).
The only numbers we found are crowdsourced. reviewtimes.fyi, read on 2026-10-14, showed 27 tracked submissions, 11 decided (5 approved, 6 rejected), a median of 8 days to approval and 3 days to rejection, and 13 still waiting, the oldest at 33 days. The site doesn’t say how it collects data, and 11 decisions is a tiny sample. Treat it as a rough signal.
Two first-hand reports on OpenAI’s forum fit that picture. In August 2026 a developer said each review took “~20days” and that their update was rejected twice for requirements that changed during the wait (OpenAI forum). In April 2026 another got rejections with an empty reason after fixing a destructiveHint problem. OpenAI support’s advice: “Best move here is to reply directly to the rejection email” (OpenAI forum).
Plan for two rounds. Only one review can be active per plugin, and each resubmission starts a new review.
If you’re rejected
- Read the email. It names the checks that failed.
- Fix, rescan, and resubmit a corrected ZIP. To change a draft that’s in review, select Cancel Review first.
- To appeal, reply to the rejection email “with a clear rationale and any new information” (review requirements). Include the plugin ID.
- For an automated tool finding, the portal has an Appeal button, but OpenAI notes “correcting it on the server and rescanning is usually faster” (submission guide).
After you’re approved
Approval doesn’t publish anything. You choose when to select Publish. After that:
- “OpenAI scans your hosted MCP server daily” (submission guide). New tools stay hidden until they pass automated checks. A changed tool keeps its old definition live until the new one passes.
- Keep old input schemas and UI resource URIs working while an update is held.
- Changing the server’s origin (scheme, host, or port) means a new plugin and a new review (review requirements). Pick your production domain before you submit. Our post on where to host an MCP server helps.
- Your listing only appears on the directory’s main pages if OpenAI picks it. Otherwise people find it by exact name or direct link.
If you want ChatGPT connected to your own product, see our AI integration service.
FAQ
Why was my ChatGPT app rejected?
Usually for one of four reasons OpenAI lists: reviewers couldn’t log in, a test case failed, a tool returned user data your privacy policy doesn’t cover, or an annotation didn’t match the tool. The rejection email names the failed checks.
How long does ChatGPT app review take?
OpenAI doesn’t publish a timeline. Crowdsourced data from reviewtimes.fyi showed a median of 8 days to approval and 3 to rejection on 11 decisions, read on 2026-10-14. Forum reports range up to about 20 days per round.
Do I need a UI to pass review?
No. Without UI you skip the CSP check and screenshots. You still need test cases, a video, and annotations.
Can I put my API key in the plugin so reviewers can log in?
No. The portal has no API-key flow, the manifest rejects secrets, and public submission rejects nonempty server headers. Use OAuth with a reviewer test account, or no auth for public data.
Is it worth checking my server for safety first?
Yes. Reviewers look at what your tools take in and return. Our 12 checks for third-party MCP servers is written for buyers, but the same checks catch problems reviewers flag.
We run one public MCP server, https://dawloom.com/mcp, and it’s the one in the preflight output above. If you want help getting your own server ready for review, see MCP servers and our MCP server checker post for what we test.