<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Peter</title>
    <description>The latest articles on DEV Community by Peter (@ucptools).</description>
    <link>https://dev.to/ucptools</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3737432%2F036b6d50-5faf-4939-ac76-c7f56e7a6382.jpeg</url>
      <title>DEV Community: Peter</title>
      <link>https://dev.to/ucptools</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ucptools"/>
    <language>en</language>
    <item>
      <title>UCP v2026-08-25 Breaks Quietly: 4 Changes That Still Pass Validation</title>
      <dc:creator>Peter</dc:creator>
      <pubDate>Sat, 29 Aug 2026 12:58:57 +0000</pubDate>
      <link>https://dev.to/ucptools/ucp-v2026-08-25-breaks-quietly-4-changes-that-still-pass-validation-537p</link>
      <guid>https://dev.to/ucptools/ucp-v2026-08-25-breaks-quietly-4-changes-that-still-pass-validation-537p</guid>
      <description>&lt;p&gt;On August 25, 2026, the Universal Commerce Protocol shipped &lt;code&gt;v2026-08-25&lt;/code&gt;. It is the first&lt;br&gt;
new spec version in four and a half months, and it is a big one: a multi-vertical refactor&lt;br&gt;
laying groundwork for Food and Lodging, grocery and location capabilities, 3D Secure via a&lt;br&gt;
new Actions primitive, split payments and installment terms, a loyalty extension, and Web&lt;br&gt;
Bot Auth interop.&lt;/p&gt;

&lt;p&gt;The release notes carry a clearly labelled "Breaking Changes" section. Good.&lt;/p&gt;

&lt;p&gt;The problem is that the most dangerous changes in it are not the ones that break loudly.&lt;br&gt;
They are the ones where your profile still parses, still validates against a naive checker,&lt;br&gt;
and agents quietly stop working.&lt;/p&gt;

&lt;p&gt;I know, because our own validator got this exactly wrong. More on that below.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why this release breaks quietly
&lt;/h2&gt;

&lt;p&gt;Look at &lt;code&gt;source/schemas/profile.json&lt;/code&gt; at tag &lt;code&gt;v2026-08-25&lt;/code&gt;. The base definition ends like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"object"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"required"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"ucp"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ucp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"$ref"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ucp.json#/$defs/base"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"keys"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"array"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"items"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"$ref"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#/$defs/jwk_public_key"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"additionalProperties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two things matter here.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;required&lt;/code&gt; is &lt;code&gt;["ucp"]&lt;/code&gt; and nothing else. And &lt;code&gt;additionalProperties&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That combination means a profile carrying removed fields is &lt;strong&gt;not invalid&lt;/strong&gt;. It parses. It&lt;br&gt;
validates. It just contains members that nothing reads any more. When a spec renames a field&lt;br&gt;
under &lt;code&gt;additionalProperties: true&lt;/code&gt;, the old name does not become an error. It becomes&lt;br&gt;
decoration.&lt;/p&gt;

&lt;p&gt;That is the failure mode to hunt for in this release. Not "my profile is rejected" but "my&lt;br&gt;
profile is accepted and ignored".&lt;/p&gt;
&lt;h2&gt;
  
  
  Silent breaker 1: &lt;code&gt;signing_keys&lt;/code&gt; became &lt;code&gt;keys&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;This is the big one, and it is the cleanest example of the pattern.&lt;/p&gt;

&lt;p&gt;Before &lt;code&gt;v2026-08-25&lt;/code&gt;, a profile published its signing keys in a root-level &lt;code&gt;signing_keys&lt;/code&gt;&lt;br&gt;
array. As of this release, &lt;code&gt;signing_keys&lt;/code&gt; is &lt;strong&gt;gone from &lt;code&gt;profile.json&lt;/code&gt; entirely&lt;/strong&gt; and &lt;code&gt;keys&lt;/code&gt;&lt;br&gt;
is the sole canonical field. The schema is blunt about it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When a profile publishes signing keys, they MUST appear here; this is where every UCP&lt;br&gt;
verifier reads them.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json-doc"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Before - v2026-04-08&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ucp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-04-08"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"signing_keys"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"kty"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"EC"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"kid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"key-1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"crv"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"P-256"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"x"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"y"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c1"&gt;// After - v2026-08-25&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ucp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-08-25"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"keys"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"kty"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"EC"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"kid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"key-1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"crv"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"P-256"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"x"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"y"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The array contents do not change. It is the same JWK Set under a different name.&lt;/p&gt;

&lt;p&gt;Now put that next to &lt;code&gt;additionalProperties: true&lt;/code&gt;. If you bump your &lt;code&gt;version&lt;/code&gt; to&lt;br&gt;
&lt;code&gt;2026-08-25&lt;/code&gt; and leave &lt;code&gt;signing_keys&lt;/code&gt; where it is, your profile is still schema-valid.&lt;br&gt;
Nothing rejects it. But no verifier looks at &lt;code&gt;signing_keys&lt;/code&gt; any more, so signature&lt;br&gt;
verification fails against a profile in which nothing looks wrong. You will be debugging&lt;br&gt;
your signing code, not your field names.&lt;/p&gt;

&lt;p&gt;The fix is a rename. The hard part is knowing you need it.&lt;/p&gt;
&lt;h2&gt;
  
  
  We got this wrong in the opposite direction
&lt;/h2&gt;

&lt;p&gt;Worth being straight about this, because it is the same trap from the other side.&lt;/p&gt;

&lt;p&gt;Our validator hard-required &lt;code&gt;signing_keys&lt;/code&gt; and had no concept of &lt;code&gt;keys&lt;/code&gt; at all. So when the&lt;br&gt;
spec moved, we started emitting a hard error on profiles that were &lt;em&gt;correctly&lt;/em&gt; migrated. A&lt;br&gt;
merchant who did the right thing got told their profile was broken.&lt;/p&gt;

&lt;p&gt;It got worse. Our pipeline skips rules and network validation whenever structural validation&lt;br&gt;
reports an error. So the bogus error did not just add noise, it suppressed every real check&lt;br&gt;
behind it. One false error, and no actual analysis.&lt;/p&gt;

&lt;p&gt;We shipped the fix on August 29. Both spellings are now accepted, &lt;code&gt;keys&lt;/code&gt; is treated as&lt;br&gt;
canonical from &lt;code&gt;2026-08-25&lt;/code&gt;, and a leftover &lt;code&gt;signing_keys&lt;/code&gt; on a new-version profile raises a&lt;br&gt;
warning that tells you it will be ignored rather than an error claiming it is invalid.&lt;/p&gt;

&lt;p&gt;The lesson generalises past our bug: &lt;strong&gt;a validator that hard-codes a field name is a&lt;br&gt;
validator that will eventually fail correct input.&lt;/strong&gt; If you have written any internal UCP&lt;br&gt;
checking, go look at how it resolves keys today.&lt;/p&gt;
&lt;h2&gt;
  
  
  Silent breaker 2: fulfillment config lost its &lt;code&gt;allows_&lt;/code&gt; prefix and changed shape
&lt;/h2&gt;

&lt;p&gt;The file was renamed from &lt;code&gt;merchant_fulfillment_config.json&lt;/code&gt; to&lt;br&gt;
&lt;code&gt;business_fulfillment_config.json&lt;/code&gt;, and two properties were renamed. But this is not just a&lt;br&gt;
rename, and that is what makes it dangerous. &lt;code&gt;multi_destination&lt;/code&gt; also changed type.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json-doc"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Before - v2026-04-08: a map of method -&amp;gt; boolean&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"allows_multi_destination"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"shipping"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"pickup"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c1"&gt;// After - v2026-08-25: an array of objects, listing = permitting&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"multi_destination"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"shipping"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now trace what happens if you skip this migration. The old schema set&lt;br&gt;
&lt;code&gt;additionalProperties: false&lt;/code&gt;, so a stray key there used to be a hard error. The new&lt;br&gt;
&lt;code&gt;business_fulfillment_config&lt;/code&gt; does not, so &lt;code&gt;allows_multi_destination&lt;/code&gt; is now simply an&lt;br&gt;
unrecognised member that nothing reads. And the new field's own rule is explicit:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Listing a method permits it; an omitted method does not.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So your untouched config does not fail. It resolves to an empty permission set. You silently&lt;br&gt;
stop allowing split shipping across addresses, and nothing anywhere says so. That is a&lt;br&gt;
capability you believe you advertise quietly turning off.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;method_combinations&lt;/code&gt; kept its array-of-arrays shape, but its &lt;code&gt;enum&lt;/code&gt; restriction to&lt;br&gt;
&lt;code&gt;["shipping", "pickup"]&lt;/code&gt; was removed in favour of an open vocabulary with those as&lt;br&gt;
well-known values. That one is a loosening, so it will not break you.&lt;/p&gt;

&lt;p&gt;While you are in these files: &lt;code&gt;fulfillment_option.description&lt;/code&gt; was upgraded from a flat&lt;br&gt;
string to a structured object (it now &lt;code&gt;$ref&lt;/code&gt;s &lt;code&gt;common/types/description.json&lt;/code&gt;). That one&lt;br&gt;
does change type on a required-ish field, so it is more likely to fail loudly, which&lt;br&gt;
honestly makes it the safer of the two.&lt;/p&gt;
&lt;h2&gt;
  
  
  Silent breaker 3: buyer consent went from booleans to a reverse-DNS map
&lt;/h2&gt;

&lt;p&gt;This one changes shape rather than just names.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json-doc"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Before - v2026-04-08: four fixed booleans&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"consent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"analytics"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"marketing"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"preferences"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sale_of_data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In &lt;code&gt;v2026-08-25&lt;/code&gt;, &lt;code&gt;consent&lt;/code&gt; is a dynamic map keyed by reverse-DNS identifiers, where each&lt;br&gt;
purpose is an object requiring &lt;code&gt;granted&lt;/code&gt;, &lt;code&gt;source&lt;/code&gt;, and &lt;code&gt;description&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json-doc"&gt;&lt;code&gt;&lt;span class="c1"&gt;// After - v2026-08-25&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"consent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"dev.ucp.consent.marketing"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"granted"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"platform"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Promotional communications across all channels"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"segments"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"dev.ucp.consent.marketing.email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"granted"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"platform"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Email marketing only"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;source&lt;/code&gt; field is the interesting addition: it distinguishes a business default&lt;br&gt;
(&lt;code&gt;"business"&lt;/code&gt;) from an explicit buyer decision captured by the platform (&lt;code&gt;"platform"&lt;/code&gt;). If&lt;br&gt;
you are handling consent for compliance reasons, that distinction is probably one you have&lt;br&gt;
been reconstructing by hand. Now it is in the protocol. Segments let you scope a decision to&lt;br&gt;
a channel without inventing your own convention, and UCP predefines&lt;br&gt;
&lt;code&gt;dev.ucp.consent.marketing.email&lt;/code&gt; and &lt;code&gt;.sms&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Silent breaker 4: payment extensions moved namespace
&lt;/h2&gt;

&lt;p&gt;Payment extensions migrated out of the shopping vertical:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;dev.ucp.shopping.split_payments&lt;/code&gt; becomes &lt;code&gt;dev.ucp.common.payment.split_payments&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;the same move applies to &lt;code&gt;payment_terms&lt;/code&gt; and &lt;code&gt;ap2_mandates&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Related, and easy to miss: shared primitive schemas (&lt;code&gt;amount.json&lt;/code&gt;, &lt;code&gt;price.json&lt;/code&gt;,&lt;br&gt;
&lt;code&gt;line_item.json&lt;/code&gt;) moved under &lt;code&gt;common/types/&lt;/code&gt;, which &lt;strong&gt;changes their &lt;code&gt;$id&lt;/code&gt; URLs&lt;/strong&gt;. If you&lt;br&gt;
pin, cache, or fetch those schemas by URL anywhere, those references are now stale.&lt;/p&gt;

&lt;p&gt;This is the change most likely to bite tooling rather than merchants. Anything that walks&lt;br&gt;
capability extension chains by prefix will read migrated extensions as orphaned.&lt;/p&gt;

&lt;h2&gt;
  
  
  The migration checklist
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Rename &lt;code&gt;signing_keys&lt;/code&gt; to &lt;code&gt;keys&lt;/code&gt; at the profile root. Delete the old key. Do not keep both -
two sources of truth for signing keys drifting apart is precisely the failure this change
was made to eliminate.&lt;/li&gt;
&lt;li&gt;Move to &lt;code&gt;business_fulfillment_config.json&lt;/code&gt;, drop the &lt;code&gt;allows_&lt;/code&gt; prefix, and convert
&lt;code&gt;multi_destination&lt;/code&gt; from a method-keyed boolean map to an array of &lt;code&gt;{ "method": "..." }&lt;/code&gt;
objects. Do not just rename it - an unconverted map reads as "nothing permitted".&lt;/li&gt;
&lt;li&gt;Convert &lt;code&gt;fulfillment_option.description&lt;/code&gt; from a flat string to the structured
&lt;code&gt;common/types/description.json&lt;/code&gt; object.&lt;/li&gt;
&lt;li&gt;Restructure buyer consent into the reverse-DNS map with &lt;code&gt;granted&lt;/code&gt; / &lt;code&gt;source&lt;/code&gt; /
&lt;code&gt;description&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Repoint payment extensions to &lt;code&gt;dev.ucp.common.payment.*&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Update any pinned &lt;code&gt;$id&lt;/code&gt; URLs for &lt;code&gt;amount&lt;/code&gt;, &lt;code&gt;price&lt;/code&gt;, and &lt;code&gt;line_item&lt;/code&gt; to &lt;code&gt;common/types/&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Bump &lt;code&gt;version&lt;/code&gt; to &lt;code&gt;2026-08-25&lt;/code&gt; &lt;strong&gt;last&lt;/strong&gt;, after the above. The version field is the part
agents read to decide how to interpret everything else.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Then re-validate. And validate with something that actually knows about &lt;code&gt;2026-08-25&lt;/code&gt; - a&lt;br&gt;
checker still targeting an older version will happily tell you a half-migrated profile is&lt;br&gt;
fine.&lt;/p&gt;

&lt;h2&gt;
  
  
  The broader point
&lt;/h2&gt;

&lt;p&gt;Date-based protocol versions make it easy to think of a spec bump as a number you increment.&lt;br&gt;
This release is a good argument against that. The changes that will cost you time are not in&lt;br&gt;
the "Breaking Changes" heading you read once. They are in the gap between "my JSON is valid"&lt;br&gt;
and "an agent can actually transact with me", and &lt;code&gt;additionalProperties: true&lt;/code&gt; is exactly&lt;br&gt;
where that gap lives.&lt;/p&gt;

