# How to publish a UCP profile, step by step

What goes in a UCP profile at /.well-known/ucp, how to serve it and let agents reach it, with a builder that checks your draft against the 2026-08-25 schema.

By Valerii Checha, 4 Nov 2026

How do you publish a UCP profile? Put a JSON document at `/.well-known/ucp` on your store's domain with the three members the spec requires, `ucp.version`, `ucp.services` and `ucp.payment_handlers`, and serve it over HTTPS with no redirect and a public `Cache-Control` of at least 60 seconds. On Shopify the platform publishes one for you. Elsewhere you write it, check it against the release's schema, and keep it in step with what your store really offers.

This guide goes through the profile one member at a time, with a builder that checks your draft against the UCP 2026-08-25 schema in this page, a check for the headers your server sends and a robots.txt test. Storewalk is ours: we build the scan, and its UCP checks, from [DISC-018](https://storewalk.ai/checks/DISC-018) to [TRX-012](https://storewalk.ai/checks/TRX-012), are ours. The specification and the platforms are quoted from their own pages, read on 4 November 2026. For what UCP is and how it compares with ACP, read [UCP explained](https://storewalk.ai/blog/ucp-explained) first, and for the five walls an agent meets on the way to a sale, [the hub article](https://storewalk.ai/blog/why-agents-cant-buy).

## What is a UCP profile, and why publish one?

It is the page of your store written for agents. UCP, the Universal Commerce Protocol, says "Businesses publish their profile at /.well-known/ucp", and an agent that speaks UCP reads that one address to learn which transports you offer (an MCP server, a REST API), which capabilities sit behind them (checkout, cart, catalogue search, orders) and which payment handlers you accept. Without it, a UCP agent has to guess from your HTML, which is a bit like reading the menu through the kitchen window.

The current release is 2026-08-25: UCP's `versions.json` lists it with the alias `latest`, above 2026-04-08, 2026-01-23 and 2026-01-11.

## Does your platform publish it for you?

On Shopify, yes. Shopify's developer changelog of 4 September 2026 says "Shopify storefronts now advertise support for Universal Commerce Protocol (UCP) version 2026-08-25 in their discovery profiles at /.well-known/ucp", and that the change "doesn't require any changes to your existing integrations". Shopify's `agents` Liquid object carries "auto-populated UCP and agent-interaction metadata for the store", the profile's address among it. What stays yours on Shopify is the robots.txt section below.

On WooCommerce, the WooCommerce AI plugin serves UCP once you turn on its agentic commerce setting, which "is off by default", and its documentation says "The plugin advertises UCP version 2026-04-08". That is an older release than the one this guide builds, and our schema check only knows the 2026-08-25 schema, so it skips that profile instead of judging it by rules it never claimed. BigCommerce and Adobe Commerce have announced plans and no profile we could read; the [BigCommerce](https://storewalk.ai/bigcommerce) and [Magento](https://storewalk.ai/magento) guides have their words. On any other platform you write the file by hand.

## What goes in a UCP profile?

A profile has four parts, and the spec requires three. The overview says "For both business and platform profiles, ucp.version, ucp.services, and ucp.payment_handlers are required", that "The services and payment_handlers registries MUST be present even when empty", and that "ucp.capabilities is optional and MAY be omitted, though useful commerce profiles normally advertise at least one capability".

- `ucp.version` is the release your profile follows, `2026-08-25`.
- `ucp.services` says how an agent reaches you, under `dev.ucp.shopping`, one entry per transport. An MCP or REST entry needs an `endpoint`, and the spec's rules for it are "endpoint MUST be a valid URL with scheme (https)" and "endpoint SHOULD NOT have a trailing slash".
- `ucp.capabilities` says what an agent may do there, keyed by name, such as `dev.ucp.shopping.checkout`. In a business profile each one needs a `schema` URL, so agents can fetch the rules for it.
- `ucp.payment_handlers` says how an agent may pay, keyed by the handler's namespace, each entry with an `id` and a `version`.

Build yours here. The addresses are an invented store's; replace them with your own.

A UCP profile builder, checked against the 2026-08-25 schema in this page.

On the page, you set your MCP and REST addresses, the capabilities you offer, a payment handler and signing keys, and the profile is checked against the UCP 2026-08-25 business profile schema in the browser. For an invented store, with RFC 8037’s example key:

```json
{
  "ucp": {
    "version": "2026-08-25",
    "services": {
      "dev.ucp.shopping": [
        {
          "version": "2026-08-25",
          "spec": "https://ucp.dev/2026-08-25/specification/overview/",
          "transport": "mcp",
          "endpoint": "https://northwind-linen.example.com/api/ucp/mcp",
          "schema": "https://ucp.dev/2026-08-25/services/shopping/mcp.openrpc.json"
        }
      ]
    },
    "capabilities": {
      "dev.ucp.shopping.checkout": [
        {
          "version": "2026-08-25",
          "spec": "https://ucp.dev/2026-08-25/specification/shopping/checkout/",
          "schema": "https://ucp.dev/2026-08-25/schemas/shopping/checkout.json"
        }
      ],
      "dev.ucp.shopping.cart": [
        {
          "version": "2026-08-25",
          "spec": "https://ucp.dev/2026-08-25/specification/shopping/cart/",
          "schema": "https://ucp.dev/2026-08-25/schemas/shopping/cart.json"
        }
      ],
      "dev.ucp.shopping.catalog.search": [
        {
          "version": "2026-08-25",
          "spec": "https://ucp.dev/2026-08-25/specification/shopping/catalog/",
          "schema": "https://ucp.dev/2026-08-25/schemas/shopping/catalog_search.json"
        }
      ],
      "dev.ucp.shopping.catalog.lookup": [
        {
          "version": "2026-08-25",
          "spec": "https://ucp.dev/2026-08-25/specification/shopping/catalog/",
          "schema": "https://ucp.dev/2026-08-25/schemas/shopping/catalog_lookup.json"
        }
      ]
    },
    "payment_handlers": {
      "com.google.pay": [
        {
          "id": "gpay",
          "version": "2026-01-11",
          "spec": "https://pay.google.com/gp/p/ucp/2026-01-11/",
          "schema": "https://pay.google.com/gp/p/ucp/2026-01-11/schemas/config.json"
        }
      ]
    }
  },
  "keys": [
    {
      "kid": "kPrK_qmxVWaYVA9wwBF6Iuo3vVzz7TxHCTwXBygrS4k",
      "kty": "OKP",
      "crv": "Ed25519",
      "alg": "EdDSA",
      "x": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo"
    }
  ]
}
```

Against the schema: valid. What our UCP checks would say (ours):

- DISC-018: PASS, valid, with no warnings.
- DISC-019: PASS, 2026-08-25 is a known release.
- DISC-020: PASS, 1 service entry, well formed.
- DISC-021: PASS 83, checkout, catalog, cart declared; missing order.
- TRX-011: PASS, 1 payment handler, with an id and a version.
- TRX-012: PASS, 1 well-formed public key.

* Not yet: Google Pay’s own schema requires a config (api_version, api_version_minor, merchant_info, allowed_payment_methods) from your Google Pay setup; add it before you publish.

Sources: [UCP 2026-08-25: specification overview](https://ucp.dev/2026-08-25/specification/overview/), [UCP 2026-08-25: profile.json](https://ucp.dev/2026-08-25/schemas/profile.json), [Google Pay: UCP payment handler 2026-01-11](https://pay.google.com/gp/p/ucp/2026-01-11/) and [RFC 8037, Appendix A (example Ed25519 key)](https://www.rfc-editor.org/rfc/rfc8037#appendix-A.2).

Signing keys go at the top of the document, beside `ucp`: "When a profile publishes signing keys, they MUST appear in the top-level keys\[] array". A payment handler also has rules of its own on top of UCP's. Google Pay's handler schema requires `api_version`, `api_version_minor`, `merchant_info` and `allowed_payment_methods` in its `config`, which come from your Google Pay setup, so the builder leaves that to you and says so.

## How do you check the profile before you publish it?

Validate it against the schema of the release it declares. The builder above does that in your browser with the same schema files our scan uses, copied unmodified from ucp.dev (the `profile.json` we hold matched the live file byte for byte on 4 November 2026), so a draft that is valid here is valid to our schema check too. The UCP project's own validator is `ucp-schema`, a command-line tool you install with `cargo install ucp-schema`; its README says you can name a schema definition directly, "a profile's business_schema" among them.

If you declare anything outside `dev.ucp.*`, a vendor capability or your own handler, check where its schema lives. The overview binds names to hosts: "An entity's schema is served from a host whose reversed labels either equal its name or are a label-aligned prefix of it." So `com.example.pay` can live on `example.com` or `pay.example.com`, and not on a shared CDN. A schema anywhere else gets the entity ignored: the platform "MUST NOT fetch it and MUST reject the entity".

## How should your server serve the profile?

The overview sets the rules: "Published artifacts MUST be served over HTTPS. Profile endpoints MUST NOT use redirects (3xx). Published artifacts MUST include a Cache-Control header with public and max-age of at least 60 seconds, and MUST NOT be served with private, no-store, or no-cache directives." It adds that artifacts "SHOULD include a validator (ETag or Last-Modified)". Agents keep their side of it: implementations "MUST NOT follow redirects (3xx)", so a profile behind a redirect is, to a strict agent, no profile at all.

Run `curl -sIL https://your-store/.well-known/ucp` and paste what it prints:

How your server answers for /.well-known/ucp: the spec’s rules and our reading.

On the page, you paste the response headers your server sends for /.well-known/ucp, as curl -sIL prints them, and see the spec's serving rules and how our scan reads the answer. The examples:

Served right:

```http
HTTP/2 200
content-type: application/json
cache-control: public, max-age=300
etag: "v7"
content-length: 2140
```

- Yes: Served over HTTPS.
- Yes: No redirect (3xx) on the way to the profile.
- Yes: Cache-Control has public and max-age of at least 60, and no private, no-store or no-cache.
- Yes: An ETag or Last-Modified header, so agents can revalidate (a SHOULD).
- Cache-Control sent: public, max-age=300.
- DISC-018: PASS, no serving warnings (the document itself is graded too).

Redirects to www:

```http
HTTP/2 301
location: https://www.northwind-linen.example.com/.well-known/ucp

HTTP/2 200
content-type: application/json
cache-control: public, max-age=300
```

- Yes: Served over HTTPS.
- No: No redirect (3xx) on the way to the profile.
- Yes: Cache-Control has public and max-age of at least 60, and no private, no-store or no-cache.
- No: An ETag or Last-Modified header, so agents can revalidate (a SHOULD).
- Cache-Control sent: public, max-age=300.
- DISC-018: WARN, warns PROFILE_REDIRECTED.

Not cacheable:

```http
HTTP/2 200
content-type: application/json
cache-control: private, no-store
```

- Yes: Served over HTTPS.
- Yes: No redirect (3xx) on the way to the profile.
- No: Cache-Control has public and max-age of at least 60, and no private, no-store or no-cache.
- No: An ETag or Last-Modified header, so agents can revalidate (a SHOULD).
- Cache-Control sent: private, no-store.
- DISC-018: PASS, no serving warnings (the document itself is graded too).

Served as text:

```http
HTTP/1.1 200 OK
content-type: text/plain; charset=utf-8
cache-control: max-age=30
```

- Yes: Served over HTTPS.
- Yes: No redirect (3xx) on the way to the profile.
- No: Cache-Control has public and max-age of at least 60, and no private, no-store or no-cache.
- No: An ETag or Last-Modified header, so agents can revalidate (a SHOULD).
- Cache-Control sent: max-age=30.
- DISC-018: WARN, warns MEDIA_TYPE (served as text/plain, not application/json, our own rule).

Not there:

```http
HTTP/2 404
content-type: text/html; charset=utf-8
```

- Yes: Served over HTTPS.
- Yes: No redirect (3xx) on the way to the profile.
- No: Cache-Control has public and max-age of at least 60, and no private, no-store or no-cache.
- No: An ETag or Last-Modified header, so agents can revalidate (a SHOULD).
- Cache-Control sent: none.
- DISC-018: SKIPPED, no profile to read, and so do the other UCP checks.
- DISC-007: FAIL, if no other discovery document (ACP, MCP) answers either.

None of our checks reads Cache-Control or ETag today, so the cache rules are between you and the spec.

Source: [UCP 2026-08-25: specification overview](https://ucp.dev/2026-08-25/specification/overview/).

A static JSON file makes those rules easy to meet. If your store answers on both `example.com` and `www.example.com`, serve the same profile on both without one redirecting to the other: our [DISC-024](https://storewalk.ai/checks/DISC-024) asks the other host and warns on a redirect. Sixty seconds of caching is the spec's polite way of asking agents not to fetch your profile on every request.

## Can agents reach the profile through robots.txt?

A robots.txt rule written to keep bots out of `/.well-known/`, or out of the whole site, keeps out the assistants that would read your profile for a shopper. Our [DISC-023](https://storewalk.ai/checks/DISC-023) asks your robots.txt about `/.well-known/ucp` for our own token and for each agent on our list that acts for a buyer, and grades the share it allows. Our scan obeys the same file: if it disallows Storewalk, we do not fetch the profile.

Does your robots.txt let agents read the UCP profile?.

On the page, you paste your robots.txt and see, for our own token and each agent on our list that acts for a buyer, whether it may read /.well-known/ucp, and what our DISC-023 says. The examples:

A typical store file:

```text
User-agent: *
Disallow: /admin
Disallow: /cart
Disallow: /checkout
Disallow: /account

Sitemap: https://northwind-linen.example.com/sitemap.xml
```

- Every agent may read it.
- DISC-023: PASS, all 8 agents may read /.well-known/ucp.

Blocks /.well-known/:

```text
User-agent: *
Disallow: /.well-known/
Disallow: /checkout
```

- Disallowed: Storewalk, ChatGPT-User, chatgpt.com, Claude-User, Perplexity-User, Google-Agent, Amzn-User, meta-externalfetcher.
- DISC-023: FAIL 0, 8 of 8 agents are kept off /.well-known/ucp.

Blocks it, then allows UCP:

```text
User-agent: *
Disallow: /.well-known/
Allow: /.well-known/ucp
```

- Every agent may read it.
- DISC-023: PASS, all 8 agents may read /.well-known/ucp.

Turns away assistants:

```text
User-agent: ChatGPT-User
User-agent: Perplexity-User
User-agent: Claude-User
Disallow: /

User-agent: *
Disallow: /checkout
```

- Disallowed: ChatGPT-User, Claude-User, Perplexity-User.
- DISC-023: WARN 63, 3 of 8 agents are kept off /.well-known/ucp.

Source: [Our check: robots.txt and the UCP profile (DISC-023)](https://storewalk.ai/checks/DISC-023).

The fix for the second example is a single `Allow: /.well-known/ucp` line in the same group, as the third example shows. The [robots.txt guide](https://storewalk.ai/guides/robots-txt-for-ai-agents) covers the rest of the file.

## Do you need signing keys and older versions?

Not to start. The overview says profiles "MAY include public JSON Web Keys used for HTTP Message Signatures and signed webhooks", in our words keys an agent or a platform can use to check that a message really came from you. The schema is strict about what a key may hold: "private key material MUST NOT appear in a profile", and "For keys used in dual-audience (Web Bot Auth) signatures, the kid MUST be the key's JWK SHA-256 Thumbprint (RFC 7638)". The builder's example is the public Ed25519 key printed in RFC 8037, with its published thumbprint as the `kid`; it is a textbook example, so swap in your own.

If you keep serving agents on an older release, the overview says businesses "SHOULD include a supported_versions object mapping each older version to a version-specific profile URI". One profile for the current release is enough until you have a reason for two.

## What do our checks read, and where do they stop?

[DISC-007](https://storewalk.ai/checks/DISC-007) looks for the profile among the agent commerce discovery documents, and fails a store that publishes none of them. Without a profile every other UCP check skips, so one missing file counts once. When the profile is there, [DISC-018](https://storewalk.ai/checks/DISC-018) validates it against the 2026-08-25 schema and adds our own warnings, among them a redirect, a media type other than `application/json`, and an empty payment registry. [DISC-019](https://storewalk.ai/checks/DISC-019) checks the version is a known release, [DISC-020](https://storewalk.ai/checks/DISC-020) the services, [DISC-021](https://storewalk.ai/checks/DISC-021) the capabilities (checkout counts for half), [DISC-022](https://storewalk.ai/checks/DISC-022) that the spec and schema links resolve, [TRX-011](https://storewalk.ai/checks/TRX-011) the payment handlers and [TRX-012](https://storewalk.ai/checks/TRX-012) the keys. [TRX-013](https://storewalk.ai/checks/TRX-013) calls the MCP server the profile names, which the [MCP guide](https://storewalk.ai/guides/add-an-mcp-server-to-your-store) covers.

Some of our rules are stricter than the spec, and some of the spec is beyond us:

- TRX-012 fails a profile with no keys, though the spec makes keys optional. It has the lowest weight we give, 1, as most of the UCP checks do.
- DISC-020 fails an empty services registry, which the schema allows, because an agent can do nothing through a store with no service.
- DISC-018 validates only 2026-08-25 profiles, and skips other releases.
- None of our checks reads `Cache-Control` or `ETag`.

## Frequently asked questions

### Where does a UCP profile go?

At `/.well-known/ucp` on your store's own domain. The spec says "Businesses publish their profile at /.well-known/ucp".

### What must a UCP profile contain?

`ucp.version`, `ucp.services` and `ucp.payment_handlers`. The two registries must be there even when empty, and capabilities are optional, though a profile without them gives an agent little to do.

### Can I redirect /.well-known/ucp to another address?

No. The spec says "Profile endpoints MUST NOT use redirects (3xx)", and agents "MUST NOT follow redirects". Serve the file at the address itself, on every host your store answers on.

### Does Shopify publish a UCP profile for my store?

Shopify's changelog says Shopify storefronts "advertise support for Universal Commerce Protocol (UCP) version 2026-08-25 in their discovery profiles at /.well-known/ucp". Check that your robots.txt lets agents read it.

### How do I validate a UCP profile?

Against the schema of the release it declares: with the UCP project's `ucp-schema` tool, or in the builder on this page, which uses the 2026-08-25 schema files. The [agent readiness checker](https://storewalk.ai/) on our homepage also runs the UCP checks on the live file.

The sources below were last checked on the date shown.

## Sources

- [UCP 2026-08-25: specification overview](https://ucp.dev/2026-08-25/specification/overview/)
- [UCP 2026-08-25: profile.json](https://ucp.dev/2026-08-25/schemas/profile.json)
- [UCP: versions.json](https://ucp.dev/versions.json)
- [UCP: ucp-schema validator](https://github.com/Universal-Commerce-Protocol/ucp-schema)
- [Google Pay: UCP payment handler 2026-01-11](https://pay.google.com/gp/p/ucp/2026-01-11/)
- [RFC 8037: CFRG curves in JOSE](https://www.rfc-editor.org/rfc/rfc8037)
- [Shopify developer changelog: storefronts support UCP 2026-08-25](https://shopify.dev/changelog/08-25-is-now-supported)
- [Shopify.dev: agents.md.liquid](https://shopify.dev/docs/storefronts/themes/architecture/templates/agents-md-liquid)
- [WooCommerce AI: UCP REST API](https://woocommerce.com/document/woocommerce-ai/ucp-rest-api/)
- [WooCommerce AI documentation](https://woocommerce.com/document/woocommerce-ai/)

Last checked 4 Nov 2026
