Shopware’s Agentic Commerce extension has been out for a few weeks, so I installed it on a real shop, pointed a fake agent at it, and watched an order appear. It works. Getting from install to that first order took three separate fixes that nothing in the readme prepares you for.
The Shopware Universal Commerce Protocol is the interesting half of that plugin. It is the contract that lets an AI shopping agent search your catalog, build a cart and complete a checkout over HTTP, without a browser and without your storefront ever rendering.
Here is what actually happened, including the part where the money does not arrive.
What the Shopware Universal Commerce Protocol plugin actually does
The plugin bundles three features that get talked about as one thing, and they are independent:
- UCP, the transactional surface. REST, A2A and embedded endpoints under
/ucp/v1/, with a public profile at/.well-known/ucpdescribing which capabilities the shop supports. This is what an agent calls to buy something. - Native agentic discovery.
/llms.txt,/agents.mdand/.well-known/ai-catalog.json, which tell crawlers and shopping agents how the merchant wants the shop used. On 6.7 these come from core, per sales channel. - Product feeds. OpenAI JSONL and Google Merchant XML exports built on Shopware’s product export system. Product links carry
referringSalesChannelplus configured affiliate and campaign codes, so orders that come back are attributable.
A merchant can run the feeds and discovery documents without ever exposing UCP. Most should start exactly there. I covered that side in what agentic commerce means for store visibility; this post is the implementation underneath it.
How to install the Agentic Commerce plugin
It is not on Packagist. Requesting shopware/agentic-commerce from repo.packagist.org returns a 404, which surprised me since the plugin is public on GitHub. Distribution goes through the Shopware Store or the release zips attached to each GitHub tag.
So the install is a zip upload under Extensions, or an unzip into custom/plugins/ and bin/console plugin:install --activate SwagAgenticCommerce. The plugin declares ucp-php-sdk/symfony-bundle as a hard dependency and pulls it through Shopware’s plugin Composer commands during installation. On managed hosting that blocks Composer at runtime, vendor that dependency in your build instead.
My test lane was Shopware 6.7.13.0 on ddev with PHP 8.4. Install ran six migrations, the SDK added six more tables of its own for signing keys, idempotency records, replay nonces and OAuth state, and the admin bundle compiled through Vite without complaint. That part was boring, which is what you want.
Three things blocked my first UCP agent request
Every one of these produced an error that pointed somewhere other than the cause.
Only Storefront-type sales channels answer agent traffic
The shop had an Agentic Commerce sales channel already, so I enabled UCP on it. Every request to its host came back as HTTP 400 with a page titled “Shopware Domain Mapping Misconfiguration”.
The reason sits in Shopware core, not the plugin. The storefront’s DomainLoader builds its host map with one filter:
$query->where('sales_channel.type_id = UNHEX(:typeId)');
$query->setParameter('typeId', Defaults::SALES_CHANNEL_TYPE_STOREFRONT);
Any host belonging to a channel of another type is unknown to the storefront request transformer, and the UCP routes never run. Only /api and /store-api paths skip that transformer, and /ucp/v1/ is neither.
So a UCP channel must be a Storefront-type channel with a public HTTPS domain. Adding a dedicated hostname does not help by itself. I proved the diagnosis by switching the channel type and watching the same URL go from 400 to 200 with no other change.
The UCP settings screen sits on the channel type that cannot serve it
Here is the part I would call a design contradiction. The plugin’s Agentic Commerce admin screens, including the UCP exposure settings, render only when the open sales channel is of the Agentic Commerce type:
isAgenticCommerce() {
return this.salesChannel.typeId === Defaults.agenticCommerceTypeId;
}
The channel type you can configure in the admin is the one Shopware refuses to serve. The channel type that works has no UCP screen at all.
The CLI does not close the gap either. ucp:config:set handles signature policy, allowlists and delivery URLs, and its own help text says the exposure fields stay in the Administration. To switch UCP on for a Storefront channel I had to call the admin API directly:
PUT /api/_admin/ucp/sales-channels/{salesChannelId}/config
{"active": true, "profileDomain": "https://shop.example.com",
"enabledTransports": ["rest", "a2a", "embedded"]}
That endpoint needs the ucp.editor ACL. Worth knowing before you promise a client a click-through setup.
The SDK rejects every local hostname
With exposure on, /.well-known/ucp finally published transports and capabilities. The first runtime call still failed:
Profile host "shop.ddev.site" resolves to a blocked IP address.
Every UCP request carries a UCP-Agent header naming the agent’s own profile URL, and the shop fetches that profile to get the agent’s signing keys. The SDK’s UrlSafetyValidator refuses any host that resolves to a private address, which is every ddev or local hostname. Two details make it stricter than it first looks:
- The blocked-IP check runs before the allowlist check, so adding your own host to
remoteProfileAllowlistchanges nothing. - The development escape hatch only accepts a literal
localhost, and only whenucp_sdk.profile_fetching_development_modeis enabled in bundle config.
The plugin never passes that flag when it builds its per-channel runtime configuration, so it cannot be set per sales channel at all. On a local lane you need a project-level config file, guarded so it can never reach production:
# config/packages/ucp_sdk.yaml
when@dev:
ucp_sdk:
profile_fetching_development_mode: true
On a live shop with a public domain, none of this applies. Real agent platforms have public profile hosts and the guard does its job. It only bites during local development, which is exactly when you are trying to learn the thing.
What a real agent order looks like
Once those three were sorted, the flow is genuinely simple. Every call needs the UCP-Agent header and a unique Idempotency-Key:
curl -X POST https://shop.example.com/ucp/v1/catalog/search \
-H 'content-type: application/json' \
-H 'UCP-Agent: my-agent; profile="https://agent.example.com/.well-known/ucp"' \
-H "Idempotency-Key: $(openssl rand -hex 8)" \
-d '{"query":"linen","limit":2}'
Search returns products with prices in minor units and image URLs. From there it is POST /ucp/v1/checkout-sessions with line items, buyer and fulfilment address, then POST /ucp/v1/checkout-sessions/{id}/complete. The session goes from ready_for_complete to completed and a real Shopware order lands in the admin with a real order number.
Two details worth keeping:
- The shop wins on price. I passed 777.63 in the line item, deliberately wrong. The order came out at 304.81, the shop’s own price. Agent-supplied prices are advisory, which is the correct behaviour and worth stating to any merchant who asks whether an agent can talk the price down.
- Idempotency is enforced, not decorative. Reusing a key with a different request body returns
idempotency_conflictas an unrecoverable error. Any client you build needs fresh keys per request.
How do agent checkouts get paid?
This is the question to settle before anyone sells this to a client, and the honest answer is that the agent does not pay. When I completed a checkout over REST, the order arrived with its transaction in the open state against CashPayment, the channel’s default method. An unpaid invoice order, in other words.
The tokenized path, where an agent hands over payment credentials and the shop charges them, is not shipped. POST /ucp/v1/tokenize returns 501, and the plugin’s own documentation is direct about why: it ships no fake tokenizer. The only handler included is ShopwareInvoicePaymentHandler, which returns false from supportsTokenization(). Turning that on requires a PHP service implementing Ucp\Sdk\Contract\PaymentHandlerInterface, tagged ucp_sdk.payment_handler, supplied by a payment plugin.
That tag matters more than it looks. It is a PHP service tag, so the handler has to come from a plugin. Shopware Payments is delivered as an app talking to Shopware over HTTP, and an app cannot register a PHP service, so it cannot supply that handler by itself.
What does work is the handoff. The embedded transport renders a summary page of the cart or checkout with a “Continue checkout” button that opens your configured continue URL in the top frame:
{% if data.continue_url is defined and data.continue_url is not empty %}
<a class="cta" target="_top" href="{{ data.continue_url }}">Continue checkout</a>
{% endif %}
The shopper finishes in the shop’s own checkout with the shop’s own payment methods, Shopware Payments included, and the order gets paid like any other order. Set --continue-url-template on the channel, allowlist the agent platform’s origin for the iframe, and that path is ready.
So the claim to make is that agents can discover the catalog, build a cart and hand the buyer to checkout. The claim to avoid is autonomous agent purchasing.
Running the plugin’s own test suites
The plugin ships unit, integration and functional suites, and the functional one drives UCP routes through a booted kernel into a real order. On a shopware/production install none of them run out of the box.
The bootstrap resolves the project directory from Composer metadata without normalising the path:
$corePath = InstalledVersions::getInstallPath('shopware/core');
// /var/www/html/vendor/composer/../shopware/core
$projectDir = dirname($corePath, 3);
// /var/www/html/vendor/composer <- wrong
A realpath() on that value fixes it. Shopware’s own CI never sees the bug because their lanes are monorepo checkouts, where the path ends in /src/Core and takes the other branch of that dirname(). After the fix, all three suites pass: 579 unit tests, 2 integration tests and 7 functional tests, including a checkout completed into an order and read back.
One warning before you try it. The kernel bootstrap runs system:install --create-database --force whenever it finds no plugin table, and APP_ENV=test makes Symfony skip .env.local, which is where ddev writes your database URL. Point that suite at a scratch database explicitly, or it will reinstall over your working shop.
What this means if you run a Shopware shop
The deadline is the reason to act, not the trend. Shopware 6.7 shipped agentic commerce inside core, 6.7.12.0 moved it into the extension, and the core admin now shows merchants this warning: the built-in feature is removed in 6.8 and the extension has to be installed so nothing breaks. Any shop that switched it on has migration work attached to its next major version upgrade.
Beyond that, the sensible order is:
- Feeds and discovery first. Cheap to configure, no protocol exposure, and the attribution codes in the feed links let you measure whether anything came of it.
- Agent checkout second, with the payment handoff wired and tested, once the shop has a reason to want it.
- Nothing before the numbers. The SDK is version 0.0.5. Pre-1.0 means breaking changes, so budget for a revisit rather than a set-and-forget install.
Every capability you expose is an endpoint that bots can hit. Signature policy, agent allowlists and embedded origins are configuration you actually have to do, not defaults you inherit.
If you want this set up on a live shop properly, or an honest read on whether it is worth it for your catalog, get in touch or look through the case studies for how I work.
Frequently asked questions
What is the Universal Commerce Protocol in Shopware? UCP is the transaction contract that lets an AI shopping agent search a catalog, build a cart and complete a checkout over HTTP instead of through a browser. In Shopware it arrives through the free Agentic Commerce extension, which wires Shopware’s catalog, cart, checkout and order handling to the ucp-php-sdk. The shop publishes what it supports at /.well-known/ucp, and agents call /ucp/v1/ endpoints from there.
Which Shopware versions support the Agentic Commerce plugin? It supports 6.5, 6.6 and 6.7 from one codebase and feature-detects per version instead of shipping dead links. Shopware 6.7.10 briefly had agentic commerce built into core, 6.7.12.0 moved it into the extension, and the core admin now warns that the built-in version is removed in 6.8.
How do I install the Shopware Agentic Commerce plugin? It is not on Packagist. Download SwagAgenticCommerce.zip from the GitHub releases page or install Agentic Commerce from the Shopware Store, then upload it under Extensions. Shopware runs the plugin’s Composer commands during installation to pull the UCP SDK, so a pipeline that blocks Composer needs that dependency vendored.
Can an AI agent pay for an order in Shopware? Not on its own today. A completed UCP checkout creates a real order, but the transaction lands in the open state on the channel’s default payment method, and /ucp/v1/tokenize returns 501 because no tokenizing payment handler ships with the plugin. The working route is the embedded handoff, where the shopper finishes in the shop’s checkout and any payment method including Shopware Payments collects the money.
Which sales channel do I use for UCP? A Storefront-type channel with a public HTTPS domain. Shopware’s storefront DomainLoader only maps domains of Storefront-type channels, so any other channel’s host returns the domain mapping error before UCP routes run. The Agentic Commerce channel type is for the product feed and tracking.
Is Shopware’s UCP implementation production ready? Discovery documents and product feeds are ready to deploy. The transactional side runs on ucp-php-sdk 0.0.5, which is pre-1.0 and will change, and it needs decisions on payment, fraud and support handling before real buyers touch it.