# UCP Profile for Magento 2  -  module reference

> Publish a signed Universal Commerce Protocol profile at /.well-known/ucp in Magento 2 (spec 2026-08-25). Keys, capabilities, signatures, CLI.

Source: https://angeo.dev/docs/modules/ucp/
Updated: 2026-10-17

Module reference

# UCP Profile for Magento 2  -  module reference

Covers `angeo/module-ucp` 2.2.1 - Checked 16 October 2026 - MIT licensed

**UCP Profile** publishes a [Universal Commerce Protocol](https://ucp.dev) business profile at `/.well-known/ucp`. AI shopping agents  -  Google AI Mode and Gemini, and other UCP platforms  -  read it to learn what your store can do and where to call it.

The profile follows UCP spec **2026-08-25** and its official JSON Schemas. On its own, this module only *describes* the store. The modules that *serve* the capabilities are [UCP Catalog](https://angeo.dev/docs/modules/ucp-catalog/) (search and lookup) and [UCP Checkout](https://angeo.dev/docs/modules/ucp-checkout/) (checkout and orders).

Package`angeo/module-ucp`Latest version2.2.1 (released 2026-10-14)Magento2.4.6+PHP8.1 - 8.5LicenseMIT, freeLinks[GitHub](https://github.com/angeo-dev/module-ucp) - [Packagist](https://packagist.org/packages/angeo/module-ucp) - [Changelog](https://github.com/angeo-dev/module-ucp/blob/main/CHANGELOG.md) - [Module page](https://angeo.dev/modules/ucp/)

**On this page**

1. What it does
2. How the endpoint is served
3. Requirements
4. Installation
5. Capabilities: declared and served
6. Signing keys
7. Inbound signature verification (2.1.0)
8. Configuration reference
9. Upgrading from 1.x
10. FAQ
11. Related

## What it does

- Builds the profile: services, capabilities, extensions, payment handlers, supported versions and public keys.
- Publishes signing keys in the top-level **`keys[]`** JWK Set (RFC 7517), **public keys only**. ES256, ES384 and Ed25519 are supported, with RFC 7638 thumbprint key ids and rotation without downtime.
- Checks **authority binding** locally. Since 2026-08-25 a platform must reject an entity whose `schema` host does not match its namespace  -  and it does so silently. `angeo:ucp:validate` runs the same algorithm, so you find out first.
- Advertises REST by default and, optionally, an MCP binding.
- Verifies RFC 9421 signatures on requests that arrive at your UCP endpoints (2.1.0).

## How the endpoint is served

`/.well-known/ucp` is served by a PHP controller, not a static file. The spec needs `Content-Type: application/json` and CORS headers, which a static file without an extension cannot send without web server changes.

The stock Magento nginx config ends with `try_files $uri $uri/ /index.php$is_args$args`, so the request reaches Magento and a custom router answers it. No nginx or Apache change is needed.

If your host added a rule such as `location ~ /\. { deny all; }`, it blocks all dot-paths. Then add a one-line allow for `^~ /.well-known/`. Do **not** leave a static file at `pub/.well-known/ucp`: nginx would serve it first, with the wrong type and no CORS.

The controller sends CORS headers, `Cache-Control: public, max-age=300`, `X-Content-Type-Options: nosniff`, `X-Frame-Options: DENY` and `Referrer-Policy: no-referrer`. It answers `OPTIONS` with `204`. When the module is off, it returns `404`, so the store does not advertise UCP.

## Requirements

| Magento | 2.4.6+ (Open Source, Mage-OS or Adobe Commerce)  -  `magento/framework >=103.0.6 <104` |
| PHP | 8.1 - 8.5  -  `~8.1.0||~8.2.0||~8.3.0||~8.4.0||~8.5.0` |
| PHP extensions | `curl`, `json`, `openssl` |

## Installation

```
composer require angeo/module-ucp
bin/magento module:enable Angeo_Ucp
bin/magento setup:upgrade
bin/magento setup:di:compile                # production mode only
bin/magento cache:flush
```

Generate a signing key and check the endpoint:

```
bin/magento angeo:ucp:keys:generate
curl -sI https://yourstore.com/.well-known/ucp
# HTTP/2 200
# content-type: application/json
# access-control-allow-origin: *
# cache-control: public, max-age=300
```

## Capabilities: declared and served

You switch capabilities on in **Stores -> Configuration -> Angeo -> UCP (Universal Commerce Protocol)**. Since 2.2.0, a switch alone does not advertise cart, checkout, order, fulfillment, discount or buyer consent. They appear in the profile only when an installed module serves them:

| Capability | Served by |

| `dev.ucp.shopping.catalog.search` / `.lookup` | [UCP Catalog](https://angeo.dev/docs/modules/ucp-catalog/) |
| `dev.ucp.shopping.checkout` + `.discount` + `.fulfillment` | [UCP Checkout](https://angeo.dev/docs/modules/ucp-checkout/) 1.0+ (MCP tools: [UCP Checkout over MCP](https://angeo.dev/docs/modules/ucp-checkout-mcp/)) |
| `dev.ucp.shopping.order` (Get Order and webhooks) | UCP Checkout 1.1+ |
| `dev.ucp.shopping.permalink` | A stock Magento cart URL |
| Cart, buyer consent | Not available yet |

`bin/magento angeo:ucp:validate` lists switches that are on but have no effect. A module registers what it serves through the `implementations` argument of `Angeo\Ucp\Model\Config` in its `di.xml`.

**REST endpoint.** Leave *Transport -> REST Endpoint URL* empty to advertise `{baseUrl}/ucp/v1`, where the catalog and checkout modules answer.

## Signing keys

| Command | What it does |

| `bin/magento angeo:ucp:keys:generate` | Generate a UCP signing keypair. The public JWK is saved to config |
| `bin/magento angeo:ucp:validate` | Validate the generated UCP profile against the |

Which key type to choose:

- **ES256** (default)  -  accepted by every counterparty. AP2 mandate signing needs it. Start here.
- **Ed25519**  -  recommended by the spec for Web Bot Auth on HTTP. Needs `ext-sodium`. It does not cover AP2, so publish both if you sign mandates.
- **ES384**  -  for a stricter curve policy.

Rotate in this order, so there is no gap:

```
bin/magento angeo:ucp:keys:generate --add    # publish the new key next to the old one
# move signing to the new key id, let caches expire
bin/magento angeo:ucp:keys:generate --force  # keep only the newest key
```

The private key never leaves the server. The profile reads public keys only. If a private JWK member (`d`, `p`, `q`, `dp`, `dq`, `qi`, `oth`, `k`) is found in stored config, it is removed before serving, with a warning to rotate.

## Inbound signature verification (2.1.0)

The profile publishes keys. Since 2.1.0 the module also checks RFC 9421 signatures on requests that arrive at your UCP endpoints. Set the mode under **Request Security**:

| Mode | Signed and valid | Signed and invalid | Unsigned |

| `disabled` (default) | served | served | served |
| `optional` | served | **401** | served |
| `required` | served | **401** | **401** |

Start with `optional`. Invalid signatures are refused, but missing ones are allowed, so you can see in the log which agents sign. Endpoint modules use this through `Api\SignatureVerifierInterface`. Signing your own *responses* is not done yet.

## Configuration reference

All settings are under **Stores -> Configuration -> Angeo -> UCP (Universal Commerce Protocol)**. The table shows each field, its config path (for `bin/magento config:set`) and its default value.

General (1)

| Setting | Config path | Default |

| Advertise UCP Profile | `angeo_ucp/general/enabled` | No |

Declared Root Capabilities (9)

| Setting | Config path | Default |

| Catalog Search (dev.ucp.shopping.catalog.search) | `angeo_ucp/capabilities/catalog_search_enabled` | No |
| Catalog Lookup (dev.ucp.shopping.catalog.lookup) | `angeo_ucp/capabilities/catalog_lookup_enabled` | No |
| Cart (dev.ucp.shopping.cart) | `angeo_ucp/capabilities/cart_enabled` | No |
| Checkout (dev.ucp.shopping.checkout) | `angeo_ucp/capabilities/checkout_enabled` | No |
| Order (dev.ucp.shopping.order) | `angeo_ucp/capabilities/order_enabled` | No |
| Identity Linking (dev.ucp.common.identity_linking) | `angeo_ucp/capabilities/identity_linking_enabled` | No |
| Identity Linking OAuth Scopes | `angeo_ucp/capabilities/identity_linking_scopes` |  -  |
| Permalink (dev.ucp.shopping.permalink) | `angeo_ucp/capabilities/permalink_enabled` | No |
| Permalink Endpoint URL | `angeo_ucp/capabilities/permalink_endpoint` |  -  |

Declared Extensions (3)

| Setting | Config path | Default |

| Fulfillment (dev.ucp.shopping.fulfillment) | `angeo_ucp/extensions/fulfillment_enabled` | No |
| Discount (dev.ucp.shopping.discount) | `angeo_ucp/extensions/discount_enabled` | No |
| Buyer Consent (dev.ucp.shopping.buyer_consent) | `angeo_ucp/extensions/buyer_consent_enabled` | No |

Transport (2)

| Setting | Config path | Default |

| REST Endpoint URL | `angeo_ucp/transport/rest_endpoint` |  -  |
| MCP Endpoint URL | `angeo_ucp/transport/mcp_endpoint` |  -  |

Signing Keys (1)

| Setting | Config path | Default |

| Public JWK Set (read-only) | `angeo_ucp/keys/signing_key_jwk` |  -  |

Advanced Profile Declarations (2)

| Setting | Config path | Default |

| Supported Versions (JSON) | `angeo_ucp/advanced/supported_versions` |  -  |
| Payment Handlers (JSON) | `angeo_ucp/advanced/payment_handlers` |  -  |

Request Security (1)

| Setting | Config path | Default |

| Inbound Signature Verification | `angeo_ucp/security/inbound_signature_mode` | `disabled` |

## Upgrading from 1.x

1.4.0 still works: it publishes a valid `2026-04-08` profile. 2.x adopts `2026-08-25`, where the protocol version, the key field and the capability URLs move together.

| | 1.x (2026-04-08) | 2.x (2026-08-25) |

| Signing keys | `signing_keys[]` | **`keys[]`** (JWK Set) |
| Capability `schema` | optional | **required** |
| `schema` origin check | prefix on `ucp.dev` | authority-binding algorithm |
| Key types | P-256 only | P-256, P-384, Ed25519 |
| Key id | random | RFC 7638 thumbprint |

```
composer require angeo/module-ucp:^2.0
bin/magento setup:upgrade && bin/magento cache:flush
bin/magento angeo:ucp:validate
```

Existing keys keep working and move to `keys[]`. If a platform already integrates with your old profile, save a copy and declare it under *Advanced -> Supported Versions*.

## FAQ

### Is the profile public?

Yes. The UCP spec requires a public, unauthenticated profile. Never put secrets, internal URLs or admin contacts in it.

### Why does my profile not list checkout even though I switched it on?

Since 2.2.0 a capability appears only when an installed module serves it. Install UCP Checkout, then run angeo:ucp:validate.

### Do I need to change nginx?

Not with the stock Magento config. Only if your host added a rule that blocks paths starting with a dot. Then allow /.well-known/.

### Which key type should I use?

Start with ES256. Add Ed25519 if you want Web Bot Auth interop. Publish both if you sign AP2 mandates.

### My CDN still shows the old profile. Why?

The profile is cacheable for five minutes. Purge /.well-known/ucp on your CDN or Varnish after rotating keys or changing settings.

## Related

- [UCP Catalog](https://angeo.dev/docs/modules/ucp-catalog/)  -  catalog.search and catalog.lookup endpoints behind the UCP profile
- [UCP Checkout](https://angeo.dev/docs/modules/ucp-checkout/)  -  UCP checkout sessions over REST; the buyer pays on your own checkout
- [UCP Checkout over MCP](https://angeo.dev/docs/modules/ucp-checkout-mcp/)  -  the same UCP checkout as five MCP tools
- [MCP Server](https://angeo.dev/docs/modules/mcp-server/)  -  live, read-only catalog tools for AI agents over MCP
- [AI Agent Activity](https://angeo.dev/docs/modules/agent-activity/)  -  report on AI agent calls, crawler visits, fake bots and agent-built orders
- [UCP for Magento 2  -  complete guide](https://angeo.dev/magento-ucp/)
- [UCP  -  definition](https://angeo.dev/ucp-definition/)
- [All module reference pages](https://angeo.dev/docs/modules/) - [Compatibility matrix](https://angeo.dev/docs/compatibility/)

This page was written from the `README.md`, `composer.json` and `etc/*.xml` files of [tag 2.2.1](https://github.com/angeo-dev/module-ucp/tree/2.2.1), read on 16 October 2026. Settings, commands and defaults come straight from that code. If a newer release is out, the README on GitHub wins.
