agent-card.json and other .well-known agent files: how to fix them
Five files under /.well-known/ describe services to agents: api-catalog, oauth-protected-resource, oauth-authorization-server, agent-card.json and ucp. Publish a file only if you run the matching service. If you do not, a real 404 is correct, and SeenLens marks the check not applicable. SeenLens flags a file that exists but is wrong, or an HTML page that stands in for it.
By SeenLens · Last updated
Run the free 43-check scan on your page
Do I need any of these files?
Most sites need none of them. Each file describes a service that you operate: a public API, an OAuth protected API, an OAuth server, an A2A agent or a shopping profile for agents. A blog, a company site or a store with a normal checkout can skip all five.
When you skip a file, return a real 404 for its path. Do not publish an empty file or a placeholder to make a check green. A page that answers the path with HTML and status 200 is a soft 404, and SeenLens flags that. See soft 404 pages.
The standards differ in maturity. RFC 9727, RFC 9728 and RFC 8414 are published IETF standards. The A2A agent card comes from the A2A protocol specification, which lists version 1.0.0 as its latest release. The UCP profile comes from the Universal Commerce Protocol specification, whose pages carry date versions such as 2026-04-08. No study shows that these files change AI search visibility.
How does SeenLens test these files?
SeenLens requests each path once. All five checks use the same rules.
| What SeenLens finds | Result |
|---|---|
| 404, or any non-200 answer | Not applicable |
| 200 with an HTML page | Warn. The path is a soft 404. |
| 200 with a body that is not valid JSON | Fail |
| 200 with JSON nested more than a safe depth | Fail |
| 200, valid JSON, but the wrong content type | Warn |
| 200, valid JSON and the right content type | Pass |
The expected content type is application/linkset+json for the API catalog. For the other four files, the content type must contain json. SeenLens does not validate the fields inside the document, so check those against the standard.
curl -sI https://example.com/.well-known/api-catalog
curl -sI https://example.com/.well-known/agent-card.jsonAPI Catalog: /.well-known/api-catalog
RFC 9727 (June 2025, Standards Track) defines the well known URI /.well-known/api-catalog. It lets a client find a publisher's APIs, with their descriptions and documentation. The catalog uses the Linkset format (RFC 9264) and the media type application/linkset+json. The RFC asks for a profile parameter of https://www.rfc-editor.org/info/rfc9727.
Publish it only if you run public APIs. A minimal catalog for one API looks like this.
{
"linkset": [
{
"anchor": "https://api.example.com/v1",
"service-desc": [{ "href": "https://api.example.com/openapi.json", "type": "application/json" }],
"service-doc": [{ "href": "https://example.com/docs/api", "type": "text/html" }]
}
]
}Send it as Content-Type: application/linkset+json; profile="https://www.rfc-editor.org/info/rfc9727". Set the type yourself (see the section on content types below).
OAuth Protected Resource Metadata: /.well-known/oauth-protected-resource
RFC 9728 (April 2025, Standards Track) lets an API tell OAuth clients how to use it. The only required field is resource, the identifier of the protected resource. It must match the URL used to fetch the metadata. authorization_servers, scopes_supported and bearer_methods_supported are optional or recommended. The content type is application/json.
If the resource identifier has a path, such as https://example.com/api, the well known string goes between the host and the path: /.well-known/oauth-protected-resource/api. A protected resource can also point clients to the file with the resource_metadata parameter in a WWW-Authenticate header.
{
"resource": "https://example.com",
"authorization_servers": ["https://auth.example.com"],
"scopes_supported": ["read", "write"],
"bearer_methods_supported": ["header"]
}Publish it only if you run an OAuth protected API. A site with a login form for its own pages does not need it.
A2A agent card: /.well-known/agent-card.json
The A2A protocol uses an agent card to describe an agent: identity, capabilities, skills, service endpoint and authentication. The specification gives the discovery path as https://{server_domain}/.well-known/agent-card.json. Version 1.0.0 is the latest release the A2A site lists.
The spec marks these top level fields as required: name, description, supportedInterfaces, version, capabilities, defaultInputModes, defaultOutputModes and skills. Optional fields include provider, documentationUrl, securitySchemes, securityRequirements, signatures and iconUrl. Servers should send Cache-Control with a max-age and an ETag. Start from the sample card in section 8.5 of the specification.
Publish it only if you run an A2A agent that other agents call. A chat widget on your site is not an A2A agent.
UCP commerce profile: /.well-known/ucp
The Universal Commerce Protocol (UCP) lets shopping agents learn how to buy from a business. A business publishes a JSON profile at /.well-known/ucp. In the version dated 2026-04-08, the profile is a JSON object with a required ucp member. ucp.version, ucp.services and ucp.payment_handlers are required, and the last two must be present even if empty. The spec requires HTTPS, no redirects, and Cache-Control: public with a max-age of at least 60 seconds.
Publish it only if you take part in agent checkout. Read the current UCP specification first, because it changes by date version.
How do I serve these files with the right content type?
A file without a .json extension, such as api-catalog, may get a generic content type. Set it yourself.
location = /.well-known/api-catalog {
default_type application/linkset+json;
}
location = /.well-known/agent-card.json {
default_type application/json;
add_header Cache-Control "public, max-age=300";
}In Next.js, put JSON files with an extension in public/.well-known/. For a path without an extension, use a route handler such as app/.well-known/api-catalog/route.ts that returns the JSON with the header Content-Type: application/linkset+json. On WordPress, upload the files to a .well-known folder in the site root. A Shopify store cannot place files at /.well-known/ paths without a proxy, so skip these checks there.
How do I verify the fix?
- Run
curl -sIfor each file you publish. The status must be 200 and the content type must match the table above. - Run
curl -s URL | python3 -m json.tool. The JSON must print. - For each file you do not publish, run
curl -sIand confirm a 404. - Scan the page with SeenLens. Published files show pass, and missing files show not applicable.
When can I ignore these checks?
Ignore them when you do not run the service. SeenLens gives a 404 the result 'not applicable', which leaves the check out of the score and lists it as not tested. Each of the five checks has weight 1 in the agent readiness dimension. Spend the time on the checks that affect every visitor first.
Sources
- RFC 9727: API Catalog (path, media type, profile; June 2025; checked 2026-11-06)
- RFC 9728: OAuth 2.0 Protected Resource Metadata (path, fields, path insertion rule; April 2025; checked 2026-11-06)
- RFC 8414: OAuth 2.0 Authorization Server Metadata (path, required fields, issuer match; June 2018; checked 2026-11-06)
- A2A Protocol specification (agent card path, required fields, caching; version 1.0.0; checked 2026-11-06)
- Universal Commerce Protocol overview (profile path, required members, transport rules; version 2026-04-08; checked 2026-11-06)