Agents only read the file at https://{domain}/.well-known/poppy.json. A 404, a plain-HTTP URL or a redirect to HTTP all mean there is no document.
Fix: Serve the file at exactly /.well-known/poppy.json over HTTPS. Redirects are fine if every hop stays on HTTPS.
Usually an HTML error or login page served at the path, a trailing comma, or a top-level array.
Fix: Return one JSON object ({ … }) with Content-Type: application/json, and check it parses.
organization, auth (and its sign-in types and custom_scopes), agent, web and extensions are objects. Many servers encode an empty map as <code>[]</code>, which typed agents reject.
Fix: Send {} for an empty object. In PHP, use (object) [] or JSON_FORCE_OBJECT for that member.
Agents read the version first to know which rules apply.
Fix: Add "protocol_version": "0.1", as a string, not the number 0.1.
The document speaks for the host that serves it, after any redirects. A scheme, a path, a different domain, or a missing or extra www. fails the match.
Fix: Use the host the file is finally served from: "domain": "www.yourcompany.com" if yourcompany.com redirects to www. Case is ignored; nothing else is.
Sessions need an OAuth issuer, so auth is required whenever the document lists agent, apis or a browser session endpoint.
Fix: Add "auth": { "issuer": "https://auth.yourcompany.com", "direct": { "scopes": ["poppy:read"] } }.
The issuer is where agents find your sign-in server and its metadata.
Fix: Set auth.issuer to your OAuth issuer as an HTTPS URL, written exactly as the server publishes it.
poppy:read and poppy:write belong to the protocol; custom scopes can't use that prefix.
Fix: Rename the scope (for example addresses) and describe it in auth.custom_scopes.
A document with no agent, apis or web gives an agent nothing to use.
Fix: List at least one route: your company agent (agent), your APIs (apis) or a website session (web).
Each entry in apis points the agent at an OpenAPI description or an MCP server.
Fix: Give every API a type (openapi or mcp) and an HTTPS url.
Not an error, but the description is how an agent picks the right API for a task.
Fix: Add a short description, such as "Orders, returns and exchanges".
Plain names are reserved for extensions the protocol defines, such as operations.
Fix: Name your own extensions with your domain, for example com.yourcompany.loyalty.
Agents fetch the issuer's OAuth Authorization Server Metadata (RFC 8414) to find its endpoints.
Fix: Publish it at the RFC 8414 well-known path for your issuer: /.well-known/oauth-authorization-server on the issuer's host.
Agents must refuse the document unless the two match exactly, so even a trailing slash on one side fails.
Fix: Copy the issuer value from your metadata into auth.issuer, character for character.
The issuer has to confirm each domain that names it. Without that, any domain could claim your sign-in server, so agents must not use the document.
Fix: Add "poppy_domains": ["yourcompany.com"] to your OAuth server metadata, listing every domain whose poppy.json names this issuer.
Personal agents sign DPoP proofs with an algorithm from this list (RFC 9449). Every company must publish it, and ES256 must be in it, so every agent has one algorithm it can rely on.
Fix: Add "dpop_signing_alg_values_supported": ["ES256"] to your OAuth server metadata, plus any other algorithms you accept.