&lt;p&gt;If you maintain UCP tooling, the practical takeaway is narrower: stop hard-coding field&lt;br&gt;
names, and make your validator report &lt;em&gt;which&lt;/em&gt; layout it detected rather than silently&lt;br&gt;
assuming one.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;UCP is an open standard co-developed by Google and Shopify. UCPtools is an independent&lt;br&gt;
community tool and is not affiliated with either company. Every schema detail above was read&lt;br&gt;
from the published specification at tag &lt;code&gt;v2026-08-25&lt;/code&gt; in the&lt;br&gt;
&lt;a href="https://github.com/Universal-Commerce-Protocol/ucp" rel="noopener noreferrer"&gt;Universal-Commerce-Protocol/ucp&lt;/a&gt;&lt;br&gt;
repository, not from the release notes.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;You can check a profile against &lt;code&gt;v2026-08-25&lt;/code&gt; with the free validator at&lt;br&gt;
&lt;a href="https://ucptools.dev" rel="noopener noreferrer"&gt;ucptools.dev&lt;/a&gt;, or run it in CI with the&lt;br&gt;
&lt;a href="https://github.com/Nolpak14/ucp-validate-action" rel="noopener noreferrer"&gt;UCP validate GitHub Action&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ecommerce</category>
      <category>ai</category>
      <category>api</category>
    </item>
    <item>
      <title>Is Your UCP Validator Lying to You About Cart and Catalog?</title>
      <dc:creator>Peter</dc:creator>
      <pubDate>Wed, 29 Jul 2026 10:06:16 +0000</pubDate>
      <link>https://dev.to/ucptools/is-your-ucp-validator-lying-to-you-about-cart-and-catalog-35i7</link>
      <guid>https://dev.to/ucptools/is-your-ucp-validator-lying-to-you-about-cart-and-catalog-35i7</guid>
      <description>&lt;h1&gt;
  
  
  Is Your UCP Validator Lying to You About Cart and Catalog?
&lt;/h1&gt;

&lt;p&gt;You ran validation. Your store got a "FAIL" on Cart and Catalog capabilities. The report says your catalog endpoint returns an invalid response shape. You spend an afternoon debugging endpoints that look fine when you test them manually.&lt;/p&gt;

&lt;p&gt;The problem isn't your store. It's your validator. Here's why.&lt;/p&gt;

&lt;h2&gt;
  
  
  The draft-spec trap nobody talks about
&lt;/h2&gt;

&lt;p&gt;The Universal Commerce Protocol isn't static. As of the April 2026 release:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Identity Linking&lt;/strong&gt; is stable. The spec is finalized. Agents expect a specific response shape, and that shape won't change without a new protocol version.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cart and Catalog&lt;/strong&gt; are draft. The endpoints exist and work. Agents interact with them in production. But the schema, response shapes, and exact field names are still evolving. The spec explicitly says they "MAY version independently when breaking changes are required."&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When a validator doesn't distinguish between stable and draft capabilities, here's what happens:&lt;/p&gt;

&lt;p&gt;Your Catalog endpoint returns products correctly. An AI agent asks "show me hiking boots under $200" and your store responds with actual products. But the response includes an optional metadata field that the draft spec didn't mention six months ago, or it nests variant data differently than the current draft expects.&lt;/p&gt;

&lt;p&gt;A spec-unaware validator flags this mismatch as a failure. Hard stop. Red X. "Catalog validation failed."&lt;/p&gt;

&lt;p&gt;The agent, meanwhile, browses your catalog just fine. It reads the products that matter, ignores the extra fields, and builds a cart. Your store works in practice. But your validator says it doesn't.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a hard-fail actually costs you
&lt;/h2&gt;

&lt;p&gt;False negatives aren't just annoying - they steer you toward the wrong work.&lt;/p&gt;

&lt;p&gt;When your validator says Cart validation failed, you investigate. You compare your endpoint response to the draft schema. You spend hours figuring out whether the mismatch is on your side or the spec's. You might even modify your endpoint to match a draft schema that changes next month.&lt;/p&gt;

&lt;p&gt;Meanwhile, the actual problems - signing keys that rotated, a CDN that changed how it serves &lt;code&gt;.well-known/ucp&lt;/code&gt;, payment handlers referencing an old gateway endpoint - go unnoticed. These are genuine, production-breaking failures that get buried under draft-spec noise.&lt;/p&gt;

&lt;p&gt;The validator that flags everything with equal severity trains you to ignore all of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix: draft-aware validation
&lt;/h2&gt;

&lt;p&gt;As of July 2026, UCPtools handles draft capabilities differently:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Stable spec requirements&lt;/strong&gt;: checked as before. If your Cart endpoint returns a 4xx, if your Catalog doesn't serve JSON, if a required stable-spec field is missing - those are real failures.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Draft-spec mismatches&lt;/strong&gt;: produce warnings, not failures. If your response shape doesn't match the current draft schema, you get a clear warning explaining what's different - not a red X that blocks everything else.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You still see the mismatch. You still know something changed. But you're not punished for a moving target.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three things to check in your current validation workflow
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Does your tool report spec status for each capability?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If every failure looks the same - same severity, same color, same urgency - you can't tell a broken endpoint from a draft-spec evolution.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Does a Cart/Catalog failure block your entire validation run?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A genuine &lt;code&gt;signing_keys&lt;/code&gt; failure and a draft-spec Catalog warning are not the same problem. If your tool treats them identically, you're spending time on the wrong one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. When was your tool last updated against the current UCP spec?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The April 2026 update was the largest spec release since January. If your validator hasn't been updated since then, it's validating against a version of the protocol that no longer reflects what agents expect.&lt;/p&gt;

&lt;h2&gt;
  
  
  The spec is moving. Your validation should move with it.
&lt;/h2&gt;

&lt;p&gt;Google shipped the April 2026 update with Cart, Catalog, and Signals. Shopify Editions Spring '26 made UCP the default on every store. The protocol is in active development - and that's a good thing. It means agentic commerce is real enough that the spec keeps improving.&lt;/p&gt;

&lt;p&gt;But point-in-time validation tools that don't distinguish between draft and stable create more noise than signal. You end up debugging spec evolution instead of fixing what actually breaks agent interactions.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Check if your validation handles draft specs correctly&lt;/strong&gt;: &lt;a href="https://ucptools.dev?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202607" rel="noopener noreferrer"&gt;Validate your UCP profile&lt;/a&gt; - free, no account needed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Running validation across multiple domains?&lt;/strong&gt; &lt;a href="https://ucptools.dev/signup?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202607" rel="noopener noreferrer"&gt;Start a free 7-day trial&lt;/a&gt; for monitoring and multi-domain dashboards. No credit card required.&lt;/p&gt;

</description>
      <category>ucp</category>
      <category>validation</category>
      <category>agenticcommerce</category>
      <category>webdev</category>
    </item>
    <item>
      <title>ChatGPT Can Shop Now. Is Your Store Ready to Sell to It?</title>
      <dc:creator>Peter</dc:creator>
      <pubDate>Mon, 20 Jul 2026 10:05:06 +0000</pubDate>
      <link>https://dev.to/ucptools/chatgpt-can-shop-now-is-your-store-ready-to-sell-to-it-17o4</link>
      <guid>https://dev.to/ucptools/chatgpt-can-shop-now-is-your-store-ready-to-sell-to-it-17o4</guid>
      <description>&lt;h1&gt;
  
  
  ChatGPT Can Shop Now. Is Your Store Ready to Sell to It?
&lt;/h1&gt;

&lt;p&gt;OpenAI shipped the Agentic Commerce Protocol (ACP) and ChatGPT's shopping agent is live. Target, Sephora, Nordstrom, Best Buy, and Home Depot are already integrated. When a user asks ChatGPT to find them a product, those stores show up.&lt;/p&gt;

&lt;p&gt;If your store isn't ACP-visible, ChatGPT users can't find you. Period.&lt;/p&gt;

&lt;p&gt;Here's the thing most e-commerce developers don't realize: your store might already be ACP-compatible and you just haven't checked. If you're on Shopify with Stripe, HTTPS, and proper policy pages, you could be one product feed away from ChatGPT discoverability.&lt;/p&gt;

&lt;h2&gt;
  
  
  What ACP Actually Checks
&lt;/h2&gt;

&lt;p&gt;ACP is the open standard (Apache 2.0) that OpenAI and Stripe built for AI commerce. It's not proprietary and it's not Stripe-only - Adyen shipped "Adyen Agentic" and PayPal announced ACP support for 2026.&lt;/p&gt;

&lt;p&gt;The protocol checks four things on your store:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Platform&lt;/strong&gt; (25 pts) - Shopify/Etsy auto-eligible path, or custom integration needed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Payment infrastructure&lt;/strong&gt; (30 pts) - Stripe is the easiest path (Shared Payment Token), but seller-backed instruments (gift cards, store credit) also work&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Policy pages&lt;/strong&gt; (25 pts) - Terms, privacy, and return policy are required by spec. Agents need these.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security&lt;/strong&gt; (20 pts) - HTTPS with modern TLS. Hard requirement.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You get a 0-100 score with letter grades (A through F) and finding codes for every failure - not "you failed," but exactly what to fix.&lt;/p&gt;

&lt;h2&gt;
  
  
  ACP vs UCP: Don't Confuse Them
&lt;/h2&gt;

&lt;p&gt;Two protocols, two AI agent ecosystems:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Protocol&lt;/th&gt;
&lt;th&gt;ACP (OpenAI + Stripe)&lt;/th&gt;
&lt;th&gt;UCP (Google + Shopify)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Primary agents&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;ChatGPT&lt;/td&gt;
&lt;td&gt;Google AI Mode, Gemini, Copilot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Discovery&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;/.well-known/acp.json&lt;/code&gt; + product feed&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;/.well-known/ucp&lt;/code&gt; manifest&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Checkout&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Stripe delegated tokens or merchant app&lt;/td&gt;
&lt;td&gt;Merchant-hosted endpoints&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Focus&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Payment + discovery&lt;/td&gt;
&lt;td&gt;Full commerce (browse, cart, checkout)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Microsoft Copilot already consumes both. checkout.com data shows dual-protocol stores capture 40% more agentic traffic. The recommendation for 2026: implement both if you can. But start by checking what you already have.&lt;/p&gt;

&lt;h2&gt;
  
  
  The March 2026 Pivot (And Why ACP Matters More, Not Less)
&lt;/h2&gt;

&lt;p&gt;OpenAI deprioritized in-ChatGPT checkout in March 2026. Users browsed but didn't buy in-chat. Purchases moved to merchant storefronts.&lt;/p&gt;

&lt;p&gt;But here's what most people missed: the ACP spec had its &lt;em&gt;biggest release&lt;/em&gt; after that pivot. April 17, 2026 brought public product feeds, Cart capability, delegate authentication with 3DS2, and mandatory idempotency keys.&lt;/p&gt;

&lt;p&gt;The takeaway: ACP now governs &lt;em&gt;discovery&lt;/em&gt; inside ChatGPT. If your products aren't ACP-surfaced, ChatGPT users never see them. The purchase completes on your site, but the discovery happens inside the agent. And discovery requires ACP readiness.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Numbers
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;McKinsey: up to $1T in US agentic commerce by 2030&lt;/li&gt;
&lt;li&gt;Morgan Stanley: 126M AI shopping-agent users by 2030&lt;/li&gt;
&lt;li&gt;Shopify: 13x YoY growth in AI search orders&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The retailers showing up today didn't wait. They integrated early. Every week your store isn't checked is a week ChatGPT users buy from someone else.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test It in 30 Seconds
&lt;/h2&gt;

&lt;p&gt;I built a free ACP Readiness Checker that scans your domain and gives you a 0-100 score across all four categories. No account required.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ucptools.dev/acp-checker?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=acp-checker-promo-202607" rel="noopener noreferrer"&gt;Run the ACP check on your store →&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you score A, you're ChatGPT-ready. If not, you get specific finding codes and fix recommendations for every failing category.&lt;/p&gt;

&lt;p&gt;Your products should show up wherever customers shop. In 2026, that includes ChatGPT.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Also published on &lt;a href="https://ucptools.dev/blog/acp-checker-chatgpt-readiness?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202607" rel="noopener noreferrer"&gt;ucptools.dev&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>acp</category>
      <category>agenticcommerce</category>
      <category>chatgpt</category>
      <category>ecommerce</category>
    </item>
    <item>
      <title>Run UCP + ACP Validation in Your CI/CD Pipeline</title>
      <dc:creator>Peter</dc:creator>
      <pubDate>Fri, 10 Jul 2026 05:26:39 +0000</pubDate>
      <link>https://dev.to/ucptools/run-ucp-acp-validation-in-your-cicd-pipeline-590d</link>
      <guid>https://dev.to/ucptools/run-ucp-acp-validation-in-your-cicd-pipeline-590d</guid>
      <description>&lt;p&gt;You deploy a change to your storefront. The JSON still parses. The linter is happy. Tests pass.&lt;/p&gt;

&lt;p&gt;Two weeks later you notice AI agents have been silently skipping your store, because a capability schema endpoint started returning a 404 and nothing anywhere told you.&lt;/p&gt;

&lt;p&gt;This is the failure mode of agentic commerce readiness: it degrades quietly. There is no 500 page, no error rate spike, no angry customer email. Agents just move on to a merchant whose profile resolves.&lt;/p&gt;

&lt;p&gt;The fix is the same as for every other class of quality problem in software. Stop checking it by hand. Put it in CI.&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually breaks
&lt;/h2&gt;

&lt;p&gt;UCP profiles are not simple documents. They carry namespace bindings, HTTPS requirements, signing keys, and extension chains that reference capabilities elsewhere in the file. A profile can be perfectly valid JSON and still be broken in ways that matter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;UCP_NS_ORIGIN_MISMATCH&lt;/code&gt; - your declared namespace does not match the origin serving it&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;UCP_ENDPOINT_NOT_HTTPS&lt;/code&gt; - agents require HTTPS, without exception&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;UCP_MISSING_SIGNING_KEYS&lt;/code&gt; - required before order and fulfillment capabilities will be trusted&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;UCP_SCHEMA_FETCH_FAILED&lt;/code&gt; - a capability schema URL returns an error&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;UCP_ORPHANED_EXTENSION&lt;/code&gt; - an extension points at a capability that no longer exists&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every one of these survives &lt;code&gt;JSON.parse()&lt;/code&gt;. Every one of these makes you invisible to some agent. And the last two are exactly the kind of thing a refactor breaks without touching the profile file at all, because the breakage lives at the other end of a URL.&lt;/p&gt;

