1. Why you actually need a Privacy Policy, Terms, and Support
Let’s start with the unpleasant truth: for the ChatGPT Store, having a public privacy policy and support contact information is not “nice to have,” it’s a strict requirement. The OpenAI guidelines explicitly state that every App must have a published Privacy Policy that clearly explains what data is collected and how it’s used, as well as a support contact.
But it’s not just about “making the moderator go away.” These documents solve several problems at once.
First, it’s a baseline level of trust for the user. They can see there are real people or a company behind the app, there are ground rules, and there’s a place to go if something goes wrong. In a world where “yet another AI service” appears every day, this is already a competitive advantage.
Second, it’s a formalization of what you’ve already implemented in your architecture. Everything you addressed in the modules on security, logging, retention, data deletion, and payments should also be reflected in words. If you promise “we don’t store conversations,” but log all tool input forever, that’s not just bad form — it’s grounds for serious complaints.
Third, it’s another layer of the contract between you and OpenAI. The Store is effectively saying: “We’re ready to show your App to millions of people, but you must honestly describe what you do with them and be reachable when something goes wrong.”
Bottom line: legal pages are not about “the lawyers made us do it.” They are a way to align expectations: what the App does, what data it touches, what responsibility you’re willing to bear, and how the user can contact you.
2. Where these legal URLs live in a ChatGPT App
Technically, for a ChatGPT App, legal pages are ordinary public URLs that you specify in the app metadata and the Store listing. In the guidelines, they’re usually called something like privacy_policy_url, terms_of_service_url, and a support contact.
These URLs must meet a few simple but important conditions:
- They live on a stable domain for your product or company. No temporary ngrok links; otherwise, in a week the Store will send users to nowhere.
- They’re accessible without authentication. A user (and reviewer) must be able to open them in a browser without logging in or jumping through hoops.
- They’re up to date and match reality. If you change your data processing architecture, you’ll need to update the text over time.
In our training project GiftGenius, we already have a Next.js frontend deployed, say, on Vercel. So the logical place for legal pages is routes like:
- /legal/privacy
- /legal/terms
- /support
These are essentially just three more pages in the app, but they’re the ones you’ll link to in the App submission form for review.
3. Privacy Policy: how to honestly describe what you do with data
The role of a Privacy Policy
The Privacy Policy (hereafter, the Policy) answers the key question: “What does this App do with my (the user’s) data?” It should describe what data categories you process, where they come from, why you need them, where and how long they’re stored, to whom they’re disclosed, and how a user can request deletion.
One key aspect of ChatGPT Apps is that users need to understand exactly what you receive from the chat. OpenAI specifically emphasizes: your App should not attempt to reconstruct the entire dialogue, but work only with the fragments that the model or the user explicitly sent to tools. You should note this in the Policy as well.
Start with architecture, not text
Before writing a single legal word, it helps to look at your App through SRE/architect eyes: what data actually passes through the system.
For our example — GiftGenius — it might look roughly like this:
| Data category | Source | Where stored | Retention/behavior |
|---|---|---|---|
| Request text (snippet from chat) | Tool call from ChatGPT | Backend request logs | N days or deleted immediately |
| User-selected gifts | Widget actions | GiftGenius database | Stored until account deletion |
| User email (if OAuth is used) | Authentication provider | User database | While the account is active |
| Technical metrics (IP, timestamp, errors) | HTTP requests | Logs / monitoring system | N days per logging policy |
You can add such a table directly to your project documentation (for example, in /docs): it will be useful for development, the Security module, and the Policy itself.
Then translate this structure into plain language.
Privacy Policy structure for GiftGenius
In a training project, there’s no need to write a 20‑page epic; a compact but honest structure is enough. Typically, you include several sections:
- Introduction: who you are and what the App is.
- What data you collect.
- How you use it.
- To whom you disclose it.
- Where and how long you store it.
- User rights (including deletion requests).
- Privacy contact information.
It’s important to understand that even for a training project this isn’t just boilerplate. You already considered log retention, deletion policy, backups in the security module — now it needs careful wording.
Simplest page implementation in Next.js
Let’s create the /legal/privacy page in our app. In the App Router it’s literally a single file:
// app/legal/privacy/page.tsx
export default function PrivacyPage() {
return (
<main className="mx-auto max-w-3xl p-8 prose">
<h1>Privacy Policy – GiftGenius</h1>
<p>Last updated: {new Date().toLocaleDateString()}</p>
{/* the policy sections go below */}
</main>
);
}
This example is intentionally simple: the goal is to pin down a static URL. In a real project, the policy text is almost always stored separately (for example, in an .md file) and loaded so you don’t smear tons of text across JSX.
For example, you can create a simple loader:
// app/legal/privacy/page.tsx
import policyHtml from "./policy.html"; // prebuilt HTML
export default function PrivacyPage() {
return (
<main
className="mx-auto max-w-3xl p-8 prose"
dangerouslySetInnerHTML={{ __html: policyHtml }}
/>
);
}
Notes in the “don’t do this without understanding” style are appropriate here: dangerouslySetInnerHTML is safe only if you control the HTML source (for example, you build it yourself from markdown in CI).
Tie it to real processes
The most important thing: you must not write in the Policy what you don’t have in code. If you state that:
- you don’t store request text for more than 7 days;
- you fully delete a user’s profile on request;
- you don’t use this data to train your models,
then you must have:
- retention settings in your logs;
- an endpoint or admin process for deleting a user;
- no code that dumps logs to a third‑party storage “for Data Science.”
Conversely, if you’ve enabled usage metrics, A/B experiments, or country‑level analytics, you should honestly say so in the Policy. And give users at least basic rights: to learn what’s stored and to request deletion.
With data and the Privacy Policy covered, the remaining step is to lock down not only data processing but also the “rules of the game” — that’s the job of the Terms.
4. Terms of Use / Service: rules of the game and AI disclaimers
Why you need Terms if you already have a Policy
The Privacy Policy answers the question “what do you do with data.” Terms of Use/Service (hereafter — Terms) answer the question “under what conditions can one use the App at all.” This is a legal agreement between you and the user.
It describes:
- what GiftGenius is and what functionality it provides;
- what user actions are allowed and what are not;
- where the “magic boundaries” of AI lie (AI disclaimers);
- your limitations of liability;
- how disputes are resolved and which jurisdiction applies.
For an AI app, two things are especially important: an accuracy disclaimer and limitation of liability.
AI specifics: “the model can be wrong”
Our GiftGenius provides gift recommendations. That’s cute and relatively safe, but even here you can step on a rake: a user asked for “a gift for someone with a nut allergy,” the model generated the wrong thing, the person was harmed, everyone is unhappy.
It’s clear that Terms are not a bulletproof shield against all risks, but they clearly establish:
- that results are generated by AI and may be inaccurate, outdated, or just odd;
- that the user must independently verify important information, especially related to health, finance, and other sensitive areas;
- that you make no guarantees of “perfect” recommendations and are not responsible for misuse of the output.
You’ll want a lawyer to refine the wording later, but a technical developer should understand the idea.
Commerce nuances
If your App performs any payment actions at all (you’ll dive deeper into the ACP and commerce module later, but it’s in the course), you need to describe in the Terms carefully:
- through whom payments are processed (Stripe, ACP, another system);
- what payment data you actually see;
- your refund and cancellation policy;
- what exactly counts as a successful transaction.
A general platform recommendation is to state explicitly that card data is processed by the payment provider, not your server, and you store only the minimum (for example, a transaction ID).
Implementing /legal/terms in Next.js
Technically, it’s very similar to the Privacy Policy. Create a page:
// app/legal/terms/page.tsx
export default function TermsPage() {
return (
<main className="mx-auto max-w-3xl p-8 prose">
<h1>Terms of Use – GiftGenius</h1>
<p>Last updated: {new Date().toLocaleDateString()}</p>
{/* sections: service description, restrictions, AI disclaimer, liability */}
</main>
);
}
And, as with the Policy, it’s better to keep the text in a separate file or CMS and have minimal markup in code.
You can extract a common wrapper for legal pages:
// app/legal/LegalLayout.tsx
export function LegalLayout(props: { title: string; children: React.ReactNode }) {
return (
<main className="mx-auto max-w-3xl p-8 prose">
<h1>{props.title}</h1>
<p>Last updated: {new Date().toLocaleDateString()}</p>
{props.children}
</main>
);
}
Then use it for both the Policy and the Terms. This isn’t about “beautiful code” but about ensuring you don’t forget to set an update date and maintain a consistent style.
5. Support / Contact: where users go when something goes wrong
The minimum and “good form”
The OpenAI guidelines state that an App must have a clear way to contact the developer for support. This can be a simple email, but it must exist, receive messages, and get replies at least occasionally.
A minimal option for a training project:
- a dedicated /support page with a short description and a mailto:support@yourdomain.com.
A more mature option:
- a contact form;
- a link to documentation or a Help Center;
- possibly a link to Slack/Discord if you’re building a community around the product.
The /support page in Next.js
Let’s start with a very simple option:
// app/support/page.tsx
export default function SupportPage() {
return (
<main className="mx-auto max-w-xl p-8 prose">
<h1>GiftGenius Support</h1>
<p>
If you have issues or questions, email us at{" "}
<a href="mailto:support@giftgenius.app">support@giftgenius.app</a>.
</p>
</main>
);
}
A slightly more advanced option is to add a simple form:
// app/support/page.tsx
"use client";
export default function SupportPage() {
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
// here will be an API call that sends an email/ticket
};
return (
<main className="mx-auto max-w-xl p-8 prose">
<h1>GiftGenius Support</h1>
<form onSubmit={handleSubmit}>
<input name="email" placeholder="Your email" className="border p-2 w-full" />
<textarea name="message" placeholder="How can we help?" className="border p-2 w-full mt-2" />
<button type="submit" className="mt-4 px-4 py-2 border rounded">
Send
</button>
</form>
</main>
);
}
Even if you don’t implement a real backend for this form in a training project, simply having a clear URL and page structure already brings you closer to Store requirements.
Relation to incident management
A Support page isn’t just “where to write if everything is down,” but also part of your operational picture. In later modules you’ll talk about incidents and the operational life of an App. There the Support page becomes the “front door” for users: bug reports, questions, and data deletion requests come through it. For now, it’s important to at least ensure that such a door exists and doesn’t lead to “404 Not Found.”
6. Integrating legal pages into the app and listing
A single URL configuration inside the project
To avoid scattering “magic strings” with URLs across your code, it’s convenient to create a simple configuration:
// lib/appConfig.ts
export const legalLinks = {
privacy: "https://giftgenius.app/legal/privacy",
terms: "https://giftgenius.app/legal/terms",
support: "https://giftgenius.app/support",
} as const;
You’ll use these same URLs:
- in your ChatGPT App settings (metadata);
- on the product landing page;
- in emails, if you implement email notifications.
Inside the widget you can give the user quick access to these pages via openExternal.
// inside the GiftGenius React widget
import { legalLinks } from "../lib/appConfig";
function FooterLinks() {
const handleOpen = (url: string) => {
window.openai?.openExternal({ url }); // Apps SDK helper
};
return (
<footer className="mt-4 text-xs text-gray-500">
<button onClick={() => handleOpen(legalLinks.privacy)}>Privacy</button>
<span> · </span>
<button onClick={() => handleOpen(legalLinks.terms)}>Terms</button>
</footer>
);
}
In real widget code, it’s better to use the useOpenExternal hook from the Apps SDK; here, for brevity, we show a direct call via window.openai.
This improves transparency: a user can jump to the legal documents from the widget in one click rather than hunting for them in the Store.
User and reviewer flow
Let’s look at the interaction flow as a small diagram:
flowchart TD A[Listing page in ChatGPT Store] --> B[User reads the App description] B --> C[Opens Privacy / Terms via link] B --> D[Installs / starts using the App] D --> E[Launches the GiftGenius widget] E --> F["If needed, taps 'Support' or 'Privacy'"]
A ChatGPT Store reviewer goes through roughly the same path, only a bit more suspiciously. They look at:
- what’s written in the listing;
- what the Policy and Terms promise;
- how the App behaves in a real scenario;
- whether behavior matches what you wrote.
If everything is honest and predictable, your chances of passing review increase sharply.
7. Practical exercise for your App
To avoid staying in theory, it’s useful to draft documents for your app right now.
The approach can be as follows.
First, at the architecture level, describe:
- what data categories you process (request text, orders, email, metrics);
- whether you store text requests and, if so, for how long;
- which external services are connected (hosting, database, payments, analytics).
After that:
- Draft the Privacy Policy structure with sections “what we collect,” “why,” “to whom we disclose,” “how long we store,” “how to delete data.”
- Draft the Terms structure: service description, rules of use, restrictions (prohibited content and abuse), AI disclaimer, limitation of liability, links to the Policy.
- Create a /support page with a short text and an email.
- Add lib/appConfig.ts to the project with URLs of legal pages and use them in the widget and any external links.
Even if the texts are rough and you plan to “show them to a lawyer later,” you’ll have done important work: linking the technical implementation with the legal description.
8. Common mistakes when preparing legal pages
Mistake #1: copy a random privacy policy from the Internet and don’t change it.
You might be tempted to grab the first policy you find, replace the product name, and call it done. The problem is that such a text almost certainly doesn’t match your architecture. It may have sections about a mobile app, push notifications, or specific analytics services you don’t use, and the reverse — it may say nothing about an MCP server, tool logs, or working via ChatGPT. A reviewer will notice discrepancies, and users will feel the text is “about someone else.”
Mistake #2: promise in the Policy what isn’t implemented in code.
A classic example is the phrase “we delete all your data upon first request,” while there’s no deletion endpoint in code and not even a way to find a specific user’s data. The same goes for log retention periods and statements like “we don’t store your message text” if you actually dump tool input to a logging system with no retention. Such a mismatch is risky for both review and real users.
Mistake #3: ignore AI specifics in the Terms.
If the Terms say nothing about answers being generated by a model and potentially being inaccurate, users may expect your App to be “the ultimate truth.” For recommendation services (gifts, travel, product selection), this is still tolerable, but for medicine, finance, or legal advice, such a gap can end very badly. It’s better to state limitations and responsibility clearly and honestly.
Mistake #4: a Support page without a real contact or with a dead address.
A /support page that points to mailto:hello@example.com, which nobody ever checks, formally exists but is effectively useless. Users don’t get feedback, bug reports get lost, and the App’s reputation suffers. The platform also expects you to respond to complaints and issues. Even if you’re a small team, it’s important to review the inbox at least every few days and respond.
Mistake #5: forget the date and version of documents.
Sometimes legal pages don’t indicate when they were last updated at all. For a reviewer, that’s a red flag: it’s unclear whether the documents reflect the current state of the product. A simple “Last updated: …” block solves the problem for both you and users and helps maintain a change history as you evolve the architecture — and the Policy/Terms text accordingly.
GO TO FULL VERSION