# How UCP Profiles Enable Permissionless Onboarding for Developers

> Learn how UCP profiles enable permissionless developer onboarding. Discover capabilities and verify identities via self-contained JSON documents without registration or API keys.

- Repository: [Universal Commerce Protocol (UCP)/ucp](https://github.com/Universal-Commerce-Protocol/ucp)
- Tags: how-to-guide
- Published: 2026-04-26

---

**UCP profiles are self‑contained JSON documents hosted at well‑known URLs that allow developers to discover capabilities and verify cryptographic identities without prior registration, API keys, or secret exchange.**

The Universal Commerce Protocol (UCP) eliminates traditional integration friction by treating **discovery profiles** as publicly accessible configuration files. Under the Universal‑Commerce‑Protocol/ucp specification, any developer can publish a static JSON file to begin accepting secure requests, while consumers can immediately verify identities and negotiate capabilities simply by fetching a URL.

## What Are UCP Discovery Profiles?

A **discovery profile** is a JSON document that describes a party’s cryptographic identity and commerce capabilities. According to [`source/discovery/profile_schema.json`](https://github.com/Universal-Commerce-Protocol/ucp/blob/main/source/discovery/profile_schema.json), the profile contains two critical sections: `signing_keys` (an array of JWK public keys) and `ucp.capabilities` (an object listing supported services). Because these profiles are served over HTTPS at standardized locations, they function as self‑sovereign identity documents that require no central registry.

## How Profile Hosting Eliminates Registration Barriers

The specification mandates that **business profiles** are served at `/.well‑known/ucp` on the merchant’s domain, while **platform profiles** are advertised via the `UCP‑Agent` request header ([[`docs/specification/overview.md`](https://github.com/Universal-Commerce-Protocol/ucp/blob/main/docs/specification/overview.md#L64)‑L70](/Universal-Commerce-Protocol/ucp/blob/main/docs/specification/overview.md#L64)). This design means:

- **No out‑of‑band coordination** is required to begin integration.
- **Any HTTP GET** to the well‑known URL returns the complete configuration.
- Developers publish a static file once, and the ecosystem discovers it automatically.

## Capability Advertisement Without Coordination

Profiles advertise supported operations through the `ucp.capabilities` object, defined in [[`profile_schema.json`](https://github.com/Universal-Commerce-Protocol/ucp/blob/main/profile_schema.json#L77)‑L89](/Universal-Commerce-Protocol/ucp/blob/main/source/discovery/profile_schema.json#L77). This object lists services such as `dev.ucp.shopping.checkout` along with version arrays. When a platform fetches a business profile, it can immediately determine the intersection of supported operations and begin requests without negotiating feature flags through support tickets or dashboard configurations.

## Cryptographic Trust Without Shared Secrets

Identity verification relies on **JWK public keys** embedded in the `signing_keys` array ([[`profile_schema.json`](https://github.com/Universal-Commerce-Protocol/ucp/blob/main/profile_schema.json#L58)‑L70](/Universal-Commerce-Protocol/ucp/blob/main/source/discovery/profile_schema.json#L58)). Because the profile itself serves as the trust anchor:

- Platforms verify HTTP Message Signatures against the published public key.
- No API secrets or OAuth client credentials are exchanged beforehand.
- Trust is bootstrapped from the HTTPS‑served JSON document alone.

## The Permissionless Discovery Flow

The onboarding sequence defined in [[`overview.md`](https://github.com/Universal-Commerce-Protocol/ucp/blob/main/overview.md#L66)‑L70](/Universal-Commerce-Protocol/ucp/blob/main/docs/specification/overview.md#L66) follows a **discover‑first, negotiate‑later** pattern:

1. **Advertisement**: The requesting platform includes its profile URI in the `UCP‑Agent` header.
2. **Fetch**: The receiving party extracts the URI, performs an HTTP GET, and validates the JSON schema.
3. **Verification**: The receiver extracts `signing_keys` to verify the request’s HTTP Message Signature and matches `ucp.capabilities` to determine viable endpoints.

This flow triggers automatically upon the first request, eliminating manual onboarding checklists.

## Implementation Examples

### Fetching Business Profiles in Python

The following example retrieves a merchant’s profile and lists advertised capabilities. It validates basic HTTPS requirements as mandated by the specification.

```python
import requests
import json

def fetch_profile(url: str) -> dict:
    """Retrieve a UCP profile; raise if HTTPS or caching rules are violated."""
    resp = requests.get(url, timeout=5)
    resp.raise_for_status()
    # Very light validation – real implementations should check the JSON schema.

    profile = resp.json()
    return profile

def list_capabilities(profile: dict):
    """Print the capabilities advertised by the profile."""
    caps = profile.get("ucp", {}).get("capabilities", {})
    for cap, versions in caps.items():
        versions_str = ", ".join(v.get("version", "?") for v in versions)
        print(f"{cap}: {versions_str}")

# Example: a merchant’s well‑known profile

merchant_profile_url = "https://merchant.example/.well-known/ucp"
merchant_profile = fetch_profile(merchant_profile_url)
print("Merchant capabilities:")
list_capabilities(merchant_profile)

```

*Key references*: The `ucp.capabilities` structure is defined in [`source/discovery/profile_schema.json`](https://github.com/Universal-Commerce-Protocol/ucp/blob/main/source/discovery/profile_schema.json) under `$defs/base`, and the well‑known path `/.well-known/ucp` is mandated in the discovery specification.

### Verifying Signatures in Node.js

This middleware extracts the profile URL from the `UCP‑Agent` header, fetches the public keys, and validates the HTTP Message Signature without any prior shared secret.

```js
const fetch = require('node-fetch');
const { parseSignature, verifySignature } = require('http-message-signatures');

// Extract profile URL from the UCP-Agent header
function getProfileUrl(req) {
  const header = req.headers['ucp-agent'];
  const match = /profile="([^"]+)"/.exec(header);
  return match ? match[1] : null;
}

// Middleware that validates the signature
async function ucpAuth(req, res, next) {
  const profileUrl = getProfileUrl(req);
  if (!profileUrl) return res.status(400).send('Missing UCP-Agent');

  const profileRes = await fetch(profileUrl);
  if (!profileRes.ok) return res.status(424).send('Profile unreachable');

  const profile = await profileRes.json();
  const signingKey = profile.signing_keys.find(k => k.kid === req.headers['signature-input']?.kid);
  if (!signingKey) return res.status(403).send('Unknown key');

  const verified = verifySignature({
    request: req,
    key: signingKey,               // JWK format
    components: ['@method','@authority','@path','ucp-agent'],
  });

  if (!verified) return res.status(403).send('Invalid signature');
  next();
}

```

*Key references*: The `UCP-Agent` header format is documented in [`docs/specification/signatures.md`](https://github.com/Universal-Commerce-Protocol/ucp/blob/main/docs/specification/signatures.md#L97), and the `signing_keys` array schema resides in [`source/discovery/profile_schema.json`](https://github.com/Universal-Commerce-Protocol/ucp/blob/main/source/discovery/profile_schema.json#L58)‑L70.

## Caching and Safety Requirements

To ensure profiles remain safe for automated retrieval, the specification enforces strict transport rules in [[`overview.md`](https://github.com/Universal-Commerce-Protocol/ucp/blob/main/overview.md#L33)‑L38](/Universal-Commerce-Protocol/ucp/blob/main/docs/specification/overview.md#L33):

- **HTTPS only**: Profiles must be served over TLS; plaintext HTTP is prohibited.
- **No redirects**: The endpoint must return the document directly.
- **Cache control**: Responses must include `Cache-Control: public, max-age=60` (or greater), allowing platforms to cache profiles and reduce latency for repeated interactions.

These constraints allow implementations to fetch profiles on‑the‑fly without security warnings or stale data concerns.

## Summary

- **UCP profiles** are JSON documents hosted at `/.well-known/ucp` that contain `signing_keys` and `ucp.capabilities`.
- **No registration** is required because platforms discover profiles via the `UCP‑Agent` header and standard HTTP GET requests.
- **Cryptographic verification** uses JWKs published in the profile, eliminating the need for pre‑shared secrets or API keys.
- **Caching rules** (HTTPS, no redirects, `max-age ≥ 60s`) ensure safe, performant discovery at scale.

## Frequently Asked Questions

### What makes UCP onboarding "permissionless"?

UCP onboarding is permissionless because developers publish a static JSON profile to a well‑known URL, and any platform can immediately discover capabilities and verify signatures by fetching that URL. No API key provisioning, OAuth registration, or manual approval is required before the first secure interaction.

### How does a platform discover a business's profile?

A platform discovers a business profile by performing an HTTP GET to `https://<domain>/.well-known/ucp`. Conversely, a business discovers a platform’s profile by reading the `profile="<url>"` parameter from the `UCP-Agent` header sent with incoming requests, as specified in [`docs/specification/overview.md`](https://github.com/Universal-Commerce-Protocol/ucp/blob/main/docs/specification/overview.md).

### Where are cryptographic keys stored in UCP?

Public keys are stored in the `signing_keys` array within the discovery profile, defined in [`source/discovery/profile_schema.json`](https://github.com/Universal-Commerce-Protocol/ucp/blob/main/source/discovery/profile_schema.json). Each key is a JWK (JSON Web Key) that includes the public key material and a `kid` (Key ID) for signature verification.

### What security requirements exist for hosting profiles?

Profiles must be served over HTTPS without redirects, and responses must carry `Cache-Control: public, max-age=60` or higher. These requirements, documented in [`docs/specification/overview.md`](https://github.com/Universal-Commerce-Protocol/ucp/blob/main/docs/specification/overview.md), ensure that fetched profiles are authentic and safe to cache, preventing man‑in‑the‑middle attacks and stale data usage.