&lt;p&gt;That is the argument for CI. Not "validate once at launch" - validate on every push, against the environment you are about to ship.&lt;/p&gt;

&lt;h2&gt;
  
  
  The GitHub Action
&lt;/h2&gt;

&lt;p&gt;If you are on GitHub, this is four lines of YAML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Nolpak14/ucp-validate-action@v1&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;domain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;yourstore.com'&lt;/span&gt;
    &lt;span class="na"&gt;fail-on-grade&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;C'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The action runs four-level validation (structural, rules, network, SDK compliance) against the live domain and scores AI readiness. &lt;code&gt;fail-on-grade&lt;/code&gt; fails the build at that grade or worse; &lt;code&gt;fail-on-score&lt;/code&gt; does the same against a 0-100 number. Use whichever matches how your team talks about quality.&lt;/p&gt;

&lt;p&gt;It exposes &lt;code&gt;score&lt;/code&gt;, &lt;code&gt;grade&lt;/code&gt;, &lt;code&gt;ucp-found&lt;/code&gt;, &lt;code&gt;passed&lt;/code&gt;, and &lt;code&gt;result-json&lt;/code&gt; as outputs, so you can branch on the result rather than just pass or fail:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Nolpak14/ucp-validate-action@v1&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ucp&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;domain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;yourstore.com'&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;echo "UCP score&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ steps.ucp.outputs.score }}"&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;steps.ucp.outputs.ucp-found == 'false'&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;echo "::warning::No UCP profile at /.well-known/ucp"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On pull requests it posts a comment with the score, a category breakdown, and any validation issues with their error codes, updating in place rather than piling up duplicates. Set &lt;code&gt;comment: 'false'&lt;/code&gt; if you would rather have step summaries only.&lt;/p&gt;

&lt;h2&gt;
  
  
  The pattern that actually pays: gate the deploy
&lt;/h2&gt;

&lt;p&gt;Validating on push is useful. Gating the deploy on it is what stops a broken profile reaching production:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;validate-ucp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Nolpak14/ucp-validate-action@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;domain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;staging.yourstore.com'&lt;/span&gt;
          &lt;span class="na"&gt;fail-on-grade&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;C'&lt;/span&gt;

  &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;validate-ucp&lt;/span&gt;   &lt;span class="c1"&gt;# deploy only runs if validation passed&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;echo "Deploying to production..."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Point it at staging. If staging's profile does not clear your bar, the &lt;code&gt;deploy&lt;/code&gt; job never runs. You catch the regression before customers see it, and before agents quietly reroute around you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Not on GitHub? Use the CLI
&lt;/h2&gt;

&lt;p&gt;The validator ships as an npm package, so it drops into any runner with Node.js 20+. It exits non-zero when validation fails, which is all a CI system needs.&lt;/p&gt;

&lt;p&gt;GitLab CI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;ucp-validation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node:20-alpine&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;npx -p @ucptools/validator ucp-validate validate --remote yourstore.com&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$CI_COMMIT_BRANCH == "main"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;CircleCI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;ucp-validation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;docker&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cimg/node:20.11&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Validate UCP profile&lt;/span&gt;
          &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npx -p @ucptools/validator ucp-validate validate --remote yourstore.com&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or wire it into your test suite so it runs wherever your tests already run - Jenkins, Bitbucket Pipelines, a Makefile, a pre-commit hook:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save-dev&lt;/span&gt; @ucptools/validator
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"test:ucp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ucp-validate validate --remote yourstore.com"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also validate the file before it ever ships, which is faster and catches structural errors without a network round trip:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx &lt;span class="nt"&gt;-p&lt;/span&gt; @ucptools/validator ucp-validate validate &lt;span class="nt"&gt;--file&lt;/span&gt; ./public/.well-known/ucp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The other protocol: ACP readiness
&lt;/h2&gt;

&lt;p&gt;UCP is not the only spec agents care about. The Agentic Commerce Protocol - OpenAI and Stripe's standard, the one behind checkout inside ChatGPT - has its own readiness surface: platform detection, payment integration, policy endpoints, security posture.&lt;/p&gt;

&lt;p&gt;Worth being precise about how this runs, because it is different: &lt;strong&gt;the GitHub Action and the CLI check UCP.&lt;/strong&gt; ACP readiness is a public REST endpoint. No key, no install, so it works in any CI that has &lt;code&gt;curl&lt;/code&gt; and &lt;code&gt;jq&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-sX&lt;/span&gt; POST https://ucptools.dev/api/acp-check &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"domain":"yourstore.com"}'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; acp.json

&lt;span class="nv"&gt;SCORE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.score'&lt;/span&gt; acp.json&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;GRADE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.grade'&lt;/span&gt; acp.json&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ACP readiness: &lt;/span&gt;&lt;span class="nv"&gt;$GRADE&lt;/span&gt;&lt;span class="s2"&gt; (&lt;/span&gt;&lt;span class="nv"&gt;$SCORE&lt;/span&gt;&lt;span class="s2"&gt;/100)"&lt;/span&gt;

&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SCORE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-ge&lt;/span&gt; 70 &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.recommendations[]? | "[\(.priority)] \(.title) - \(.action)"'&lt;/span&gt; acp.json
  &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One trap worth knowing: the endpoint answers &lt;code&gt;200 OK&lt;/code&gt; even when a domain fails, with &lt;code&gt;"ok": false&lt;/code&gt; in the body. A reflexive &lt;code&gt;curl --fail&lt;/code&gt; gate would pass every grade, including an F. Read &lt;code&gt;.score&lt;/code&gt; or &lt;code&gt;.grade&lt;/code&gt; from the response, not the HTTP status.&lt;/p&gt;

&lt;p&gt;The response also carries &lt;code&gt;score_breakdown&lt;/code&gt; across platform, payment, policies, and security, plus &lt;code&gt;recommendations&lt;/code&gt; as structured objects with &lt;code&gt;priority&lt;/code&gt;, &lt;code&gt;title&lt;/code&gt;, &lt;code&gt;description&lt;/code&gt;, and &lt;code&gt;action&lt;/code&gt; - which is why the snippet above prints something a developer can act on rather than a wall of JSON.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to set the bar
&lt;/h2&gt;

&lt;p&gt;Resist the urge to start at &lt;code&gt;fail-on-grade: 'A'&lt;/code&gt;. You will spend a week fighting your own pipeline and then disable the check, which is worse than never adding it.&lt;/p&gt;

&lt;p&gt;Start where you are. Run the validator once, take the grade it gives you, and set the threshold one notch below. Now the build fails only on regression, which is the thing you actually care about. Ratchet the bar upward as you fix the backlog. A gate that fires on real regressions and stays quiet otherwise is a gate people keep.&lt;/p&gt;

&lt;p&gt;For ACP, 70 is a reasonable opening threshold. For UCP, &lt;code&gt;fail-on-grade: 'C'&lt;/code&gt; catches the profile-level breakage without blocking on Schema.org polish.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Add the action, or the &lt;code&gt;npx&lt;/code&gt; one-liner, to a workflow you already run&lt;/li&gt;
&lt;li&gt;Point it at staging, not production&lt;/li&gt;
&lt;li&gt;Set a threshold one notch below your current grade&lt;/li&gt;
&lt;li&gt;Add the ACP curl step if you care about ChatGPT checkout&lt;/li&gt;
&lt;li&gt;Make the deploy job &lt;code&gt;needs:&lt;/code&gt; the validation job&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Copy-paste recipes for GitHub Actions, GitLab CI, CircleCI, npx, and the ACP endpoint live at &lt;a href="https://ucptools.dev/ci" rel="noopener noreferrer"&gt;ucptools.dev/ci&lt;/a&gt;. If you would rather call validation from your own code than from a CI step, the &lt;a href="https://ucptools.dev/api-docs" rel="noopener noreferrer"&gt;API and npm reference&lt;/a&gt; covers the public REST endpoints and the typed package.&lt;/p&gt;

&lt;p&gt;The point is not the score. The point is that a profile which silently stopped resolving should break your build, not your revenue.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;UCPtools is an independent community tool. UCP is an open standard by Google and Shopify; ACP is an open standard by OpenAI and Stripe. We are not affiliated with any of them.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>githubactions</category>
      <category>ecommerce</category>
      <category>webdev</category>
    </item>
    <item>
      <title>The Agentic Commerce Stack Is Complete - Here's Your July 2026 Implementation Checklist</title>
      <dc:creator>Peter</dc:creator>
      <pubDate>Mon, 06 Jul 2026 10:07:25 +0000</pubDate>
      <link>https://dev.to/ucptools/the-agentic-commerce-stack-is-complete-heres-your-july-2026-implementation-checklist-1f3l</link>
      <guid>https://dev.to/ucptools/the-agentic-commerce-stack-is-complete-heres-your-july-2026-implementation-checklist-1f3l</guid>
      <description>&lt;h1&gt;
  
  
  The Agentic Commerce Stack Is Complete - Here's Your July 2026 Implementation Checklist
&lt;/h1&gt;

&lt;p&gt;Three infrastructure changes in the last 30 days turned agentic commerce from a spec to a live channel:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Mastercard backed UCP.&lt;/strong&gt; Their Agent Pay network now runs on Google's Universal Commerce Protocol. The payment layer converged on the discovery layer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shopify shipped Agentic Storefronts in Summer '26 Editions.&lt;/strong&gt; Every Shopify merchant now has an AI shopping channel, active by default, manageable from the admin.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google AI Mode is in production.&lt;/strong&gt; Gemini browses stores via UCP. ChatGPT shops through Shopify's agentic storefronts.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The stack is live: &lt;strong&gt;Discovery&lt;/strong&gt; (Google AI Mode, Gemini, ChatGPT) + &lt;strong&gt;Storefront&lt;/strong&gt; (Shopify, BigCommerce) + &lt;strong&gt;Payment&lt;/strong&gt; (Mastercard Agent Pay, Visa Intelligent Commerce, Adyen Agentic).&lt;/p&gt;

&lt;p&gt;If your store doesn't have a valid UCP profile, you are invisible to every layer. Not "less visible." Invisible. The agents read protocols, not websites.&lt;/p&gt;

&lt;p&gt;Here's the 5-item checklist to fix that. Each step takes 10-30 minutes.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Validate at All 4 Levels
&lt;/h2&gt;

&lt;p&gt;A structural JSON check tells you the manifest parses. It doesn't tell you whether an AI agent can actually shop.&lt;/p&gt;

&lt;p&gt;You need all four:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Structural&lt;/strong&gt; - valid JSON, required fields, schema compliance&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rules&lt;/strong&gt; - capability consistency, namespace/origin match, business logic&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Network&lt;/strong&gt; - HTTPS on all endpoints, live response codes, reachability&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SDK/Simulation&lt;/strong&gt; - end-to-end agent interaction (browse, cart, checkout, pay)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most free checkers stop at Level 1. The failures that make your store invisible live at Levels 3 and 4.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ucptools.dev/validator?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=stack-checklist-202607" rel="noopener noreferrer"&gt;Run a free 4-level validation&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Run the AI Agent Simulator
&lt;/h2&gt;

&lt;p&gt;Validation says your manifest is correct. Simulation says whether an agent can actually use it.&lt;/p&gt;

&lt;p&gt;The simulator sends real agent-style requests: browse catalog, add to cart, initiate checkout. Failures it catches that validators miss:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Payment handlers declared but unreachable (302 redirect, CORS block)&lt;/li&gt;
&lt;li&gt;Identity Linking endpoints that return 200 but fail cross-domain&lt;/li&gt;
&lt;li&gt;Cart where add works but remove doesn't&lt;/li&gt;
&lt;li&gt;Signing keys present but cryptographically invalid&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://ucptools.dev/tools/simulator?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=stack-checklist-202607" rel="noopener noreferrer"&gt;Run the AI agent simulator&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Fix the Top 3 Failures
&lt;/h2&gt;

&lt;p&gt;Production scan data shows three issues cause most agent-visible failures:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Missing &lt;code&gt;signing_keys&lt;/code&gt;&lt;/strong&gt; - Without them, agents can't verify your manifest. They walk. This is the single most common failure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Namespace/origin mismatch&lt;/strong&gt; - Your manifest says &lt;code&gt;origin: "store.example.com"&lt;/code&gt; but the agent requested from &lt;code&gt;www.store.example.com&lt;/code&gt;. The agent sees a contradiction and aborts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Broken &lt;code&gt;payment_handlers&lt;/code&gt;&lt;/strong&gt; - The manifest declares checkout but doesn't tell agents what payment methods exist, or lists handlers that don't work. Cart fills, purchase fails.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Set Up Monitoring
&lt;/h2&gt;

&lt;p&gt;Your UCP profile will break. Not because you made a mistake - because infrastructure changes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;TLS certs rotate and the new CA isn't trusted&lt;/li&gt;
&lt;li&gt;Your platform ships an update that changes the manifest shape&lt;/li&gt;
&lt;li&gt;Payment gateway config drifts from what the manifest declares&lt;/li&gt;
&lt;li&gt;A caching plugin starts serving &lt;code&gt;/.well-known/ucp&lt;/code&gt; as &lt;code&gt;text/html&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When this happens, you won't get an error. You'll just stop appearing in AI shopping results. Silent, invisible failure.&lt;/p&gt;

&lt;p&gt;Monitoring re-validates on a schedule, compares to baseline, and alerts on regression. Same principle as uptime monitoring - but for AI agent discoverability.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ucptools.dev/tools/monitoring?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=stack-checklist-202607" rel="noopener noreferrer"&gt;Set up monitoring&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Test All Three Layers
&lt;/h2&gt;

&lt;p&gt;The stack works as a chain. Break any link and it fails:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Discovery&lt;/strong&gt; - Can Google AI Mode find your store? Search your products in AI Mode.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Storefront&lt;/strong&gt; - Is your Shopify Agentic Storefront active? Check admin under Sales Channels.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Payment&lt;/strong&gt; - Can Mastercard Agent Pay process a payment through your store? Test the handler endpoints.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;Shopify reported 13x year-over-year growth in AI search orders &lt;em&gt;before&lt;/em&gt; Agentic Storefronts shipped. Cloudflare reports bots have surpassed human traffic. McKinsey projects up to $1T in US agentic commerce by 2030.&lt;/p&gt;

