Endpoints and OpenAPI

Endpoint is the typed description of one operation. Api.bind attaches the function. OpenAPI 3.1 and Swagger UI are projections. The teaching path is Operations are an AST then Docs and HTTP fall out.

OpenAPI is a projection
/shows/{id}:
  get:
    summary: One bill
    parameters: [{ name: id, in: path, schema: integer }]
    responses:
      "200": { schema: Show }
      "404": { schema: NotFound }

The DSL

.inJson / .out / .outError need Schema and JsonCodec. .query, .header, .auth, .summary, .tag are documentation that also drives decoding.

BoxOffice.seed.flatMap { store =>
  BoxOffice.api(store).routes(Request.get("/shows/1")).map(res => (res.status, res.body.asString))
}
(Status(200,OK),{"id":1,"title":"Evening bill","remaining":12})

OpenAPI is a projection

BoxOffice.seed.map { store =>
  val json = BoxOffice.api(store).openApi.toJson
  json.contains("/shows/{id}") && json.contains("One bill")
}
true

The live projection is on Docs and HTTP fall out.

Errors

.outError[E](Status.NotFound) maps a typed E to a status. OpArgs is how MCP flattens path, query, and JSON body into tool arguments; non-JSON bodies are not promotable.

BoxOffice.seed.flatMap { store =>
  BoxOffice.api(store).routes(Request.get("/shows/99")).map(_.status)
}
Status(404,Not Found)