# `PhoenixKit.API.REST`
[🔗](https://github.com/BeamLabEU/phoenix_kit/blob/v2.64.1/lib/phoenix_kit/api/rest.ex#L1)

The REST transport of the machine API: a plug that the router `forward`s to,
once per audience.

    forward "/api/admin/v1", PhoenixKit.API.REST, audience: :admin
    forward "/api/public/v1", PhoenixKit.API.REST, audience: :public

`phoenix_kit_routes/0` emits both, and the deprecated alias of the public
one (`PhoenixKit.API.Paths`), which also takes `deprecated: [path: ...,
successor: ...]`: the answers on it carry `Deprecation` and a `Link` to the
canonical path (`PhoenixKit.API.Auth`). The plug sees the path after the
forward, so the routes are:

  * `POST /<tool>` runs a tool; the body is the tool's input as one JSON
    object. A tool name has a dot in it (`catalogue.set_prices`);
  * `GET /` lists the tools the caller may use, with their schemas;
  * `GET /openapi.json` is the generated OpenAPI 3.1 document of the tools the
    caller may use.

Any other method on those paths answers `405` with an `allow` header,
and any other path `404`, both as `application/problem+json`.

The public tool list also carries a `Link` header to the OpenAPI document
and the guide while the guide is published
(`PhoenixKit.API.PublicDiscovery.put_list_links/1`).

Everything else is done by `PhoenixKit.API.Auth` (API switch, key) and
`PhoenixKit.API.Executor` (rights, limits, validation, the tool). The
request body is `conn.body_params`, which `PhoenixKit.API.BodyGuard`
filled in the endpoint; a body that never went through a parser reads as
`%{}`.

A tool name is matched as sent: the segments of the guarded prefix are
percent-decoded by `BodyGuard`, the tool segment is not (`system%2Eget_identity`
is an unknown tool).

## Encoded prefixes need `BodyGuard` in the endpoint

Phoenix's `forward` strips its prefix from the raw `path_info`, so a request
that writes the prefix percent-encoded (`/api/%61dmin/v1/x`, which the router
still matches) raises a `MatchError` inside the forward. `BodyGuard`, in the
host's endpoint, decodes those segments first. A host that has not run
`mix phoenix_kit.update` since `BodyGuard` was added answers such a crafted
URL with a 500; the MCP forwards will have the same dependency.

## CORS

The public audience answers cross-origin requests from any origin, so a
browser-based agent can call it: `access-control-allow-origin: *`, no
credentials, and a `204` for the `OPTIONS` preflight. The headers the rate
limit and the support flow rely on (`retry-after`, `ratelimit-*`,
`x-request-id`, `idempotent-replayed`) are listed in `access-control-expose-headers`, otherwise a
browser script cannot read them. The admin audience
sends no CORS headers: a browser must not call it from another site.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
