Tacnode™
Back to Blog
AI Infrastructure

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.

Alex Kimball
Alex Kimball
Product Marketing
11 min read
A grid of 95 squares, one per completed agent checkout, under the line “95 orders for 40 pairs”: 40 filled cyan for the pairs in stock and 55 orange outlines for orders with nothing to ship.

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.”

What Is the Agentic Commerce Protocol?

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.

Implementing ACP as a Merchant

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.”

How an ACP Checkout Works

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.

CallWhat it does
POST /checkout_sessionsCreate 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}/completeFinalize with a payment method; MUST create an order
POST /checkout_sessions/{id}/cancelCancel if not already completed or canceled

The Delegated Payment Allowance

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_time
  • max_amount: the maximum charge in minor units
  • currency
  • checkout_session_id: the session this payment is for
  • merchant_id: the merchant allowed to use the token
  • expires_at: when the allowance expires

In 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.

ACP Product Feeds: Discovery, Not a Promise

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.

What ACP Leaves to the Merchant

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.

A Worked Failure: The Drop

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.

TimeEvent
10:00:00Release goes live. Cache says 40 available.
10:00:05–10:00:50260 agents create sessions. Each reads 40 available and returns ready_for_payment.
10:00:20–10:00:5595 agents call complete. Each call creates an order, as the spec requires.
10:01:00Cache refreshes to 0. New sessions now return out_of_stock.
10:05:00Fulfillment allocates the 40 pairs. 55 orders have nothing to ship.

Authorization-Time Context at Complete

Call it authorization-time context: everything the decision to accept a completion has to read, at the moment it runs. For an ACP complete call that is:

  • Available to sell, net of open holds. On-hand from the order system, minus holds placed by other live sessions.
  • This session’s hold. Whether it still exists and has not expired.
  • The session’s expiry. The session’s expires_at against the clock. The payment token’s allowance is applied at the PSP, per the Delegate Payment spec.
  • Session status and the idempotency record. Whether this complete is new, a replay, or a conflict.

Most of this is derived context: counts and sums across many sessions, all changing as other agents create, complete and abandon checkouts. It has to be read in one consistent snapshot. A hold count from one store and an on-hand count from another, read a few seconds apart, can both look fine and still add up to an oversell.

How to Hold Checkout State Correctly

The fix is to make the hold, the check and the idempotency record one transaction.

sql
-- 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.

One ACP checkout session as a line: a stock hold placed at create runs along the session and then forks, converting on complete or releasing on cancel or expiry

Where Tacnode Fits

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.

FAQ

Agentic Commerce ProtocolAgentic CommerceAI AgentsCheckoutInventory
Alex Kimball

Written by Alex Kimball

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.

Subscribe on LinkedIn

Ready to see Tacnode Context Lake™ in action?

Book a demo and discover how Tacnode can power your AI-native applications.

Book a Demo