&lt;p&gt;The protocol war is effectively settled. UCP has the discovery layer, the storefront layer, and now the payment layer. Implementing it isn't a competitive advantage - it's table stakes.&lt;/p&gt;

&lt;p&gt;Five steps. One afternoon. The difference between being visible to the agentic commerce stack and being invisible to it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ucptools.dev?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=stack-checklist-202607" rel="noopener noreferrer"&gt;Start your free validation&lt;/a&gt; - no account required.&lt;/p&gt;

</description>
      <category>ucp</category>
      <category>ecommerce</category>
      <category>webdev</category>
      <category>ai</category>
    </item>
    <item>
      <title>Building a Dual-Protocol Checkout with Agorio SDK: Support UCP and ACP in Your AI Commerce Agent</title>
      <dc:creator>Peter</dc:creator>
      <pubDate>Fri, 26 Jun 2026 15:53:12 +0000</pubDate>
      <link>https://dev.to/ucptools/building-a-dual-protocol-checkout-with-agorio-sdk-support-ucp-and-acp-in-your-ai-commerce-agent-3ob7</link>
      <guid>https://dev.to/ucptools/building-a-dual-protocol-checkout-with-agorio-sdk-support-ucp-and-acp-in-your-ai-commerce-agent-3ob7</guid>
      <description>&lt;h1&gt;
  
  
  Building a Dual-Protocol Checkout with Agorio SDK: Support UCP and ACP in Your AI Commerce Agent
&lt;/h1&gt;

&lt;p&gt;The AI commerce landscape is rapidly standardizing around two open protocols: &lt;strong&gt;Google's Universal Commerce Protocol (UCP)&lt;/strong&gt; and &lt;strong&gt;OpenAI's Agentic Commerce Protocol (ACP)&lt;/strong&gt;. By holiday 2026, Visa and Mastercard will enable all US cardholders for agent transactions, while Shopify's 4.8M merchants are discoverable via UCP and Stripe's 1.5M merchants can accept ACP payments with one line of code.&lt;/p&gt;

&lt;p&gt;For developers building AI shopping agents, this creates a critical challenge: &lt;strong&gt;should you implement UCP, ACP, or both?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In this step-by-step tutorial, you'll learn how to build a dual-protocol checkout system using the &lt;a href="https://agorio.dev?utm_source=devto&amp;amp;utm_medium=tutorial&amp;amp;utm_campaign=202606" rel="noopener noreferrer"&gt;Agorio SDK&lt;/a&gt; — the only open-source toolkit that supports both UCP and ACP with auto-detection. We'll walk through setting up a TypeScript project, discovering merchants via UCP, managing checkout sessions via ACP, and assembling a full-featured shopping agent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Dual-Protocol Support Matters
&lt;/h2&gt;

