1. Why you need a Product Feed at all
If you compare it to classic e-commerce, a Product Feed is something between:
- a product “storefront” (a catalogue with prices, availability, links, and media);
- and a technical contract that describes which SKUs exactly the merchant is willing to show and/or sell via ChatGPT.
OpenAI’s specification explicitly states that the feed is the single source of truth for products, which powers search, recommendations, and preparing checkout data.
In a typical online store, the user browses pages, flips through categories, applies filters, etc. In AI‑commerce it’s the other way around: the user just tells the model “find me a digital gift under $30 for a developer friend who loves board games” — and ChatGPT decides which SKUs from your Product Feed fit, in what order to show them, and how to present them as cards with a subsequent Instant Checkout.
Therefore, the Product Feed solves several tasks at once.
First, it gives ChatGPT structured data for search. The model relies not only on a product’s title and description, but also on categories, tags, price, availability, locale, and country restrictions.
Second, it is a source of checkout data. When ChatGPT starts preparing a checkout_session, SKU IDs, price, currency, the seller URL, and other commercial information are taken from the feed.
Finally, a Product Feed is a formalized contract between you and the platform. You explicitly say: “here’s the list of SKUs; here’s which can be used only for discovery (search), and which can also be purchased via Instant Checkout.”
To see this, it’s helpful to draw a simple diagram.
flowchart TD A[GiftGenius DB] --> B[Feed Builder] B --> C["Product Feed (CSV/JSON/...)"] C --> D[OpenAI Ingestion] D --> E[Search index + ranking] E --> F[ChatGPT/Agent selects gifts] F --> G["Instant Checkout (ACP)"]
On the left is your internal database, where the “real” catalogue lives. On the right is ChatGPT, which the user sees. In the middle are the Product Feed and the ingestion and indexing mechanisms. Everything we cover in this lecture sits precisely between A and D.
2. Formats and the “mechanics” of the Product Feed
The OpenAI specification is quite flexible about file formats: TSV, CSV, XML, and JSON are supported. This is intentional so most existing systems (from homegrown monoliths to Shopify) can export a feed without hacks.
In a typical setup:
- you host a file or endpoint with the Product Feed on your own HTTPS server;
- you register that address in the ChatGPT Merchants portal;
- OpenAI periodically pulls the feed, validates it, and indexes products.
The documentation emphasizes that the feed should be updated regularly (even every 10–15 minutes), so users see up-to-date prices and availability, especially during sales and peak periods.
In the GiftGenius training example we’ll use JSON because it’s comfortable for TypeScript developers. But it’s important to understand: at the specification level OpenAI isn’t tied to JSON; it’s just more convenient for us here.
A minimal JSON feed might look like this:
[
{
"id": "gg-coffee-sub-1m-usd",
"title": "Coffee subscription for 1 month",
"description": "A monthly box of whole-bean coffee for a developer.",
"price": 2900,
"currency": "usd",
"availability": "in_stock",
"link": "https://giftgenius.app/gifts/coffee-subscription-1m",
"image_link": "https://cdn.giftgenius.app/images/coffee-1m.png",
"enable_search": true,
"enable_checkout": true
}
]
In reality there will be more fields; some are required, others recommended or optional. We’ll break this down next.
3. Product vs variant (SKU): how to model it
One of the most common questions: “How do I represent sizes, bundles, subscription durations, and other variants of one product in a Product Feed?”
The Product Feed specification operates on rows/records, each describing a single sellable configuration. The industry pattern (and one that aligns nicely with OpenAI’s feed) is: each distinct configuration (size, subscription duration, plan, region) is a separate feed record, i.e., a separate SKU.
Your base product lives in your internal model, while the feed operates at the SKU level.
For example, you have a service and subscriptions for 1, 3, and 6 months. From the product feed’s perspective, these are different SKUs. If one service can be bought under 20 different terms, your product feed should have 20 SKUs.
In TypeScript this might look like:
// GiftGenius internal model
export interface GiftProduct {
id: string; // product_123
name: string;
description: string;
baseImageUrl: string;
}
// SKU that goes into the Product Feed
export interface GiftSkuFeedItem {
id: string; // product_123_usd_1m
productId: string; // reference to GiftProduct.id
title: string;
description: string;
price: number; // in minor units (cents)
currency: string; // "usd"
}
Within GiftGenius you might have a one-to-many relationship between GiftProduct and GiftSkuFeedItem. In the feed you expose a “flat” list of SKUs.
To help ChatGPT understand which SKUs belong to the same base product (for example, a subscription for 1, 3, and 12 months), a grouping field such as item_group_id is often used. However, this is an architectural pattern, not a hard requirement.
For example:
{
"id": "gg-coffee-sub-1m-usd", // SKU for a 1-month subscription
"item_group_id": "gg-coffee-sub", // Your product
"title": "Coffee subscription — 1 month",
"price": 2900,
"currency": "usd",
"enable_search": true,
"enable_checkout": true
}
And for a 3‑month subscription:
{
"id": "gg-coffee-sub-3m-usd", // SKU for a 3-month subscription
"item_group_id": "gg-coffee-sub", // Same product id
"title": "Coffee subscription — 3 months",
"price": 7900,
"currency": "usd",
"enable_search": true,
"enable_checkout": true
}
This approach makes life easier both for the model and for your backend when creating orders: the SKU ID becomes a unique key by which you can always find the exact configuration the user purchased.
4. Required fields of the Product Feed and their impact on UX
In the Product Feed specification, OpenAI roughly splits fields into three groups: required, recommended, and optional.
Exact names and lists should always be checked in the up-to-date documentation, but for learning purposes we can lean on this “minimal” set.
| Field | What it’s for | What if missing |
|---|---|---|
|
Unique SKU identifier within the merchant | The item cannot be uniquely identified |
|
Short title for the card | Harder for the model to understand what the item is |
|
Extended description | Responses will be more generic; worse personalization |
|
Price in minor units | Cannot prepare checkout |
|
ISO 4217 currency code, usually lowercase | The platform won’t know which currency to use |
|
Product page URL at the merchant | The user won’t be able to go to your site |
|
Stock status (in_stock, out_of_stock, etc.) | Unavailable items may be shown |
|
Whether the item can be used in search | Without true the item won’t appear in results |
|
Whether it can be purchased via Instant Checkout | Discovery/link‑out only |
An important nuance: enable_search and enable_checkout logically separate operating modes.
If enable_search = true, enable_checkout = false, the item can appear in search results, but when attempting to buy, the user will be sent via your link to your site — not into Instant Checkout inside ChatGPT (where a card is already on file).
If enable_checkout = true, then, provided the other conditions are met (supported region, currency, valid ACP backend), the item can be purchased directly in ChatGPT in one or two clicks (which significantly boosts conversion).
Example of a “minimally viable” GiftGenius object for checkout:
{
"id": "gg-dev-notebook-plain-usd",
"title": "Minimalist developer notebook",
"description": "Black, unruled, 120 pages. For those who write specs by hand.",
"price": 1500,
"currency": "usd",
"availability": "in_stock",
"link": "https://giftgenius.app/gifts/dev-notebook",
"image_link": "https://cdn.giftgenius.app/images/dev-notebook.png",
"enable_search": true,
"enable_checkout": true
}
Note: even in this example, we add an image (image_link) — formally it may be recommended, not required, but without it the UX will suffer greatly.
5. Recommended and optional fields: how to make the feed more compelling
Required fields are “so it works at all.” If you stop there, you’ll get something like a minimally valid CSV for accounting, not a great AI storefront.
Recommended fields usually include:
- primary and additional image URLs;
- product category (often based on a taxonomy, such as “gifts > experiences > online courses”);
- brand/merchant name;
- attributes like color, size, material;
- adult-content flags, age restrictions, etc.
The richer you describe the item, the more meaningful responses the model can generate. For example, if you explicitly state that the notebook is made from recycled paper and supports “developers who care about the planet,” ChatGPT can thoughtfully recommend it to a user who asked for eco-friendly gifts.
In GiftGenius, we could expand such a SKU as follows:
{
"id": "gg-dev-notebook-plain-usd",
"title": "Eco notebook for a developer",
"description": "A minimalist, unruled notebook with 120 pages made from recycled paper.",
"price": 1500,
"currency": "usd",
"availability": "in_stock",
"link": "https://giftgenius.app/gifts/eco-dev-notebook",
"image_link": "https://cdn.giftgenius.app/images/eco-dev-notebook.png",
"category": "gifts > office > notebooks",
"brand": "GiftGenius Originals",
"enable_search": true,
"enable_checkout": true
}
Additional attributes like category and brand not only improve search results, but also help with analytics: you can see which categories convert better via ChatGPT and which don’t.
Optional fields are often tied to very specific scenarios (for example, geo-parameters for price, which we’ll discuss separately, or custom metadata). Add them as the project matures, not just to tick a box.
6. Commercial flags and discovery‑only mode
Let’s reiterate the logic for enable_search and enable_checkout, because this is the critical bridge to the next lectures on ACP and Instant Checkout.
Imagine you’re just starting as a ChatGPT merchant. You have a gift catalogue, but your ACP backend and Delegated Payment are still in development. You want ChatGPT to be able to find your SKUs now and send users to your site to pay.
In this case you:
- publish a Product Feed with enable_search = true for the needed SKUs;
- leave enable_checkout = false until you wrap up and certify the ACP integration.
ChatGPT will then be able to include your gifts in answers, show cards, and offer a “Go to the GiftGenius website” link, but it won’t build the internal Instant Checkout UI.
When you implement Agentic Checkout and Delegated Payment, you can flip certain items to “ready for Instant Checkout” — simply by setting enable_checkout = true and additionally meeting all data requirements (price, currency, seller URL, etc.).
At the specification level, exactly the fields from the Product Feed will be used to populate line_items and totals in the checkout_session.
Thus, the feed becomes a lever for fine‑tuning: which SKUs, and in what way, ChatGPT is allowed to sell on your behalf at all.
7. Locales, currencies, regions, and multi‑regional pricing
You’re surely aware the world isn’t limited to en-US and dollars. In our localization modules we already discussed how locale and userLocation affect business logic. Here this comes to the forefront: items in Germany may be priced differently than in the United States, and some gifts simply cannot be sold in certain countries.
The Product Feed specification accounts for this via several mechanisms.
First, currency: currency must be a valid ISO 4217 code (for example, usd, eur, gbp).
Second, fields may describe geo‑dependent price and availability. The documentation provides examples of attributes like geo_price and related region codes based on ISO 3166.
There are two basic architectural approaches.
Approach one: one feed per region.
- product-feed-us-en.json for the USA;
- product-feed-de-de.json for Germany;
- product-feed-br-pt.json for Brazil.
In each feed, all SKUs are already normalized to the required currency and locale. It’s simpler for ChatGPT and more maintenance for you to keep multiple feeds in sync.
Approach two: a single feed with geo fields.
Within each record you store either an array of prices or additional attributes:
{
"id": "gg-dev-notebook-multi",
"title": "Eco notebook for a developer",
"description": "Supports your love for clean code and the planet.",
"prices": [
{ "region": "US", "currency": "usd", "price": 1500 },
{ "region": "DE", "currency": "eur", "price": 1400 }
],
"availability_by_region": [
{ "region": "US", "availability": "in_stock" },
{ "region": "DE", "availability": "out_of_stock" }
],
"enable_search": true,
"enable_checkout": true
}
The exact structure of multi‑regional fields depends on the spec version, but the idea is the same: the feed must allow the platform to understand in which countries the SKU exists and how much it costs there.
From GiftGenius’s perspective, it’s important to think through the mapping between:
- locale and userLocation, which ChatGPT knows;
- and the part of the feed from which to take prices and texts.
Most commercial scenarios avoid returning one record “for the entire world.” Instead, you create different SKUs for different countries to more easily comply with taxes, policy, and product restrictions.
8. Data quality and policy: without them Instant Checkout won’t take off
A Product Feed is not just about the format; it’s also about data quality and adhering to OpenAI policy.
On quality, OpenAI explicitly requires:
- correct, stable identifiers;
- valid HTTPS URLs responding with 200;
- consistent price and currency;
- accurate availability (there must not be in_stock items that are actually unavailable).
The spec also includes limits on text length: for example, title shouldn’t be overly long (hundreds of characters), and description has a reasonable limit (thousands of characters), so cards look neat and don’t become a three‑volume novel.
A separate block is the Prohibited Products Policy. This is a list of product and service categories that cannot be sold via Instant Checkout and/or in ChatGPT at all: obvious things like illegal goods, weapons, certain medical services, etc. Always check the current policy for specifics, but the key point for us: the Product Feed will be checked not only for format, but also for content permissibility.
If your catalogue includes ambiguous categories (for example, alcohol, gambling, or anything involving children), treat those sections with extra care. It’s often simpler to leave them in enable_checkout = false and sell only through your own site with full legal safeguards.
9. Practice: building a minimal Product Feed for GiftGenius
Let’s apply all this in practice and assemble a simple feed for three GiftGenius SKUs. Imagine we have:
- Eco notebook for a developer.
- Coffee subscription for 1 month.
- Gift certificate for the course “TypeScript for adults”.
First, define a TypeScript type that we’ll use to generate the feed:
export interface GiftGeniusFeedItem {
id: string;
title: string;
description: string;
price: number; // in cents
currency: "usd" | "eur";
availability: "in_stock" | "out_of_stock";
link: string;
image_link?: string;
enable_search: boolean;
enable_checkout: boolean;
}
Now create an array in code with a few elements and then serialize it to JSON:
export const giftGeniusFeed: GiftGeniusFeedItem[] = [
{
id: "gg-eco-notebook-usd",
title: "Eco notebook for a developer",
description: "A minimalist, unruled notebook made from recycled paper.",
price: 1500,
currency: "usd",
availability: "in_stock",
link: "https://giftgenius.app/gifts/eco-dev-notebook",
image_link: "https://cdn.giftgenius.app/images/eco-dev-notebook.png",
enable_search: true,
enable_checkout: true
},
{
id: "gg-coffee-sub-1m-usd",
title: "Coffee subscription for a developer — 1 month",
description: "A monthly box of whole-bean coffee. Pairs well with deadlines.",
price: 2900,
currency: "usd",
availability: "in_stock",
link: "https://giftgenius.app/gifts/coffee-subscription-1m",
image_link: "https://cdn.giftgenius.app/images/coffee-1m.png",
enable_search: true,
enable_checkout: true
},
{
id: "gg-ts-course-gift-usd",
title: "Gift certificate for a TypeScript course",
description: "An online course for developers who finally want to understand generics.",
price: 9900,
currency: "usd",
availability: "in_stock",
link: "https://giftgenius.app/gifts/ts-course",
image_link: "https://cdn.giftgenius.app/images/ts-course.png",
enable_search: true,
enable_checkout: false // discovery only for now
}
];
Next, you can build a simple utility that generates a product-feed.json file every N minutes from this structure and uploads it to your HTTPS server.
import { writeFile } from "node:fs/promises";
import { giftGeniusFeed } from "./feed-data";
// The simplest JSON feed generator
async function buildProductFeed() {
const json = JSON.stringify(giftGeniusFeed, null, 2);
await writeFile("public/product-feed.json", json, "utf8");
}
buildProductFeed().catch(console.error);
In a real project you won’t store the entire feed in code; instead, data will come from a database. But to start, it’s useful to assemble at least this sort of training example to test the pipeline: generation → upload → validation.
10. Anti‑example: what a “bad” Product Feed looks like
To better feel the spec and UX requirements, it helps to look at a feed example that almost works formally but will cause problems in practice:
{
"id": "1",
"title": "Gift",
"description": "Cool gift",
"price": 12.333333,
"currency": "usdollars",
"availability": "yes",
"link": "http://giftgenius.local/gift/1",
"enable_search": "true",
"enable_checkout": "maybe"
}
You can spot several issues right away.
First, id = "1" is an unstable and impoverished identifier. If you ever migrate your DB or introduce sharding, such identifiers become fragile. It’s better to use meaningful, sufficiently long IDs that are unique within the merchant.
Second, price is a decimal number with a long tail. Specs and payment systems usually expect price as an integer in minor units (cents, pennies) to avoid floating‑point and rounding problems.
Third, currency = "usdollars" and availability = "yes" don’t match expected formats (ISO 4217 and an allowed set of statuses).
Fourth, link uses http and a local domain — neither is acceptable for real production; the specification explicitly requires HTTPS and public availability.
Fifth, the enable_search and enable_checkout flags must be booleans, not strings. Otherwise, OpenAI’s parser will either reject the feed or coerce to a default value that may surprise you.
Such issues can lead to a hard validation error (feed rejected) or a nastier situation: the feed is formally accepted, but some SKUs are ignored or behave unexpectedly. That’s why you should invest in internal validation on your side.
11. Common mistakes when working with a Product Feed
Mistake #1: thinking of the feed as a “one‑off CSV” for import.
Sometimes teams treat the Product Feed as a file they’ll generate once “for integration” and forget. In AI‑commerce that’s not the case: the feed is a living source of truth that must be updated regularly. If you change prices, delist items, or launch promos — all of this must promptly reach the feed. Otherwise, ChatGPT will recommend what no longer exists or at old prices, and users will rightfully be annoyed.
Mistake #2: mixing the product model and the SKU.
A popular anti‑pattern is trying to represent one base product with lots of options in a single feed record with many fields like “size1/size2/size3” or “duration1/duration2.” The result is that the model doesn’t understand what exactly is being sold, and your ACP backend suffers unpacking these fields during checkout. It’s much simpler and more reliable: one SKU — one feed record, even if it’s a variant of the same product.
Mistake #3: ignoring locales and regions.
Developers building the first MVP often set currency = "usd" and enable_checkout = true for everything without considering that Instant Checkout may be unavailable in their region or that some items can’t be sold in certain countries due to policy or law. Later, when entering a new market, things break: prices don’t match, taxes aren’t accounted for. It’s better to tie SKUs to regions and currencies from the start, even if you currently operate in only one market.
Mistake #4: treating descriptions like old‑school SEO text.
Some teams copy old site descriptions into the Product Feed, sometimes written for “keywords” and bots. For ChatGPT this is more harmful than beneficial: the model can write text on its own; it needs structured, honest, and precise facts. It’s better to describe concisely and to the point than to stuff description with marketing fluff.
Mistake #5: not validating the feed yourself.
Relying solely on OpenAI’s validation is a recipe for sleepless nights before deadlines. Build a simple validator in your backend or CI that checks field schemas, allowed values, URL and currency formats. You can even do this in TypeScript, using, for example, Zod or your own checks. Then you’ll catch issues before pushing the feed to production.
Mistake #6: stuffing “everything” into the Product Feed.
It’s tempting to throw thousands of SKUs into the feed “just in case.” In practice this complicates debugging, analytics, and quality control. It’s more reasonable to start with a limited subset: only those categories and SKUs you’re ready to maintain and truly want to sell via ChatGPT. Keep the rest in discovery mode or skip integrating them altogether.
Mistake #7: not syncing the Product Feed and the ACP backend.
The feed and the ACP API are two sides of the same coin. If a new SKU appears in the feed but your backend can’t sell it yet (or vice versa — the SKU was removed from the feed, but the backend still assumes it exists), you’ll get desynchronization, tricky bugs, and complex support tickets. Good practice is to have a single domain model of the catalogue and use it both to generate the feed and to process checkout.
GO TO FULL VERSION