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
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./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:
.-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
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.
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.