Agentic Commerce Protocol (ACP): How Agent Checkout Works
How the Agentic Commerce Protocol runs an agent checkout, what its delegated payment token limits, and the stock state a merchant must read at complete.
How the Agentic Commerce Protocol runs an agent checkout, what its delegated payment token limits, and the stock state a merchant must read at complete.
TL;DR: The Agentic Commerce Protocol (ACP) is an open standard, founded by OpenAI and Stripe, for how an AI agent runs a checkout against a merchant: create a session, update it, complete it with a delegated payment token. The merchant stays the system of record for the order, and ACP does not say when to reserve stock. That decision runs on authorization-time context: available stock net of open holds, this session’s hold and its expiry, read at the moment the session is completed.
A checkout page built for people has a forgiving rhythm. A shopper adds an item, wanders off, comes back, types a card number. Stock problems show up as an occasional “sorry, that sold out” email, and support absorbs the rest.
Agents change the rhythm. An agent can open a checkout session, update it, and complete it in the time a person takes to read the product title. Many agents can do that at once against the same item, especially when a drop or a price change sends every assistant to the same product page. The merchant’s checkout now answers machine-speed requests, and each answer is a commitment the agent repeats to a buyer.
This page covers how ACP structures a checkout, what the delegated payment token is allowed to do, and what state the merchant has to read correctly when an agent says “complete.”
The Agentic Commerce Protocol (ACP) is an open standard for AI agents to buy from merchants. The agent creates and updates a checkout session through the merchant’s API, the merchant returns the authoritative cart on every response, and the agent completes the session with a delegated payment token. The merchant keeps the order, payment and compliance.
Stripe announced ACP on September 29, 2025 as “a new, merchant-friendly open standard codeveloped by Stripe and OpenAI,” alongside Instant Checkout in ChatGPT. The same announcement says “ACP is an open standard: businesses not processing with Stripe can still adopt it with their existing payment providers, and it works across AI agents.”
The specification repository describes ACP as “an interaction model and open standard for connecting buyers, their AI agents, and businesses to complete purchases seamlessly” and says it is “currently in beta.” OpenAI and Stripe are its founding maintainers, and its governance document lists Meta in a third seat on the Technical Steering Committee, added in April 2026. It uses date-based versions; the latest stable one listed in the README is 2026-04-17, which added cart, feed, orders, authentication and MCP.
OpenAI’s commerce documentation covers three pieces: “agentic checkout, delegated payment, and product feeds.” The merchant provides “a structured product feed so ChatGPT accurately indexes and displays your products with up-to-date price and availability,” implements the Agentic Checkout Spec, and works with a payment provider, since “Payment Service Providers implement the Delegated Payment Spec to handle and secure Agentic Commerce transactions.”
The first merchants arrived through platforms. Stripe’s launch announcement said “ChatGPT users in the US can buy goods from US-based Etsy businesses—and, coming soon, over a million Shopify merchants,” and that merchants keep “full control over what’s sold, how their brand shows up, and how orders are fulfilled.” On October 14, 2025, Salesforce said its ACP partnership “will allow the thousands of merchants using Agentforce Commerce to harness the power of conversational AI for a faster path to purchase.”
The Agentic Checkout RFC states up front the line that shapes everything after it: “The merchant remains the system of record for all orders, payments, taxes, and compliance.” Orders run on the merchant’s existing commerce stack, and “Payment authorization and settlement continue to occur via the merchant’s PSP.”
The session has five operations:
Every response carries the full cart: line items, fulfillment options, totals and a status. The RFC’s status list names five (not_ready_for_payment, ready_for_payment, completed, canceled and in_progress), and the 2026-04-17 OpenAPI spec defines more, including authentication_required and expired. All money fields are integers in minor units. Problems come back as messages[], and the error codes include out_of_stock and payment_declined. The OpenAPI spec also defines warnings such as low_stock and high_demand.
Since the 2026-04-17 release, every POST must carry an Idempotency-Key. The draft RFC on the repository’s main branch says what that protects, in an idempotency section its own change log still lists as unreleased: “Servers MUST NOT re-execute side effects (e.g., payment capture, inventory reservation) on replay.” Its implementation guidance, marked informative, recommends that the key record and the business operation “be committed in the same ACID transaction to prevent ghost keys (key stored, operation failed) or lost keys (operation succeeded, key not stored).” For the general pattern, see idempotency.
| Call | What it does |
|---|---|
POST /checkout_sessions | Create a session from items and optional buyer and address |
POST /checkout_sessions/{id} | Update items, address or fulfillment option |
GET /checkout_sessions/{id} | Return the latest authoritative state |
POST /checkout_sessions/{id}/complete | Finalize with a payment method; MUST create an order |
POST /checkout_sessions/{id}/cancel | Cancel if not already completed or canceled |
The agent never hands the merchant a raw card. ACP’s Delegate Payment spec “Tokenizes a credential for controlled usage by the merchant’s PSP per the Allowance constraints.” The Allowance object requires six fields:
reason: currently only one_timemax_amount: the maximum charge in minor unitscurrencycheckout_session_id: the session this payment is formerchant_id: the merchant allowed to use the tokenexpires_at: when the allowance expiresIn Stripe’s flow, the token is a Shared Payment Token (SPT), which Stripe’s launch announcement says is “scoped to a specific merchant and cart total.” Stripe’s docs call an SPT “a scoped grant to use the customer’s payment method,” granted “each with usage and expiration limits,” and say “The agent sets the maximum amount to match the total amount of the transaction.”
That is a tight credential. It works for one session, one merchant, up to one amount, until one time. It does not know whether the merchant still has the item.
Before an agent can check out, it has to find the item. ACP’s Feed API lets a merchant push its catalog to a feed the agent hosts, in two ways: upload a full snapshot, or upsert products through PATCH /feeds/{id}/products. Its status is mixed. The 2026-04-17 release ships a feed OpenAPI spec, and its changelog adds “an unreleased Feed API surface,” while the product feeds RFC on the main branch still reads “Status: Proposal.”
The RFC is direct about what a feed does not promise. “Feed prices are discovery-time signals.” “Availability in a feed does not reserve inventory. Reservation semantics, if any, occur during checkout.” And: “Agents MUST treat checkout responses as authoritative even when they differ from feed data.” Its reasoning: “Feed data can be cached, delayed, or scoped for discovery,” and keeping checkout authoritative “avoids making feeds responsible for transactional guarantees they cannot provide.”
So a feed can send every agent to the same item at once, and checkout is where the merchant has to be right.
The RFC lists what it does not cover: “PSP authorization/capture semantics, returns/exchanges workflows, tax configuration, fraud modeling details.” Inventory is not on that list, but the checkout RFC doesn’t define it either. ACP gives the merchant a way to report stock (out_of_stock, low_stock), but when to reserve stock, for how long, and what ready_for_payment promises about availability are the merchant’s call.
That leaves three points where a merchant can check stock, each with a different promise:
At create or update. The merchant can place a hold when the session first prices the cart. The agent gets a stable answer, at the cost of stock held for sessions that never complete.
At complete. The merchant can check and decrement only when the agent completes. No stock sits idle, but ready_for_payment is a statement about the past.
Both. A short hold at create, re-checked at complete, released on cancel or expiry. This is the usual answer, and it is the one that needs the most state.
The numbers below are illustrative, built to show the mechanism.
A merchant sells a limited sneaker release through ACP: 40 pairs. Its checkout service reads availability from a cache that a job refreshes from the order management system every 60 seconds, and it places no holds.
Fifty-five agents told their buyers the order was placed, because complete returned completed minutes before fulfillment found no stock. The merchant now cancels 55 orders and reverses 55 payments, each one a support ticket and a buyer who trusted the assistant.
Nothing in ACP failed. Every call matched the spec. The checkout answered from context that was up to a minute old and did not count other sessions that had already been told yes. That is a race condition on stock, and agents make it common, because they arrive together and complete fast. For the non-agent version of the same failure, see real-time inventory oversell.
| Time | Event |
|---|---|
| 10:00:00 | Release goes live. Cache says 40 available. |
| 10:00:05–10:00:50 | 260 agents create sessions. Each reads 40 available and returns ready_for_payment. |
| 10:00:20–10:00:55 | 95 agents call complete. Each call creates an order, as the spec requires. |
| 10:01:00 | Cache refreshes to 0. New sessions now return out_of_stock. |
| 10:05:00 | Fulfillment allocates the 40 pairs. 55 orders have nothing to ship. |
The fix is to make the hold, the check and the idempotency record one transaction.
-- At create: place a hold (Postgres-style SQL). On update, resize it by the difference.
BEGIN;
INSERT INTO idempotency_keys (key, endpoint, request_hash)
VALUES (:idem_key, 'create_session', :request_hash);
-- unique violation: a replay. ROLLBACK and return the stored response
-- (or idempotency_conflict if request_hash differs).
UPDATE stock -- one UPDATE per SKU, in SKU order, to avoid deadlocks
SET held = held + :qty
WHERE sku = :sku
AND on_hand - held >= :qty
RETURNING sku; -- zero rows: skip the hold insert, answer out_of_stock
INSERT INTO holds (session_id, sku, qty, status, expires_at)
VALUES (:session_id, :sku, :qty, 'active', :session_expires_at);
UPDATE idempotency_keys -- store the answer, hold or out_of_stock,
SET response = :response -- so a replay gets the same one
WHERE key = :idem_key AND endpoint = 'create_session';
COMMIT;
-- At complete: convert the hold only if it is still live, and move qty from
-- held to sold on the stock row in the same transaction:
-- UPDATE holds SET status = 'converted'
-- WHERE session_id = :session_id AND status = 'active' AND expires_at > now();
-- zero rows -> expired or released; re-run the stock check before creating the order
Every exit from the session changes the hold in the same transaction as the session status.
Three details matter:
Tie the hold to the session. ACP sessions carry an expires_at. A hold that outlives the session is stock held for a checkout that can no longer complete, so expire the hold no later than the session.
Release on every exit. Cancel, expiry and payment failure all release the hold, in the same transaction that changes the session status. The expiry job and a late complete call both update the same holds row with a status = ’active’ check, so only one of them wins.
Keep holds next to the stock count. Under READ COMMITTED, a second conditional UPDATE on the same row waits for the first to commit and then re-evaluates its WHERE clause against the updated row, so two holds cannot both claim the last unit. That guarantee only holds if holds and stock live in the same transactional store.
If your catalog is small and your order system can take this traffic, do it there and stop. It gets harder when the order system can’t absorb agent-rate reads, when on-hand arrives from warehouses and stores, or when the checkout also reads derived signals such as per-buyer purchase limits during a drop or recent cancellation rates by agent platform.
Tacnode holds the checkout’s decision context: what each completion reads and what it decides. The order and the payment stay where ACP puts them, with the merchant’s order system and PSP as the system of record.
On-hand counts from the order system arrive by change data capture, so the checkout reads stock in Tacnode instead of querying the order system on every agent call. Holds, session status, idempotency keys and per-buyer limits are state the checkout owns. Held in the Tacnode Context Lake™, they are covered by full ACID transactions: two sessions cannot both hold the last unit, because the hold is a committed write. Derived reads such as available-to-sell net of holds or holds per agent platform in the last minute are computed on demand against the latest committed data, in the same snapshot as the hold itself. Each completion is written back and becomes context for the next session.
That closes the context gap for one decision: the merchant says ready_for_payment and completed with an accurate picture of what it has already promised. For the payment side of the same loop, see x402 and agentic TOCTOU.

Former Cockroach Labs. Tells stories about infrastructure that actually make sense.
Fresh Context Newsletter
The argument behind the posts — what breaks when AI acts on stale data, from the team building the Context Lake. About twice a month.
Book a demo and discover how Tacnode can power your AI-native applications.
Book a Demo