Skip to main content
A widget runs in an iframe on another origin, and it can call its own server without going through the model at all. Booking a slot, loading a calendar, looking up a price, receiving a webhook: api/ is where those live. The path comes from the file’s position, the folder name included, so there is nothing to keep in step with the fetch() on the other side:
api/cal/slots.ts
The widget reaches it at the origin the host hands the view, which is the dev port locally and the deployed origin inside ChatGPT or Claude:

What the runtime adds

Four things arrive with every endpoint, so no app writes them:
HttpMethod | HttpMethod[]
Restrict the endpoint to these methods. Leaving it off accepts every method.
boolean
default:true
false opts out of CORS.
boolean
default:true
false opts out of the JSON body parser.
RequestHandler
required
An Express handler. Throwing is safe: the runtime answers 500 and logs.
Reach for a tool instead when the model is the caller. An endpoint never shows up in tools/list and the model cannot see that it was called, so it costs the conversation nothing. That suits a calendar the widget paints for itself, and rules it out for anything the model has to reason about or quote back. See Tools.
Endpoints share the process with /mcp, so lib/ is one set of modules for both, and the build check prints what it mounted:

well-known/ is for the root of the domain

Some paths are not the app’s to name. /.well-known/openai-apps-challenge proves to OpenAI that a deployment is yours; security.txt and apple-app-site-association answer to standards of their own. Whoever reads them looks at the root of the domain or nowhere, so /api/ is not an option and neither is a config key. well-known/ is api/ with a different prefix. It takes the same defineEndpoint, gets CORS, the method guard and the error envelope from the runtime, and the path still comes from the file’s position:
The folder on disk has no dot. npm strips a .-prefixed directory out of a published tarball and the generator treats dotfiles as tooling rather than source, so a literal .well-known/ would never reach the build. The dot goes back on when the URL is built. A handler runs per request, which is what a value that differs per environment needs:
well-known/openai-apps-challenge.ts
The verifier compares the body byte for byte, and res.send(string) on its own would label it text/html, hence res.type("text/plain"). A 404 when the token is unset makes an unconfigured environment look unclaimed.
Two names are refused: oauth-authorization-server and oauth-protected-resource. The framework serves those itself once an app configures OAuth, and an app’s endpoints mount ahead of the framework’s, so a file at either one would answer a client’s discovery request with a body it cannot use. waniwani check says so by name.
On Vercel this needs no routing of its own. The reservation that forces a route for /api/* does not exist under .well-known, and the build writes no static file there, so the request misses the filesystem phase and the catch-all already in the tree carries it to the server. See Deploy.