Can ChatGPT Buy Clothes for You | Inside the Spec Behind the Buy Button
OpenAI and Stripe publish the machine-readable rulebook for buying inside a chat window, and every release of it sits in public. This desk read all five plus the unreleased changes on September 25, 2026, then lined the fields up against the things a shopper actually carries to a checkout.
There is a line in a public file called schema.discount.json that reads discount_code_user_not_logged_in. It sits in a list of eight reasons a discount code can be refused, in the Agentic Commerce Protocol, the open standard OpenAI and Stripe wrote so that an AI agent could put a coupon on an order. Somebody had to sit down and name that failure. Before the code could ever be typed, the people building the pipe had already written down the moment it wouldn’t work.
That file is one of about thirty in a repository anybody can open. It’s the rulebook behind the Buy button that appeared inside ChatGPT in September 2025, and it turns out to be the most honest document in agentic shopping, because it’s written for machines and machines don’t take marketing copy.
Where does your coupon code go in a chat window?
A coupon code needs a box. On a store’s own checkout page, the box is right there above the total, and the whole ritual of hunting for a working code depends on it existing. Strip the page away and replace it with a conversation, and the box has to be rebuilt as a field in a message between two computers. Otherwise there is nowhere to put it.
For the first four months of in-chat buying, there wasn’t one.
The protocol’s first published release is dated 2025-09-29, the day after the feature launched. Open its checkout schema and discount appears exactly twice: as an integer on a line item, and as a type of total. Both are numbers the store reports downward. Neither is a place for a shopper to hand something up. The same is true of the release dated 2025-12-12 and the one dated 2026-01-16.
The box arrives on 2026-01-30, in the fourth release, as a separate file stamped internally Version: 2026-01-27. Its title is not “Discounts.” It is “Discount Extension,” and the distinction matters more than it looks.
Is the discount field optional?
Extensions in this protocol are negotiated, not assumed. The checkout schema carries a capabilities object, and inside it a list the specification describes as “Extensions the agent understands,” with seller responses containing “the intersection of supported interventions.” Plain version: the agent declares what it can do, the store declares what it supports, and only the overlap exists. If either side never implemented the discount extension, there’s no box. Not a broken box. No box.
Which makes the eight refusal codes worth reading slowly, because two of them aren’t about the code at all. discount_code_user_not_logged_in and discount_code_user_ineligible describe a shopper whose code is perfectly valid and who’s simply not signed in to the account that the code is attached to. The core checkout schema has its own version of this, an error called requires_sign_in.
Anyone who’s ever had a first-order code bounce because the account was wrong will recognize the shape of that. The difference is that here it’s been given a name and shipped.
What can the spec carry, and what can’t it?
The useful exercise is to take the things a person actually carries to a clothing checkout and look for each one in the files.
| What you bring | Is there a field? | What the document says |
|---|---|---|
| A code you type in | Yes, since release four (2026-01-30) | An optional extension both sides must declare |
| A code tied to your account | No, but there’s an error for it | discount_code_user_not_logged_in |
| Loyalty points | Half | LoyaltyInfo carries tier and points_balance, with no field for spending them |
| A gift card | No field found | Zero matches across the current checkout schema |
| Store credit | Only as arithmetic | A totals line type, and a post-purchase adjustment called credit |
| A multi-item cart | Yes, since release five (2026-04-17) | schema.cart.json, absent from the four releases before it |
| The price you were shown | Not yet | suggested_price sits in unreleased changes |
| A return window | A number, without a starting gun | return_deadline_in_days, “use the policy to explain when the window starts” |
Look at the loyalty row again. The buyer object can tell a store your tier, your points balance and the date you joined. There is no field anywhere in the current schema for redeeming any of it. The protocol can introduce you as a member and then leave the membership at the door.
The return row is the one this desk has been circling for a year in another form. accepts_returns is optional, so it can simply be unstated. return_deadline_in_days takes “a positive whole number of days allowed for returns under your policy,” and then the documentation adds the sentence that undoes the field: “Use the policy to explain when the window starts.” Thirty days from what? From the order, from the shipment, from the delivery, from the postmark? That’s prose, and prose doesn’t travel in a feed. Every fight we have documented over when a return clock actually starts and why a return gets denied lives in the gap that sentence leaves open.
Does the sale price in a chat window have to be real?
Prices reach ChatGPT through a product feed the merchant uploads, and the feed spec sets the rules for what a sale is allowed to be. sale_price has to be greater than zero, strictly less than the regular price, and in the same currency. A sale priced equal to or higher than the regular price “is not used.” That’s a tighter rule than most store banners hold themselves to.
Then comes the part that governs everything else. “Sale-window dates, expiration dates, and availability dates do not schedule price or availability changes in this contract.” Uploaded dates decorate. They don’t act. The instruction to merchants is to submit the current price and update the feed when a sale starts or ends, and the integration guide describes feeds as “daily snapshots.”
So the number on a product card in a chat is a snapshot of whatever the seller last pushed, and the amount you’re charged is computed on the seller’s own system at the moment of checkout. The protocol’s own authors know the two can disagree.
There’s an unreleased change sitting in the repository right now that adds suggested_price to an item, so an agent can tell a seller which feed and which timestamp a displayed price came from. The stated benefit is “reducing checkout price discrepancies.” The stated limit is sharper: “Time window and source verification are seller-defined, not protocol-mandated.” The store decides whether to honor the price it showed you, and that mechanism hasn’t shipped.
One cheerful discovery, since this is a desk that complains constantly about sales with no end date. In the promotions API, a Promotion object requires an active_period with both a start_time and an end_time. The machine format won’t accept “ends soon.” Its list of possible benefits is also closed at three: amount off, percent off, free shipping. A tiered buy more, save more offer, a gift with purchase, a spend-and-get, none of them fit.
So can it actually buy the clothes?
Not in the chat, and not since March. OpenAI retired the in-chat Instant Checkout and published the reason in a post titled Powering Product Discovery in ChatGPT: “We’ve found that the initial version of Instant Checkout did not offer the level of flexibility that we aspire to provide, so we’re allowing merchants to use their own checkout experiences while we focus our efforts on product discovery.” Shopify’s VP of product, Mani Fazeli, describes what replaced it in the same post. Buyers “complete purchases on the merchant’s online store through an in-app browser.”
Here are the limits on what this desk can tell you. Nobody here has completed a purchase through an AI agent, in a chat or anywhere else, so none of this is a report on what the experience felt like. The merchant-facing pages at chatgpt.com are closed to us, because that site’s robots file names anthropic-ai and Claude-Web and disallows them, and we don’t dress up as a browser to get around a file like that. CNBC covered the retreat first and its robots file blocks ClaudeBot outright, so its reporting is secondhand to us. The trade publication Modern Retail is open, and its account adds a number worth having: Walmart’s Daniel Danker told Wired that conversion was three times lower for items sold inside the chatbot than for items that required clicking out.
Modern Retail also quotes an unnamed executive at a large specialty retailer listing what Instant Checkout needed before it could be taken seriously. Real-time inventory. Coupons. Promotions. Customer data. Loyalty integration. Store pickup. “It never got there.”
Read that list against the table above and the post-mortem writes itself. The coupon field landed in release four. The cart landed in release five, six weeks after the checkout it was meant for had already been switched off. Loyalty could be announced and not spent. And what Walmart offers today instead, inside its own ChatGPT app, is account linking, loyalty and Walmart payments, which is to say the three things the shared protocol couldn’t carry.
None of this makes the chat window useless for clothes. Discovery is where it now sits, and Target, Sephora, Nordstrom, Lowe’s, Best Buy, The Home Depot and Wayfair have all wired their catalogs into the protocol for exactly that. It does mean the thing on your screen is a feed row with a snapshot price, and the actual transaction is the store’s, under the store’s terms, which is the same set of terms this site keeps reading the fine print on.
The spec is still being written, in public, with release notes. That’s more than most of retail offers. Three questions it has not answered yet. Will the field that makes a displayed price binding ship, or stay unreleased for another year? When a day count travels in a feed and its starting point does not, whose clock wins? And if loyalty and coupons only work inside a merchant’s own app, what exactly was the open protocol for?