&lt;p&gt;UCP and ACP solve different problems:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Protocol&lt;/th&gt;
&lt;th&gt;Lead Developers&lt;/th&gt;
&lt;th&gt;Primary Use Case&lt;/th&gt;
&lt;th&gt;Discovery Method&lt;/th&gt;
&lt;th&gt;Payment Model&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;UCP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Google, Shopify, 25+ partners&lt;/td&gt;
&lt;td&gt;Full commerce lifecycle (discovery, cart, checkout, order tracking)&lt;/td&gt;
&lt;td&gt;Decentralized (&lt;code&gt;/.well-known/ucp&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Multiple handlers (Google Pay, Shop Pay, tokenization)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ACP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;OpenAI, Stripe&lt;/td&gt;
&lt;td&gt;Checkout-focused, delegated payment&lt;/td&gt;
&lt;td&gt;Centralized (merchant application)&lt;/td&gt;
&lt;td&gt;Any PSP via delegated payment (Stripe first)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Merchants are adopting both. If your agent only supports one protocol, you'll miss transactions on the other. The smart solution: &lt;strong&gt;support both, but avoid writing twice the code&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Agorio Provides
&lt;/h2&gt;

&lt;p&gt;Agorio abstracts the protocol differences behind a unified API:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;UcpClient&lt;/code&gt;&lt;/strong&gt; – Discovers merchants via &lt;code&gt;/.well-known/ucp&lt;/code&gt;, normalizes capability formats, handles REST/MCP/A2A transports.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;AcpClient&lt;/code&gt;&lt;/strong&gt; – Manages ACP checkout sessions (create, get, update, complete, cancel) with Bearer auth and request tracing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ShoppingAgent&lt;/code&gt;&lt;/strong&gt; – 17 built‑in tools that work across both protocols via automatic detection.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;MockMerchant&lt;/code&gt;&lt;/strong&gt; – A full UCP‑compliant test server, plus ACP‑only and MCP‑only variants.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LLM adapters&lt;/strong&gt; – Gemini, Claude, OpenAI, Ollama — swap without changing your agent logic.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With Agorio, you write commerce logic once; the SDK adapts to the merchant's supported protocol.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Project Setup
&lt;/h2&gt;

&lt;p&gt;Start a new TypeScript project and install Agorio:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm init &lt;span class="nt"&gt;-y&lt;/span&gt;
npm &lt;span class="nb"&gt;install &lt;/span&gt;typescript ts-node @types/node &lt;span class="nt"&gt;--save-dev&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; @agorio/sdk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a &lt;code&gt;tsconfig.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"compilerOptions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ES2022"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"module"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"NodeNext"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"moduleResolution"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"NodeNext"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"esModuleInterop"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"outDir"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./dist"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"strict"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2: Discover a Merchant via UCP
&lt;/h2&gt;

&lt;p&gt;First, let's discover a merchant's capabilities using the &lt;code&gt;UcpClient&lt;/code&gt;. The client automatically fetches the &lt;code&gt;/.well-known/ucp&lt;/code&gt; manifest and normalizes the capability format.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;UcpClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@agorio/sdk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;UcpClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;discoverMerchant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;discover&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Merchant &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; supports:`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;capabilities&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cap&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`  - &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;cap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; (&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;cap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Discovery failed:`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Example: discover a UCP-enabled merchant&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;discoverMerchant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;shop.example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;result&lt;/code&gt; object contains normalized services, payment handlers, and transport bindings. You can now invoke any UCP capability (catalog, cart, checkout) using the client's REST or MCP transport.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Create an ACP Checkout Session
&lt;/h2&gt;

&lt;p&gt;For merchants that support ACP (like Stripe‑enabled stores), you'll manage checkout sessions via the &lt;code&gt;AcpClient&lt;/code&gt;. ACP uses delegated payment: the user's payment method stays with their PSP (Stripe, PayPal), and the agent acts as an operator.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AcpClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@agorio/sdk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;acp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AcpClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.stripe.com/acp/v1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Replace with merchant's ACP endpoint&lt;/span&gt;
  &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;STRIPE_API_KEY&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createAcpCheckout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;acp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createCheckout&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;lineItems&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
      &lt;span class="nx"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2999&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// $29.99 in minor units (cents)&lt;/span&gt;
        &lt;span class="na"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;USD&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}],&lt;/span&gt;
    &lt;span class="na"&gt;merchantDomain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;shop.example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`ACP checkout session &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; created`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Status: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// "not_ready_for_payment"&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createAcpCheckout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;prod_123&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;SB_1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;AcpClient&lt;/code&gt; handles all required headers (Authorization, API‑Version, Idempotency‑Key, Request‑Id) and validates responses against the ACP schema.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Build a Dual-Protocol Shopping Agent
&lt;/h2&gt;

&lt;p&gt;Now combine both clients into a single &lt;code&gt;ShoppingAgent&lt;/code&gt;. The agent automatically detects whether a merchant speaks UCP or ACP and uses the appropriate client.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ShoppingAgent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;GeminiAdapter&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@agorio/sdk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ShoppingAgent&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;llm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GeminiAdapter&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GEMINI_API_KEY&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="na"&gt;verbose&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;// ShoppingAgent automatically instantiates UcpClient and AcpClient&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;runShoppingTask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;           &lt;span class="c1"&gt;// Natural language summary&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;checkout&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Order ID if purchase completed&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;totalTokens&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// LLM token usage&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// This single command works for both UCP and ACP merchants:&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;runShoppingTask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Go to shop.example.com and buy wireless headphones&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent's 17 built‑in tools handle the entire flow: merchant discovery, product search, adding to cart, submitting shipping and payment, and order tracking. You can extend with custom tools via plugins.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Test with Mock Merchants
&lt;/h2&gt;

&lt;p&gt;Before hitting real merchants, test with Agorio's mock servers. &lt;code&gt;MockMerchant&lt;/code&gt; provides a full UCP‑compliant server; &lt;code&gt;MockAcpMerchant&lt;/code&gt; provides an ACP‑only server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MockMerchant&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;MockAcpMerchant&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@agorio/sdk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Start a UCP merchant&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ucpMerchant&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MockMerchant&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ucpMerchant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`UCP mock merchant running at &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ucpMerchant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Start an ACP merchant&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;acpMerchant&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MockAcpMerchant&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;acpMerchant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`ACP mock merchant running at &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;acpMerchant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Test your agent against the mock&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Go to &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ucpMerchant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; and buy a laptop`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Order placed:`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;checkout&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Clean up&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ucpMerchant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;acpMerchant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both mocks simulate realistic latency, support configurable error rates for chaos testing, and provide structured product catalogs (10 products by default).&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: Deployment Considerations
&lt;/h2&gt;

&lt;p&gt;When moving to production, keep these points in mind:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;LLM cost control&lt;/strong&gt; – Set &lt;code&gt;maxIterations&lt;/code&gt; in &lt;code&gt;AgentOptions&lt;/code&gt; to limit agent loops.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Observability&lt;/strong&gt; – Use the &lt;code&gt;onLog&lt;/code&gt; callback to stream logs to your monitoring system, or integrate with Agorio Cloud for full tracing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Session storage&lt;/strong&gt; – Swap &lt;code&gt;MemorySessionStorage&lt;/code&gt; for &lt;code&gt;FileSessionStorage&lt;/code&gt; or &lt;code&gt;@agorio/session-redis&lt;/code&gt; in multi‑process deployments.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Idempotency&lt;/strong&gt; – Always pass &lt;code&gt;idempotencyKey&lt;/code&gt; on &lt;code&gt;completeCheckout&lt;/code&gt; to prevent duplicate charges.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compliance&lt;/strong&gt; – If you handle EU users, enable the built‑in EU AI Act compliance export (&lt;code&gt;GET /api/compliance/export&lt;/code&gt;).&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusion: Why Agorio is the Right Choice
&lt;/h2&gt;

&lt;p&gt;Building dual‑protocol AI commerce agents from scratch requires deep knowledge of both UCP and ACP specs, plus months of implementation work. Agorio gives you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Protocol agnosticism&lt;/strong&gt; – Write once, run on UCP, ACP, or both.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Production‑ready tooling&lt;/strong&gt; – Mock merchants, LLM adapters, observability, CLI.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enterprise hardening&lt;/strong&gt; – RBAC, audit logs, compliance exports, security posture.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The SDK is open‑source (MIT), has zero runtime fees, and already powers agents handling real purchases.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Get started today:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;npm&lt;/strong&gt;: &lt;code&gt;npm install @agorio/sdk&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/Nolpak14/agorio?utm_source=devto&amp;amp;utm_medium=tutorial&amp;amp;utm_campaign=202606" rel="noopener noreferrer"&gt;github.com/Nolpak14/agorio&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docs&lt;/strong&gt;: &lt;a href="https://agorio.dev/docs?utm_source=devto&amp;amp;utm_medium=tutorial&amp;amp;utm_campaign=202606" rel="noopener noreferrer"&gt;agorio.dev/docs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Playground&lt;/strong&gt;: &lt;a href="https://agorio.dev/playground?utm_source=devto&amp;amp;utm_medium=tutorial&amp;amp;utm_campaign=202606" rel="noopener noreferrer"&gt;agorio.dev/playground&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're building AI commerce agents, join the 10+ GitHub stars and 46+ weekly npm downloaders who've chosen Agorio as their foundation. Ship your agent in 20 lines, not 20 weeks.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>typescript</category>
      <category>commerce</category>
      <category>ecommerce</category>
    </item>
    <item>
      <title>We Tracked Every AI Agent That Visited Our Site. 5 Different Agents Found Us This Month.</title>
      <dc:creator>Peter</dc:creator>
      <pubDate>Mon, 22 Jun 2026 10:04:50 +0000</pubDate>
      <link>https://dev.to/ucptools/we-tracked-every-ai-agent-that-visited-our-site-5-different-agents-found-us-this-month-544d</link>
      <guid>https://dev.to/ucptools/we-tracked-every-ai-agent-that-visited-our-site-5-different-agents-found-us-this-month-544d</guid>
      <description>&lt;h1&gt;
  
  
  We Tracked Every AI Agent That Visited Our Site. 5 Different Agents Found Us This Month.
&lt;/h1&gt;

&lt;p&gt;Three weeks ago, I noticed something strange in our referrer logs.&lt;/p&gt;

&lt;p&gt;A user from &lt;code&gt;chatgpt.com&lt;/code&gt; had landed on our validator page, browsed our documentation, and — crucially — triggered tool events. Not a human clicking around. An AI agent. ChatGPT had crawled our site, loaded our tool, and used it.&lt;/p&gt;

&lt;p&gt;That was one agent. One week. A curiosity.&lt;/p&gt;

&lt;p&gt;This week: &lt;strong&gt;five.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What showed up (and what they did)
&lt;/h2&gt;

&lt;p&gt;The full lineup of AI agents that visited ucptools.dev in the last month:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ChatGPT (chatgpt.com)&lt;/strong&gt; — 4 visits this week alone. Most active of the bunch. Browsed documentation pages and the free UCP validator. Triggered actual tool events — someone asked ChatGPT about UCP validation, and it came to us to answer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gemini (gemini.google.com)&lt;/strong&gt; — 1 visit this week. First time we've seen Gemini in our logs. Shorter session than ChatGPT, but it hit the same pages: validator, compare page, docs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Perplexity (perplexity.ai)&lt;/strong&gt; — First spotted in May. Here's the wild part: every Perplexity-referred session in May resulted in a tool user. 100% conversion rate. Someone asked Perplexity "how do I validate my UCP profile?" and Perplexity sent them straight to our tool, already knowing what to do.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Claude (claude.ai)&lt;/strong&gt; — Returned June 14 after a multi-week gap. Browsed our UCP protocol documentation and the agent simulator page. Not using tools yet — but it's building a map.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NotebookLM (notebooklm.google.com)&lt;/strong&gt; — Newcomer. Discovered us June 7. Google's research assistant found our site and ingested content. We don't know what question triggered it, but something in our content matched a user's research query.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Trend line: 1 → 1 → 5.&lt;/strong&gt; Not a spike. A ramp.&lt;/p&gt;




&lt;h2&gt;
  
  
  This isn't about SEO as you know it
&lt;/h2&gt;

&lt;p&gt;If you work in developer tools or e-commerce infrastructure, you've probably spent the last 6 months hearing about UCP, ACP, AI shopping agents, and "agentic commerce readiness." The conversation has been about protocols — can an AI agent parse your UCP manifest? Can it call your payment handlers? Can it add items to a cart?&lt;/p&gt;

&lt;p&gt;That's the protocol layer. And it matters.&lt;/p&gt;

&lt;p&gt;But here's what the referral data shows: &lt;strong&gt;AI agents are indexing your entire web presence, not just your UCP endpoint.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When ChatGPT crawled our validator tool, it wasn't reading a &lt;code&gt;.well-known/ucp&lt;/code&gt; manifest. It was reading our documentation, our compare page, our blog posts. It was building a knowledge graph of "what tool exists for checking UCP profiles" that it can retrieve when a user asks.&lt;/p&gt;

&lt;p&gt;When Perplexity sent us a user with a 100% tool completion rate, it had already decided — before the human ever saw our site — that UCPtools was the right answer to their question.&lt;/p&gt;

&lt;p&gt;This is a new kind of discoverability. Not search ranking. &lt;strong&gt;Knowledge base ranking.&lt;/strong&gt; The question isn't "do I rank #1 for 'UCP validator'?" — it's "when an AI agent needs to answer a UCP question, does it know I exist?"&lt;/p&gt;




&lt;h2&gt;
  
  
  What this means for developer tools and e-commerce platforms
&lt;/h2&gt;

&lt;p&gt;Five different agents found us without us doing anything to attract them. No agent-specific SEO. No structured data markup for AI crawlers. Just a website with clear, specific content about a niche protocol.&lt;/p&gt;

&lt;p&gt;The implication: &lt;strong&gt;AI agents are actively hunting for authoritative sources in specialized domains.&lt;/strong&gt; If you build something in the UCP/ACP/agentic commerce space, the agents will find you — but only if your content is in their corpus.&lt;/p&gt;

&lt;p&gt;Three patterns we can already see:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Agents prefer documentation over marketing.&lt;/strong&gt; Every agent that visited us hit our docs and tools. None touched our pricing page. None visited our blog homepage. They went straight for substance.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tool usage is the strongest signal.&lt;/strong&gt; Perplexity's 100% tool-user rate tells us the agent pre-qualified the user — it already knew our tool was the answer. The human didn't browse; they executed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Multi-agent coverage compounds.&lt;/strong&gt; One agent finding you is luck. Five agents finding you means your content is propagating across knowledge bases. ChatGPT, Gemini, Perplexity, Claude, and NotebookLM use different indexes and different retrieval strategies. If you're in all five, you're discoverable regardless of which agent a user chooses.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  The protocol layer matters too — here's how they connect
&lt;/h2&gt;

&lt;p&gt;We built UCPtools to validate UCP profiles. The core insight was: an AI shopping agent needs a valid UCP manifest to buy from your store. But the protocol layer and the knowledge layer are connected in ways that aren't obvious yet.&lt;/p&gt;

&lt;p&gt;Consider this sequence:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A developer asks ChatGPT: "How do I set up UCP for my Shopify store?"&lt;/li&gt;
&lt;li&gt;ChatGPT searches its knowledge base, finds our documentation (because we're in its corpus), and recommends our validator.&lt;/li&gt;
&lt;li&gt;The developer uses the free validator, finds 3 broken endpoints, and fixes them.&lt;/li&gt;
&lt;li&gt;Now their store is both &lt;strong&gt;UCP-valid&lt;/strong&gt; (protocol layer) AND &lt;strong&gt;indexed by the agents that will send them customers&lt;/strong&gt; (knowledge layer).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Without step 2, the developer might never find the validator. Without step 3, their UCP profile stays broken and AI shopping agents can't transact.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You need both.&lt;/strong&gt; A valid UCP profile that no AI agent knows about is useless. And an AI agent that knows about your store but can't transact because your payment handlers are broken is equally useless.&lt;/p&gt;




&lt;h2&gt;
  
  
  How to check if AI agents can find you (and transact with you)
&lt;/h2&gt;

&lt;p&gt;There's no all-in-one dashboard for this yet. But here's what you can do right now:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Check your referrer logs&lt;/strong&gt; for these domains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;chatgpt.com&lt;/code&gt; / &lt;code&gt;chat.openai.com&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;gemini.google.com&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;perplexity.ai&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;claude.ai&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;notebooklm.google.com&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you see any of them, AI agents are already building knowledge about your site.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Check your UCP profile&lt;/strong&gt; — because being discoverable is pointless if agents can't transact. Our free validator runs 4 levels of checks: structural, rules, network, and an AI agent simulator that actually tests whether an agent can complete a purchase flow.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ucptools.dev/?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=ai-agent-traffic-202606" rel="noopener noreferrer"&gt;Run the free UCP validator →&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Check your robots.txt.&lt;/strong&gt; Most AI crawlers respect &lt;code&gt;robots.txt&lt;/code&gt;. If you're blocking &lt;code&gt;GPTBot&lt;/code&gt;, &lt;code&gt;ClaudeBot&lt;/code&gt;, or &lt;code&gt;Google-Extended&lt;/code&gt;, you're opting out of AI discoverability. That might be intentional — but it's worth knowing what you're blocking.&lt;/p&gt;




&lt;h2&gt;
  
  
  One thing I'm still figuring out
&lt;/h2&gt;

&lt;p&gt;The data is real but the sample is small. Five agents, a handful of visits, one site. This isn't a research paper — it's an observation from our referrer logs that I think matters for anyone building in this space.&lt;/p&gt;

&lt;p&gt;What I don't know yet:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How frequently agents re-crawl and refresh their knowledge&lt;/li&gt;
&lt;li&gt;Whether agent referral traffic converts differently than organic search&lt;/li&gt;
&lt;li&gt;Whether NotebookLM ingestion leads to downstream discoverability in other Google products&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're seeing AI agents in your own referrer logs, I'd love to compare notes. Drop a comment — especially if you're seeing patterns I haven't mentioned.&lt;/p&gt;

&lt;p&gt;The one thing I'm confident about: &lt;strong&gt;the agents are here, they're indexing, and they're already making decisions about which tools and services to recommend.&lt;/strong&gt; The question isn't whether to care about AI discoverability. It's whether you're already invisible.&lt;/p&gt;

</description>
      <category>ucp</category>
      <category>ai</category>
      <category>webdev</category>
      <category>ecommerce</category>
    </item>
    <item>
      <title>Google's Universal Cart Is Live. Here's the Checklist to Make Sure Agents Can Actually Check Out on Your Store</title>
      <dc:creator>Peter</dc:creator>
      <pubDate>Sat, 13 Jun 2026 13:35:37 +0000</pubDate>
      <link>https://dev.to/ucptools/googles-universal-cart-is-live-heres-the-checklist-to-make-sure-agents-can-actually-check-out-on-2n80</link>
      <guid>https://dev.to/ucptools/googles-universal-cart-is-live-heres-the-checklist-to-make-sure-agents-can-actually-check-out-on-2n80</guid>
      <description>&lt;p&gt;At Google Marketing Live on May 20, 2026, Universal Cart stopped being a demo. Google announced that shoppers can now save products across retailers and check out - with Google Pay or the retailer's own flow - directly inside Search, AI Mode, the Gemini app, and YouTube shopping ads. The launch list is not a pilot: Nike, Sephora, Target, Walmart, Wayfair, and Shopify merchants like Fenty and Steve Madden, with Affirm and Klarna wired in for financing and hotels and food delivery slated next.&lt;/p&gt;

&lt;p&gt;The plumbing under all of it is UCP (Universal Commerce Protocol) - the open standard that gives an AI agent a machine-readable entry point to a store at &lt;code&gt;/.well-known/ucp&lt;/code&gt;. &lt;em&gt;(Quick disclaimer: UCP is owned and maintained by Google and Shopify. UCPtools, which I work on, is an independent community tool - not affiliated with either.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Here is the part that matters for everyone not named in that launch list: the agent decides in milliseconds whether your store can transact. There is no "almost." Either your UCP profile answers the questions the agent asks, or the agent moves on to a merchant whose profile does. No bounce shows up in your analytics. You just quietly stop existing in the one shopping surface that is about to matter most.&lt;/p&gt;

&lt;p&gt;So this is the readiness checklist. It maps to the four things a validator actually checks, in the order an agent hits them.&lt;/p&gt;




&lt;h2&gt;
  
  
  Level 1: Structure - does the profile parse at all?
&lt;/h2&gt;

&lt;p&gt;Before anything semantic, the profile has to be a well-formed UCP document served correctly. This is where most "it's there but it doesn't work" failures live.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] &lt;code&gt;/.well-known/ucp&lt;/code&gt; returns &lt;code&gt;200&lt;/code&gt; with &lt;code&gt;Content-Type: application/json&lt;/code&gt; - not &lt;code&gt;text/html&lt;/code&gt;, not a redirect to a marketing page.&lt;/li&gt;
&lt;li&gt;[ ] The root &lt;code&gt;ucp&lt;/code&gt; object exists. Missing it is &lt;code&gt;UCP_MISSING_ROOT&lt;/code&gt; and the agent has nothing to read.&lt;/li&gt;
&lt;li&gt;[ ] A &lt;code&gt;version&lt;/code&gt; field is present and formatted &lt;code&gt;YYYY-MM-DD&lt;/code&gt;. A stray semver string trips &lt;code&gt;UCP_INVALID_VERSION_FORMAT&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;[ ] The JSON is valid - no trailing commas, no CDN error page wrapped around it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sounds trivial. It is the single most common break, because the file is served by a platform or a CDN that nobody is watching, and a caching plugin or a security challenge can turn a clean profile into an HTML error page overnight.&lt;/p&gt;




&lt;h2&gt;
  
  
  Level 2: Rules - is it actually UCP-compliant?
&lt;/h2&gt;

&lt;p&gt;A parseable profile can still be semantically wrong in ways that make an agent distrust it. Level 2 is the UCP compliance layer.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] &lt;strong&gt;Namespace and origin match.&lt;/strong&gt; The namespace your capabilities declare has to bind to the origin serving the profile. A mismatch is &lt;code&gt;UCP_NS_ORIGIN_MISMATCH&lt;/code&gt; - a classic copy-paste-from-staging bug.&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;Every endpoint is HTTPS, no trailing slash.&lt;/strong&gt; &lt;code&gt;UCP_ENDPOINT_NOT_HTTPS&lt;/code&gt; and &lt;code&gt;UCP_ENDPOINT_TRAILING_SLASH&lt;/code&gt; are both hard stops; agents will not POST a payment intent over &lt;code&gt;http://&lt;/code&gt; or to a sloppy URL.&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;No orphaned extensions.&lt;/strong&gt; Every extension has to chain back to a capability that declares it. A dangling one is &lt;code&gt;UCP_ORPHANED_EXTENSION&lt;/code&gt; and signals a profile that was hand-edited and never re-validated.&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;Signing keys are present.&lt;/strong&gt; &lt;code&gt;UCP_MISSING_SIGNING_KEYS&lt;/code&gt; is the one that quietly kills checkout: without published keys, the agent cannot verify that the offer and the payment handler are really yours. It will browse and then refuse to transact.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last one is the difference between showing up in Universal Cart's discovery layer and actually completing a purchase in it. Discovery without signing keys gets you window-shopped, not bought.&lt;/p&gt;




&lt;h2&gt;
  
  
  Level 3: Network - do your references actually resolve?
&lt;/h2&gt;

&lt;p&gt;Your profile points outward - to capability schemas and handler endpoints. Level 3 fetches them, the way an agent would, from outside your network.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Every referenced capability schema is reachable and self-describing. A schema host that 404s or times out is &lt;code&gt;UCP_SCHEMA_FETCH_FAILED&lt;/code&gt;, and it is &lt;em&gt;someone else's outage becoming your broken profile&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;[ ] Handler endpoints respond from the public internet, not just from inside your VPC. A check that runs on your own infra can hit a warm cache and report healthy while real agents get a connection error.&lt;/li&gt;
&lt;li&gt;[ ] TLS is valid across every edge, not just your origin. Certs renew and propagate unevenly; one stale CDN edge is enough to fail the agent that lands on it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You cannot fully test Level 3 from a unit test, because the failures are environmental and external. This is the level that needs to be checked against your &lt;em&gt;live&lt;/em&gt; domain, from outside.&lt;/p&gt;




&lt;h2&gt;
  
  
  Level 4: SDK - does it pass the official compliance pass?
&lt;/h2&gt;

&lt;p&gt;The top level runs your profile through &lt;code&gt;@ucp-js/sdk&lt;/code&gt;, the official UCP compliance kit. This is the closest a check gets to "would Google's own agent accept this?" - the same library the ecosystem builds against, applied to your live profile. Passing Levels 1 through 3 and failing here usually means a subtle shape mismatch the schema rules did not catch. If you only automate one gate, make it this one, because it is the one that tracks the spec as it evolves.&lt;/p&gt;




&lt;h2&gt;
  
  
  Run the whole checklist in one call
&lt;/h2&gt;

&lt;p&gt;You do not have to eyeball any of this. UCPtools exposes a remote endpoint that fetches your live profile and runs all four levels server-side, the way an agent sees it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-sS&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://ucptools.dev/v1/profiles/validate-remote &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"domain":"mystore.com"}'&lt;/span&gt; | jq
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ok"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"profile_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://mystore.com/.well-known/ucp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;72&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"grade"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"C"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"issues"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"severity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"UCP_MISSING_SIGNING_KEYS"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"$.ucp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"No signing keys published"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"hint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Publish your Ed25519/ES256 public keys so agents can verify offers."&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"validated_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-06-13T10:00:00Z"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wire that same check into CI with the &lt;a href="https://github.com/Nolpak14/ucp-validate-action" rel="noopener noreferrer"&gt;&lt;code&gt;ucp-validate-action&lt;/code&gt;&lt;/a&gt; so a regression fails the build before it ships:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Nolpak14/ucp-validate-action@v1&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;domain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;mystore.com'&lt;/span&gt;
    &lt;span class="na"&gt;min-score&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;90&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Where each stack tends to break
&lt;/h2&gt;

&lt;p&gt;The checklist is platform-agnostic - it reads the open standard, not platform internals - but the failure that actually bites you tends to cluster by stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Shopify:&lt;/strong&gt; you are largely along for the ride on what the platform serves at the well-known path, which is good until a platform change shifts the manifest shape under you. Validate after every theme or app change, because you do not control that surface directly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WooCommerce:&lt;/strong&gt; the usual culprit is a caching or security plugin that starts serving &lt;code&gt;/.well-known/ucp&lt;/code&gt; from cache, behind a challenge, or as &lt;code&gt;text/html&lt;/code&gt;. That is a Level 1 break (&lt;code&gt;Content-Type&lt;/code&gt;) hiding behind a green admin dashboard.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;BigCommerce / headless:&lt;/strong&gt; a frontend deploy moves a handler endpoint the profile still advertises, and Level 3 starts failing while the storefront looks perfect to a human. Storefront-scope mismatches show up here too.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom / self-hosted:&lt;/strong&gt; signing-key drift. The key rotates in your infra but not in the published profile, and &lt;code&gt;UCP_MISSING_SIGNING_KEYS&lt;/code&gt; or a verification failure silently downgrades you from "can transact" to "can only be browsed."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In every case the break is invisible to a human visitor and only shows up when something fetches the live profile from outside and runs the checks. That is the entire argument for validating continuously instead of once.&lt;/p&gt;

&lt;h2&gt;
  
  
  The thing the launch list quietly proves
&lt;/h2&gt;

&lt;p&gt;The brands Google named did not get into Universal Cart by accident. Someone on each of those teams treated the UCP profile as production infrastructure - versioned, validated, and monitored - not as a one-time SEO chore. That is the whole gap. The standard is open and the endpoint is a static file; nothing stops a mid-sized Shopify or WooCommerce store from being exactly as agent-ready as Nike. What stops most of them is that nobody ever validated past "the file exists."&lt;/p&gt;

&lt;p&gt;Universal Cart going live is the forcing function. Agents are transacting now, in surfaces real shoppers already use every day. Run the checklist against your store - &lt;a href="https://ucptools.dev" rel="noopener noreferrer"&gt;validate it free at ucptools.dev&lt;/a&gt; - and if it passes, set up monitoring so it &lt;em&gt;stays&lt;/em&gt; passing at 3am when a cert renews and no one shipped a thing. The merchants who win this transition treat their UCP profile like uptime, because for agentic checkout, that is exactly what it is.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;UCP is an open standard by Google and Shopify. UCPtools is an independent community tool.&lt;/em&gt;&lt;br&gt;
&lt;em&gt;Built by Peter at &lt;a href="https://ucptools.dev" rel="noopener noreferrer"&gt;UCPtools&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ecommerce</category>
      <category>ai</category>
      <category>webdev</category>
      <category>googlecloud</category>
    </item>
    <item>
      <title>UCP vs ACP: The Battle of AI Commerce Protocols and How to Build for Both</title>
      <dc:creator>Peter</dc:creator>
      <pubDate>Thu, 11 Jun 2026 18:49:04 +0000</pubDate>
      <link>https://dev.to/ucptools/ucp-vs-acp-the-battle-of-ai-commerce-protocols-and-how-to-build-for-both-18ao</link>
      <guid>https://dev.to/ucptools/ucp-vs-acp-the-battle-of-ai-commerce-protocols-and-how-to-build-for-both-18ao</guid>
      <description>&lt;p&gt;The AI commerce agent landscape exploded in early 2026 when both Google's &lt;strong&gt;Universal Commerce Protocol (UCP)&lt;/strong&gt; and OpenAI's &lt;strong&gt;Agentic Commerce Protocol (ACP)&lt;/strong&gt; launched within weeks of each other. If you're building an AI shopping agent, you now face a critical choice: which protocol should you implement? Or should you support both?&lt;/p&gt;

&lt;p&gt;In this deep-dive, we'll compare UCP and ACP from an implementer's perspective, examine their architectural trade-offs, and show you how to get started with &lt;strong&gt;Agorio&lt;/strong&gt; - the only open-source SDK that implements both with automatic detection.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Rise of Agentic Commerce
&lt;/h2&gt;

&lt;p&gt;AI agents that can shop on your behalf are no longer science fiction. With 4.8M Shopify merchants discoverable via UCP and 1.5M Stripe merchants ready for ACP, the infrastructure for autonomous commerce is already in place. Visa and Mastercard are enabling all US cardholders for agent transactions by holiday 2026.&lt;/p&gt;

&lt;p&gt;But to tap into this ecosystem, your agent needs to speak the right protocol. Let's look at the two contenders.&lt;/p&gt;

&lt;h2&gt;
  
  
  UCP: Google &amp;amp; Shopify's Decentralized Approach
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;Universal Commerce Protocol&lt;/strong&gt; is an open standard co-developed by Google, Shopify, Etsy, Wayfair, Target, Walmart and endorsed by Stripe, PayPal, Visa, Mastercard.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Architecture
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Discovery&lt;/strong&gt;: Merchants publish a JSON manifest at &lt;code&gt;/.well-known/ucp&lt;/code&gt; that tells agents what they support - capabilities, endpoints, payment handlers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Capabilities&lt;/strong&gt;: Modular capabilities like &lt;code&gt;dev.ucp.shopping.checkout&lt;/code&gt; (cart, tax, session handling), &lt;code&gt;dev.ucp.shopping.order&lt;/code&gt; (post-purchase), and extensions for fulfillment, discounts, buyer consent.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Payment Architecture&lt;/strong&gt;: The "trust triangle" separates business, payment credential provider, and platform. Multiple handler types: Google Pay, Shop Pay, Direct Tokenization, AP2 (autonomous agent mandates).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transport Bindings&lt;/strong&gt;: REST (OpenAPI), MCP (OpenRPC for LLM tools), and Agent Card (A2A).
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Example&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;.well-known/ucp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;manifest&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ucpVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-01-11"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"capabilities"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dev.ucp.shopping.checkout"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-01-11"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"restEndpoint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://api.example.com/ucp/checkout"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"paymentHandlers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"google_pay"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  When to Choose UCP
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;You need &lt;strong&gt;decentralized discovery&lt;/strong&gt; (any merchant can self-host)&lt;/li&gt;
&lt;li&gt;Your agents need &lt;strong&gt;rich shopping workflows&lt;/strong&gt; (cart, catalog, order tracking)&lt;/li&gt;
&lt;li&gt;You're building for &lt;strong&gt;Google's AI Mode&lt;/strong&gt; or &lt;strong&gt;Gemini&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;You need &lt;strong&gt;multiple payment handler types&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ACP: OpenAI &amp;amp; Stripe's Delegated Payment Model
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;Agentic Commerce Protocol&lt;/strong&gt; is an open standard (Apache 2.0) by OpenAI and Stripe, primarily powering ChatGPT Instant Checkout.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Architecture
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Discovery&lt;/strong&gt;: Centralized - merchants apply at chatgpt.com/merchants and provide product feeds&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Payment Model&lt;/strong&gt;: Delegated payment - any PSP can implement the spec (Stripe first, PayPal second)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Endpoints&lt;/strong&gt;: 5 REST endpoints for the full checkout lifecycle: create, get, update, complete, cancel&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;States&lt;/strong&gt;: &lt;code&gt;not_ready_for_payment&lt;/code&gt; → &lt;code&gt;ready_for_payment&lt;/code&gt; → &lt;code&gt;completed&lt;/code&gt; (with optional authentication steps)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Example ACP checkout session flow&lt;/span&gt;
&lt;span class="nx"&gt;POST&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;checkout_sessions&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;line_items&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;product_id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;prod_123&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;amount&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2999&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// $29.99 in cents&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;quantity&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;return_url&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://example.com/return&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;success_url&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://example.com/success&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  When to Choose ACP
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;You're building for &lt;strong&gt;ChatGPT&lt;/strong&gt; or other OpenAI-agent ecosystems&lt;/li&gt;
&lt;li&gt;You prefer a &lt;strong&gt;centralized, vetted merchant pool&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Your transactions are &lt;strong&gt;single-item purchases&lt;/strong&gt; (current limitation)&lt;/li&gt;
&lt;li&gt;You want &lt;strong&gt;delegated payment&lt;/strong&gt; through any PSP&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Head-to-Head Comparison
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;UCP&lt;/th&gt;
&lt;th&gt;ACP&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Developers&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Google, Shopify, 25+ partners&lt;/td&gt;
&lt;td&gt;OpenAI, Stripe&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Discovery&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Decentralized (&lt;code&gt;/.well-known/ucp&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Centralized (merchant application)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Payment&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Multiple handlers (Google Pay, Shop Pay, etc.)&lt;/td&gt;
&lt;td&gt;Delegated via any PSP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Extensibility&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Namespace-based extensions&lt;/td&gt;
&lt;td&gt;RFC-based extensions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Transport&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;REST, MCP, A2A&lt;/td&gt;
&lt;td&gt;REST only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Merchant Footprint&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;4.8M Shopify merchants + any self-hosted&lt;/td&gt;
&lt;td&gt;1.5M Stripe merchants + 35M PayPal (coming)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;License&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Open spec&lt;/td&gt;
&lt;td&gt;Apache 2.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Current Limitations&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Complex capability negotiation&lt;/td&gt;
&lt;td&gt;US-only, single-item only&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Why Choose? Build Both with Agorio
&lt;/h2&gt;

&lt;p&gt;The reality is that &lt;strong&gt;most serious commerce agents will need to support both protocols&lt;/strong&gt; to reach the maximum number of merchants. That's where &lt;a href="https://agorio.dev?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202606" rel="noopener noreferrer"&gt;Agorio&lt;/a&gt; comes in.&lt;/p&gt;

&lt;p&gt;Agorio is an open-source TypeScript SDK that implements both UCP and ACP with automatic protocol detection. Here's how you can use it:&lt;/p&gt;

&lt;h3&gt;
  
  
  Example: UCP Client
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;UcpClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@agorio/sdk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Discover merchant capabilities&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ucp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;UcpClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;profile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ucp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;discover&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Check if they support checkout&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;supportsCheckout&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ucp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createCheckoutSession&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;cartId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cart_123&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;paymentHandler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;google_pay&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Example: ACP Client
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AcpClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@agorio/sdk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;acp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AcpClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;your_stripe_key&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;merchantId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;merch_123&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;acp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createCheckoutSession&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;lineItems&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;prod_123&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2999&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The ShoppingAgent: Auto-Detection Magic
&lt;/h3&gt;

&lt;p&gt;Agorio's &lt;code&gt;ShoppingAgent&lt;/code&gt; automatically detects which protocol a merchant supports and uses the appropriate client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ShoppingAgent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;GeminiAdapter&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@agorio/sdk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ShoppingAgent&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;llm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GeminiAdapter&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GEMINI_API_KEY&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="na"&gt;verbose&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Agent automatically handles protocol detection&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Go to store.example.com and buy me wireless headphones&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Works with both UCP and ACP merchants&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Order placed: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;checkout&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Implementation Complexity Analysis
&lt;/h2&gt;

&lt;p&gt;Implementing UCP from scratch requires:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JSON-LD manifest parsing&lt;/li&gt;
&lt;li&gt;Capability intersection algorithm&lt;/li&gt;
&lt;li&gt;Payment handler abstraction&lt;/li&gt;
&lt;li&gt;Multiple transport bindings&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Implementing ACP from scratch requires:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bearer auth with API versioning&lt;/li&gt;
&lt;li&gt;Payment token delegation&lt;/li&gt;
&lt;li&gt;Product feed management&lt;/li&gt;
&lt;li&gt;Strict state machine adherence&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Agorio handles all of this for you&lt;/strong&gt;, providing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;UcpClient&lt;/code&gt; with discovery + REST/MCP auto-transport&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;AcpClient&lt;/code&gt; with full session lifecycle management&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ShoppingAgent&lt;/code&gt; with 12 built-in tools for commerce workflows&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;MockMerchant&lt;/code&gt; for testing (UCP, ACP, and MCP variants)&lt;/li&gt;
&lt;li&gt;LLM adapters for Gemini, Claude, OpenAI, Ollama&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Practical Recommendations
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Start with ACP if&lt;/strong&gt; you're building for ChatGPT or want the simplest integration with Stripe/PayPal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Start with UCP if&lt;/strong&gt; you need decentralized discovery, multi-item carts, or Google AI Mode integration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use Agorio if&lt;/strong&gt; you want to support both protocols from day one (recommended).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Remember: &lt;strong&gt;merchants should support both protocols&lt;/strong&gt; for maximum AI agent coverage, and so should your agent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It Yourself
&lt;/h2&gt;

&lt;p&gt;The fastest way to experiment is Agorio's interactive playground:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://agorio.dev/playground?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202606" rel="noopener noreferrer"&gt;Agorio Playground&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Or install the SDK:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @agorio/sdk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check out the &lt;a href="https://github.com/Nolpak14/agorio" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt; (MIT licensed) and star it if you find it useful.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Future of Agentic Commerce
&lt;/h2&gt;

&lt;p&gt;Both UCP and ACP are evolving rapidly. UCP's AP2 mandates will enable cryptographic payment authorization for autonomous agents, while ACP is expanding to multi-item purchases and international markets.&lt;/p&gt;

&lt;p&gt;The key takeaway: &lt;strong&gt;protocol literacy is your moat&lt;/strong&gt;. Understanding these standards will make you a more effective agent builder, whether you're working on a personal shopping assistant or an enterprise procurement agent.&lt;/p&gt;

&lt;p&gt;Have you started building with UCP or ACP? Share your experiences in the comments below!&lt;/p&gt;

</description>
      <category>ai</category>
      <category>typescript</category>
      <category>commerce</category>
      <category>ucp</category>
    </item>
    <item>
      <title>Build a UCP Watchdog: Catch the Production Breaks Your CI Never Will</title>
      <dc:creator>Peter</dc:creator>
      <pubDate>Fri, 05 Jun 2026 14:50:32 +0000</pubDate>
      <link>https://dev.to/ucptools/build-a-ucp-watchdog-catch-the-production-breaks-your-ci-never-will-18jh</link>
      <guid>https://dev.to/ucptools/build-a-ucp-watchdog-catch-the-production-breaks-your-ci-never-will-18jh</guid>
      <description>&lt;p&gt;You wired UCP validation into CI. Every push runs the checks, every PR gets a score, and a bad profile fails the build before it merges. Good - that is the right baseline.&lt;/p&gt;

&lt;p&gt;Here is what it does not catch: the break that happens when nobody touches the code.&lt;/p&gt;

&lt;p&gt;The standard here is UCP (Universal Commerce Protocol) - an open standard that gives AI shopping agents a machine-readable entry point to a store at &lt;code&gt;/.well-known/ucp&lt;/code&gt;. &lt;em&gt;(Quick disclaimer: UCP is owned and maintained by Google and Shopify. UCPtools, which I work on, is an independent community tool - not affiliated with either.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A CI gate is triggered by &lt;em&gt;your&lt;/em&gt; commits. But a UCP profile is a live production surface, and most of the things that break it are not commits at all:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;TLS certificate renews&lt;/strong&gt; and propagates to your origin but not to every CDN edge.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;capability schema host&lt;/strong&gt; your profile references goes down - someone else's outage, your broken profile.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;CDN or DNS change&lt;/strong&gt; starts serving a cache page or a redirect at &lt;code&gt;/.well-known/ucp&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Your &lt;strong&gt;platform&lt;/strong&gt; (Shopify, BigCommerce, a WooCommerce plugin update) quietly changes the served manifest or strips the &lt;code&gt;Content-Type: application/json&lt;/code&gt; header.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;signing key rotates&lt;/strong&gt; in your infra but not in the published profile.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these trips a build, because there is no build. Your CI is green. Your store works fine for human browsers. The only thing that regressed is the machine-readable layer that no human ever visits - and the AI agent that hits it does not file a bug. It just leaves for the next merchant whose profile answers.&lt;/p&gt;

&lt;p&gt;CI catches what you break on merge. A watchdog catches what breaks itself. You need both.&lt;/p&gt;




&lt;h2&gt;
  
  
  What a Watchdog Actually Watches
&lt;/h2&gt;

&lt;p&gt;A pre-merge gate asks "is the profile I'm about to ship valid?" A watchdog asks a different question on a schedule: "is the profile that is live &lt;em&gt;right now&lt;/em&gt; still valid, from outside, the way an agent sees it?"&lt;/p&gt;

&lt;p&gt;Two design rules make the difference:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Check from outside your network.&lt;/strong&gt; A check that runs inside your own infra can hit a warm cache or an internal route and report healthy while external agents get errors. Fetch your public URL over the public internet.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compare against a baseline, not just against pass/fail.&lt;/strong&gt; A profile can stay technically valid while its score quietly slides from A to C. Alert on regression from a known-good baseline, not only on hard failures.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's build it two ways: a dependency-free cron version, and a GitHub Action with Slack alerts.&lt;/p&gt;




&lt;h2&gt;
  
  
  The 10-Line Version: cron + curl
&lt;/h2&gt;

&lt;p&gt;UCPtools exposes a public remote-validation endpoint that fetches a live domain's profile and runs the checks server-side. You can hit it from anything that runs &lt;code&gt;curl&lt;/code&gt; and &lt;code&gt;jq&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="c"&gt;# ucp-watch.sh - alert if the live UCP profile is broken&lt;/span&gt;
&lt;span class="nv"&gt;DOMAIN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"mystore.com"&lt;/span&gt;

&lt;span class="nv"&gt;resp&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-sS&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://ucptools.dev/v1/profiles/validate-remote &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;domain&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="nv"&gt;$DOMAIN&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="nv"&gt;ok&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$resp&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;     | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.ok'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$resp&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'[.issues[] | select(.severity=="error")] | length'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ok&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s2"&gt;"true"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$errors&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-gt&lt;/span&gt; 0 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nv"&gt;codes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$resp&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'[.issues[] | select(.severity=="error") | .code] | join(", ")'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
  curl &lt;span class="nt"&gt;-sS&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SLACK_WEBHOOK_URL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Content-type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:rotating_light: UCP profile for &lt;/span&gt;&lt;span class="nv"&gt;$DOMAIN&lt;/span&gt;&lt;span class="s2"&gt; is broken: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;codes&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}"&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The endpoint returns the live result, shaped like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ok"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"profile_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://mystore.com/.well-known/ucp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"issues"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"severity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"UCP_SCHEMA_FETCH_FAILED"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"$.ucp.capabilities[0]"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"hint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"validated_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-06-05T14:33:57Z"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Schedule it and you have a watchdog:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;*/15 * * * * SLACK_WEBHOOK_URL=https://hooks.slack.com/... /opt/ucp-watch.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now &lt;code&gt;UCP_SCHEMA_FETCH_FAILED&lt;/code&gt; or &lt;code&gt;UCP_ENDPOINT_NOT_HTTPS&lt;/code&gt; showing up at 3am - hours after a cert renewal, with no deploy in sight - pages you instead of silently costing you agent traffic.&lt;/p&gt;




&lt;h2&gt;
  
  
  The GitHub Action Version: scheduled, with a baseline
&lt;/h2&gt;

&lt;p&gt;If your store already lives in GitHub, you can run the same idea on a &lt;code&gt;schedule:&lt;/code&gt; trigger and reuse the existing &lt;a href="https://github.com/Nolpak14/ucp-validate-action" rel="noopener noreferrer"&gt;&lt;code&gt;ucp-validate-action&lt;/code&gt;&lt;/a&gt; - the same action people put in CI - but pointed at your &lt;strong&gt;live production domain&lt;/strong&gt; and run on a clock instead of on push. The difference is entirely in the trigger and what you do with the result.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;UCP Watchdog&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;schedule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;cron&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;*/30&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*'&lt;/span&gt;   &lt;span class="c1"&gt;# every 30 minutes&lt;/span&gt;
  &lt;span class="na"&gt;workflow_dispatch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;          &lt;span class="c1"&gt;# let me run it by hand too&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;watch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ucp&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Nolpak14/ucp-validate-action@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;domain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;mystore.com'&lt;/span&gt;   &lt;span class="c1"&gt;# your LIVE domain, not staging&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Alert on regression&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;SLACK_WEBHOOK_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.SLACK_WEBHOOK_URL }}&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;score="${{ steps.ucp.outputs.score }}"&lt;/span&gt;
          &lt;span class="s"&gt;grade="${{ steps.ucp.outputs.grade }}"&lt;/span&gt;
          &lt;span class="s"&gt;found="${{ steps.ucp.outputs.ucp-found }}"&lt;/span&gt;
          &lt;span class="s"&gt;baseline=$(cat .ucp-baseline 2&amp;gt;/dev/null || echo 0)&lt;/span&gt;

          &lt;span class="s"&gt;echo "Live: score=$score grade=$grade found=$found | baseline=$baseline"&lt;/span&gt;

          &lt;span class="s"&gt;if [ "$found" = "false" ] || [ "$score" -lt "$baseline" ]; then&lt;/span&gt;
            &lt;span class="s"&gt;curl -sS -X POST "$SLACK_WEBHOOK_URL" -H 'Content-type: application/json' \&lt;/span&gt;
              &lt;span class="s"&gt;-d "{\"text\":\":rotating_light: UCP regression on mystore.com - score ${score} (grade ${grade}), baseline ${baseline}\"}"&lt;/span&gt;
            &lt;span class="s"&gt;exit 1&lt;/span&gt;
          &lt;span class="s"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Commit a one-line baseline file the first time you go green:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo &lt;/span&gt;90 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; .ucp-baseline   &lt;span class="c"&gt;# your known-good score&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The action exposes &lt;code&gt;score&lt;/code&gt;, &lt;code&gt;grade&lt;/code&gt;, &lt;code&gt;ucp-found&lt;/code&gt;, &lt;code&gt;passed&lt;/code&gt;, and &lt;code&gt;result-json&lt;/code&gt;, so you can build whatever alerting logic you want on top. The point is that the trigger is a clock, the target is production, and the comparison is against your last known-good state.&lt;/p&gt;




&lt;h2&gt;
  
  
  Alert Hygiene (so you don't train yourself to ignore it)
&lt;/h2&gt;

&lt;p&gt;A watchdog that cries wolf gets muted, and a muted watchdog is worse than none. Three things keep it honest:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Baseline, don't just pass/fail.&lt;/strong&gt; A slow slide from grade A to grade C is the regression you most want to know about, and a binary "still valid?" check will miss it entirely. Diff the score.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Debounce flaps.&lt;/strong&gt; A single failed fetch can be a transient network blip. Require two consecutive bad checks before paging, or alert on a sustained drop rather than one data point.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bump the baseline when you improve.&lt;/strong&gt; When you legitimately raise your score, update &lt;code&gt;.ucp-baseline&lt;/code&gt; in the same PR. The baseline is a ratchet - it should only move up on purpose.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Platform Notes
&lt;/h2&gt;

&lt;p&gt;The watchdog is platform-agnostic - it reads the open &lt;code&gt;/.well-known/ucp&lt;/code&gt; standard, not platform internals - but the regression that pages you tends to differ by stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;WooCommerce:&lt;/strong&gt; a caching or security plugin update that starts serving &lt;code&gt;/.well-known/ucp&lt;/code&gt; from cache, behind a challenge, or as &lt;code&gt;text/html&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;BigCommerce / headless:&lt;/strong&gt; a frontend deploy or app change that moves an endpoint the profile still advertises, or a storefront-scope mismatch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shopify:&lt;/strong&gt; the platform changing what it serves at the well-known path out from under you.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In every case the failure is invisible until something fetches the live profile from outside and compares it to what you expect. That is the whole job of the watchdog.&lt;/p&gt;




&lt;p&gt;CI proves the profile you wrote is correct. A watchdog proves the profile your customers' agents actually hit is still correct - at 3am, after a cert renewal, when no one shipped a thing. Both are a few lines of YAML. The merchants who win the agentic-commerce transition will treat the second one like uptime, because that is exactly what it is.&lt;/p&gt;

&lt;p&gt;If you would rather not run your own, UCPtools does hosted monitoring with break-alerts across all four validation levels - &lt;a href="https://ucptools.dev/signup" rel="noopener noreferrer"&gt;start here&lt;/a&gt;. Either way: watch the live profile, not just the build.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;UCP is an open standard by Google and Shopify. UCPtools is an independent community tool.&lt;/em&gt;&lt;br&gt;
&lt;em&gt;Built by Peter at &lt;a href="https://ucptools.dev" rel="noopener noreferrer"&gt;UCPtools&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>githubactions</category>
      <category>devops</category>
      <category>ecommerce</category>
      <category>ai</category>
    </item>
    <item>
      <title>How AI Shopping Agents Read Your Magento Store (and 4 UCP Checks That Fail in Production)</title>
      <dc:creator>Peter</dc:creator>
      <pubDate>Thu, 21 May 2026 15:52:52 +0000</pubDate>
      <link>https://dev.to/ucptools/how-ai-shopping-agents-read-your-magento-store-and-4-ucp-checks-that-fail-in-production-1lkj</link>
      <guid>https://dev.to/ucptools/how-ai-shopping-agents-read-your-magento-store-and-4-ucp-checks-that-fail-in-production-1lkj</guid>
      <description>&lt;p&gt;If you run a Magento or Adobe Commerce store, you have probably started getting the question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Can ChatGPT or Google's AI find my products? Are we 'AI-ready'?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The standard that answers it is UCP (Universal Commerce Protocol) - an open standard from Google and Shopify that gives AI shopping agents a machine-readable entry point to a store, served at &lt;code&gt;/.well-known/ucp&lt;/code&gt;. &lt;em&gt;(Quick disclaimer: UCP is owned and maintained by Google and Shopify. UCPtools, which I work on, is an independent community tool - not affiliated with either.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Here is the catch for Magento specifically: a UCP profile that validates perfectly on your laptop can still be broken for every real AI agent in production. Magento's enterprise stack - GraphQL plus REST, Varnish full-page cache, multi-store scopes, CDN edges - breaks UCP discovery in ways a single-tenant Shopify store simply never encounters.&lt;/p&gt;

&lt;p&gt;This post walks through the four levels of UCP validation and, for each, the Magento-specific failure mode that bites in production.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Magento Is a Different Animal
&lt;/h2&gt;

&lt;p&gt;On Shopify, the platform owns the edge. Your UCP surface is largely handled for you and the failure modes are narrow.&lt;/p&gt;

&lt;p&gt;On Magento/Adobe Commerce, &lt;em&gt;you&lt;/em&gt; own the edge - and that is exactly where UCP lives:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;/.well-known/ucp&lt;/code&gt; is a static-looking path served by a very dynamic system.&lt;/strong&gt; Varnish, Fastly, or your CDN decides whether agents see a fresh profile or a stale one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Two API surfaces.&lt;/strong&gt; Magento exposes both GraphQL and REST. A UCP profile that points agents at endpoints has to point at ones that actually answer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multiple store views and websites.&lt;/strong&gt; A profile bound to the wrong scope describes the wrong catalog, currency, or domain.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Self-managed TLS and CDN.&lt;/strong&gt; Certificate renewals and cache propagation are your problem, and agents are unforgiving about both.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;UCP validation is not a one-time setup step here. It is a production concern, like uptime.&lt;/p&gt;




&lt;h2&gt;
  
  
  Level 1: Structural - Does the Profile Parse?
&lt;/h2&gt;

&lt;p&gt;The first level is the cheap one: is &lt;code&gt;/.well-known/ucp&lt;/code&gt; valid JSON, are the required fields present, and is the version string a valid &lt;code&gt;YYYY-MM-DD&lt;/code&gt; date?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Magento failure mode: Varnish serves HTML, not JSON.&lt;/strong&gt; The single most common Magento issue is that the full-page cache or a misconfigured rewrite intercepts &lt;code&gt;/.well-known/ucp&lt;/code&gt; and returns an HTML error page (or the homepage) with a &lt;code&gt;200&lt;/code&gt;. Structurally, an agent receives HTML where it expected JSON, and discovery dies at step one.&lt;/p&gt;

&lt;p&gt;The fix is a cache-bypass rule for the &lt;code&gt;.well-known&lt;/code&gt; path. If you are on Varnish, exclude it from the full-page cache so the profile is always served fresh and as &lt;code&gt;application/json&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Level 2: Rules - Is the Profile Internally Consistent?
&lt;/h2&gt;

&lt;p&gt;Structural validity is not compliance. Level 2 checks the UCP rules: namespace and origin binding, extension chains, HTTPS-only endpoints, and the presence of signing keys.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Magento failure mode: origin/scope mismatch.&lt;/strong&gt; Multi-store Magento installs serve several domains and store views from one backend. It is easy to publish a profile whose declared origin does not match the host actually serving it, or whose capability endpoints point at a different store view's domain. To an agent, that is a profile that does not trust its own host - and it will not transact against it.&lt;/p&gt;

&lt;p&gt;A close second: declaring capability endpoints over &lt;code&gt;http://&lt;/code&gt; or with trailing slashes that your rewrites then bounce. Agents follow the spec literally; "close enough" URLs are not close enough.&lt;/p&gt;




&lt;h2&gt;
  
  
  Level 3: Network - Do the Endpoints Actually Answer?
&lt;/h2&gt;

&lt;p&gt;Level 3 leaves the profile behind and goes to the wire. It fetches the schemas and endpoints the profile advertises and verifies they resolve, over HTTPS, with a valid certificate chain.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Magento failure mode: the deploy that quietly breaks an endpoint.&lt;/strong&gt; This is the one that makes UCP a recurring concern rather than a checklist item. A &lt;code&gt;setup:upgrade&lt;/code&gt;, a module update, or a routing change can move or 500 the very endpoint your profile promised. The profile still validates structurally - it is the live endpoint that regressed.&lt;/p&gt;

&lt;p&gt;The other classic: a certificate renewal that propagated to your origin but not to every CDN edge, so agents hitting one POP get a valid chain and agents hitting another get a handshake error. You will not see it in a browser; an agent will.&lt;/p&gt;




&lt;h2&gt;
  
  
  Level 4: SDK / Spec - Does It Pass Official Compliance?
&lt;/h2&gt;

&lt;p&gt;The final level runs the profile against the official UCP SDK to confirm it complies with the current published spec, not just a plausible-looking shape. Specs move; a profile written against an older draft can drift out of compliance without anyone touching it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Magento failure mode: pinned-and-forgotten.&lt;/strong&gt; Enterprise Magento changes slowly and deliberately - which is a virtue everywhere except here. A profile authored months ago against an earlier UCP version keeps validating against its own assumptions while the spec advances around it. Level 4 is what catches that drift.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Part Most Magento Teams Miss: Discovery Is Not Checkout
&lt;/h2&gt;

&lt;p&gt;A page can be perfectly structured - Schema.org markup, FAQs, breadcrumbs - and an agent can still be unable to &lt;em&gt;buy&lt;/em&gt;. Structured data helps agents understand your catalog. UCP is the actionable layer that lets them complete a purchase: the capabilities, endpoints, and payment handlers a profile declares.&lt;/p&gt;

&lt;p&gt;Passing a structural "AI readiness" check means you are discoverable. Passing all four UCP levels - in production, on every CDN edge, after every deploy - means you are transactable. Those are different bars, and only the second one earns the order.&lt;/p&gt;




&lt;h2&gt;
  
  
  How to Check Your Store
&lt;/h2&gt;

&lt;p&gt;If you want to see where your Magento store actually stands:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Run your domain through a UCP validator that does all four levels, not just a JSON parse. (UCPtools does this for free; it works on Magento, Adobe Commerce, and any other platform, since it reads the open &lt;code&gt;/.well-known/ucp&lt;/code&gt; standard rather than platform internals.)&lt;/li&gt;
&lt;li&gt;Pay special attention to the &lt;strong&gt;Network&lt;/strong&gt; level - that is where Magento's Varnish/CDN/deploy issues surface.&lt;/li&gt;
&lt;li&gt;Re-run it after every deploy and certificate renewal. Treat a UCP regression like a failed health check.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I wrote a deeper, Magento-specific walkthrough - including the GraphQL + REST endpoint setup and the Varnish bypass rule - in the &lt;a href="https://ucptools.dev/guides/magento" rel="noopener noreferrer"&gt;Magento &amp;amp; Adobe Commerce UCP guide&lt;/a&gt;. If you want to compare structural "readiness" checks against full UCP validation, &lt;a href="https://ucptools.dev/compare/ucptools-vs-shopify-agentic-readiness" rel="noopener noreferrer"&gt;this comparison&lt;/a&gt; lays out the difference.&lt;/p&gt;




&lt;p&gt;The merchants who win the agentic-commerce transition will not be the ones with the prettiest product pages. They will be the ones whose checkout an agent can actually complete - reliably, in production, on Magento's genuinely complicated stack. The good news is that it is all measurable now. Measure it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;UCP is an open standard by Google and Shopify. UCPtools is an independent community tool.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ucp</category>
      <category>ecommerce</category>
      <category>magento</category>
      <category>ai</category>
    </item>
    <item>
      <title>ACP Moved Checkout to the Merchant Surface. It Didn't Cede Commerce - Here's the Protocol Convergence.</title>
      <dc:creator>Peter</dc:creator>
      <pubDate>Wed, 06 May 2026 10:21:17 +0000</pubDate>
      <link>https://dev.to/ucptools/acp-pivoted-from-checkout-to-discovery-ucp-wins-by-default-heres-the-protocol-convergence-374e</link>
      <guid>https://dev.to/ucptools/acp-pivoted-from-checkout-to-discovery-ucp-wins-by-default-heres-the-protocol-convergence-374e</guid>
      <description>&lt;h1&gt;
  
  
  ACP Moved Checkout to the Merchant Surface. It Didn't Cede Commerce - Here's the Protocol Convergence.
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;Updated July 2026: this article originally ran as "ACP Pivoted from Checkout to Discovery. UCP Wins by Default." That call was wrong. ACP's March pivot moved the checkout **surface&lt;/em&gt;&lt;em&gt;, not the checkout **capability&lt;/em&gt;* - and the 2026-04-17 spec release that landed weeks after this article was published added carts, an orders lifecycle, a public product feed spec, its own &lt;code&gt;/.well-known/acp.json&lt;/code&gt; discovery document and MCP transport. ACP is not a discovery-only layer. The sections below are corrected; the implementation advice about UCP profile quality stands.*&lt;/p&gt;

&lt;p&gt;In March 2026, OpenAI pivoted ACP (Agentic Commerce Protocol) away from its original premise: completing purchases inside ChatGPT. The "Instant Checkout" button that was supposed to revolutionize shopping? Narrowed. Replaced by a merchant app model where purchases complete on the retailer's own storefront.&lt;/p&gt;

&lt;p&gt;That was widely read - including here - as ACP retreating to discovery and handing the commerce stack to UCP. Four months later, that reading has not held up.&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually happened
&lt;/h2&gt;

&lt;p&gt;OpenAI's ACP launched with an ambitious vision: users would discover products, compare options, and complete purchases entirely within ChatGPT. Stripe provided the payment rails. The promise was a seamless, one-tap checkout experience inside an AI conversation.&lt;/p&gt;

&lt;p&gt;The in-chat version of that didn't work.&lt;/p&gt;

&lt;p&gt;CNBC reported on March 20 that "OpenAI's first try at agentic shopping stumbled." Users browsed products in ChatGPT but abandoned purchases before completing them. The in-chat checkout experience lacked sales tax infrastructure, return policy visibility, and the trust signals that mature e-commerce storefronts provide.&lt;/p&gt;

&lt;p&gt;The pivot, detailed by Digital Commerce 360 on March 24, shifts where checkout &lt;em&gt;renders&lt;/em&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dedicated retailer apps inside ChatGPT (Instacart, Etsy, Shopify, Walmart)&lt;/li&gt;
&lt;li&gt;Product discovery and comparison happens in-chat&lt;/li&gt;
&lt;li&gt;Purchase completion happens on the merchant's own storefront (in-app browser on mobile, separate tab on desktop)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The commentary at the time framed this as a demotion. Roger Dunn wrote that "ACP's role shifts from universal long-tail merchant connector to plumbing for deep, bespoke retailer partnerships." Ken Huang's Substack went further: "Google's UCP Just Won Agentic Commerce."&lt;/p&gt;

&lt;p&gt;Then OpenAI shipped the 2026-04-17 spec release, and the demotion thesis fell apart.&lt;/p&gt;

&lt;h2&gt;
  
  
  What ACP actually shipped after the pivot
&lt;/h2&gt;

&lt;p&gt;The 2026-04-17 release is the largest in ACP's history. It added:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Carts&lt;/strong&gt; (&lt;code&gt;rfc.cart&lt;/code&gt;) - merchant-hosted, pre-checkout basket building&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Orders&lt;/strong&gt; (&lt;code&gt;rfc.orders&lt;/code&gt;) - a full order lifecycle with fulfillments and adjustments&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A public product feed spec&lt;/strong&gt; (&lt;code&gt;rfc.product_feeds&lt;/code&gt;) - the feed model is now public&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;/.well-known/acp.json&lt;/code&gt;&lt;/strong&gt; (&lt;code&gt;rfc.discovery&lt;/code&gt;) - ACP's own discovery document, structurally the same move UCP made with &lt;code&gt;/.well-known/ucp&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Delegate authentication&lt;/strong&gt; (&lt;code&gt;rfc.delegate_authentication&lt;/code&gt;) - 3DS2 browser-based auth delegation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MCP transport&lt;/strong&gt; - an OpenRPC binding so agents can speak ACP over MCP&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mandatory idempotency keys&lt;/strong&gt;, marketing consent, decimal quantities&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Carts and orders are not discovery primitives. They are the transaction layer. A protocol that ships a cart model, an order lifecycle and delegated authentication six weeks after "retreating from checkout" is not retreating from checkout - it is moving the checkout UI to a surface merchants already control while keeping the protocol underneath.&lt;/p&gt;

&lt;h2&gt;
  
  
  The protocol math - corrected
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Before the pivot (January - February 2026):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;UCP: Discovery + browsing + cart + checkout, completed in the agent surface&lt;/li&gt;
&lt;li&gt;ACP: Discovery + Instant Checkout completed inside ChatGPT&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;After the pivot and the April spec release:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;UCP (latest &lt;strong&gt;2026-04-08&lt;/strong&gt;): Discovery + browsing + cart + checkout, agent-completed, identity linking, payment handlers&lt;/li&gt;
&lt;li&gt;ACP (latest &lt;strong&gt;2026-04-17&lt;/strong&gt;): Discovery + feeds + cart + orders + delegated payment, with the final purchase step rendered on the merchant surface&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The protocols are converging structurally, not splitting into layers. Both now have a &lt;code&gt;.well-known&lt;/code&gt; discovery document. Both have a cart model. Both have an orders/checkout lifecycle. They differ in architecture - UCP starts from a JSON manifest agents parse, ACP is a server-to-server REST API the agent calls - and in who completes the final step.&lt;/p&gt;

&lt;p&gt;The clearest evidence that "pick the winner" is the wrong frame: &lt;strong&gt;Microsoft Copilot is deliberately dual-protocol.&lt;/strong&gt; It added UCP feeds and UCP-powered checkout in April 2026 while continuing to consume ACP-side Shopify Catalog data. It did not switch. It added.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this means for developers building today
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Implement UCP if you want agent-completed checkout.&lt;/strong&gt; It's co-created by Google and Shopify with 20+ partners, and it's what Google AI Mode, Gemini, Google Maps, YouTube Shopping and Microsoft Copilot use to discover and transact with stores. The full capability set - catalog browsing, cart, identity linking, payment handlers - is agent-side.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Implement ACP if you want ChatGPT reach - and treat it as a transaction protocol, not a billboard.&lt;/strong&gt; Feed quality decides whether ChatGPT surfaces your products at all, and the checkout endpoints plus delegated payment decide whether the purchase completes. Payment is an open delegated model: Stripe first, Adyen in the provider enums, PayPal building.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you have reach ambitions, the honest answer is both.&lt;/strong&gt; That is what the dual-protocol merchants are doing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The real implementation gap isn't protocol choice.&lt;/strong&gt; It's quality. Most deployed UCP profiles fail at basic validation levels:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Missing &lt;code&gt;signing_keys&lt;/code&gt; means agents can't verify your manifest&lt;/li&gt;
&lt;li&gt;Namespace/origin mismatches break discovery before it starts&lt;/li&gt;
&lt;li&gt;Incomplete Cart capability definitions create failed add-to-cart experiences&lt;/li&gt;
&lt;li&gt;Identity Linking (the stable spec) is the least-implemented capability despite being the most valuable for cross-domain user recognition&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At UCPtools, we scan thousands of profiles. The average score sits well below what AI agents consider reliable. Being "detected" is not the same as being "buyable."&lt;/p&gt;

&lt;h2&gt;
  
  
  What to build
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Implement UCP on your primary store domain first.&lt;/strong&gt; The &lt;code&gt;.well-known/ucp&lt;/code&gt; manifest is table stakes. Make sure it validates at all four levels: structural JSON validity, business rules consistency, network reachability, and SDK-level agent simulation.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Don't skip Identity Linking.&lt;/strong&gt; It's the stable spec that enables cross-domain user recognition. When a user browses your store on one device and returns via an AI agent on another, Identity Linking is what connects those sessions. Note that the 2026-04-08 UCP revision declares it via &lt;code&gt;config.scopes&lt;/code&gt; with PKCE S256 mandatory - validate against whichever version your profile declares.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Treat ACP as a full protocol surface.&lt;/strong&gt; Publish a clean product feed, stand up the checkout endpoints, and make sure &lt;code&gt;/.well-known/acp.json&lt;/code&gt; is reachable. The handoff to your storefront is a UX decision by OpenAI, not a reason to implement half the protocol.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Monitor continuously.&lt;/strong&gt; Protocols evolve - this article is itself an example of how fast a confident read can go stale. Your profile that validated perfectly in April might fail in June when a capability gets promoted from draft to stable. Continuous validation catches regressions before they cost you AI agent traffic.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The bottom line
&lt;/h2&gt;

&lt;p&gt;There is no protocol war with a winner. ACP moved its checkout surface and then expanded its transaction layer. UCP shipped its own April revision. Microsoft runs both on purpose.&lt;/p&gt;

&lt;p&gt;The question for e-commerce developers isn't which protocol to bet on - it's whether your implementation of either one is good enough for an AI agent to actually complete a purchase.&lt;/p&gt;

&lt;p&gt;Most aren't.&lt;/p&gt;

&lt;p&gt;Validate your UCP profile at &lt;a href="https://ucptools.dev?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202605" rel="noopener noreferrer"&gt;https://ucptools.dev?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202605&lt;/a&gt; - the free tier runs all four validation levels and simulates a real AI agent interaction against your store. Check your ChatGPT/ACP readiness at &lt;a href="https://ucptools.dev/acp-checker" rel="noopener noreferrer"&gt;https://ucptools.dev/acp-checker&lt;/a&gt;. If an AI agent can't buy from you, your customers can't either.&lt;/p&gt;

</description>
      <category>agenticcommerce</category>
      <category>ucp</category>
      <category>acp</category>
      <category>ecommerce</category>
    </item>
  </channel>
</rss>
