RFC 9457 application/problem+json errors for the shop API.
Every error the API sends, from the endpoint's PhoenixKit.API.BodyGuard
to a tool handler's {:error, code, message, context}, goes through
send/4, so a client sees one shape:
{"type": "https://phoenixkit.dev/api/errors/forbidden",
"title": "Forbidden", "status": 403, "code": "forbidden",
"detail": "This key lacks ecommerce.prices:write.",
"context": {"required_scope": "ecommerce.prices:write"},
"request_id": "F9x..."}code is the contract: stable, machine-readable, and what an agent
branches on. detail is always English prose for a human or an agent to
relay. context carries the fields that make the error actionable
(required_scope, errors, retry_after, ...).
The table of codes and statuses is statuses/0. A code that is not in it
answers 500 and is logged: it means a handler returned a code this
module does not know.
Summary
Functions
The problem document as a map with string keys.
Sends the problem document for code and halts the connection.
The HTTP status of an error code.
Every error code this module knows, with its HTTP status.
Functions
The problem document as a map with string keys.
request_id is left out of the map when it is nil.
@spec send(Plug.Conn.t(), atom(), String.t(), map()) :: Plug.Conn.t()
Sends the problem document for code and halts the connection.
The response carries content-type: application/problem+json and
cache-control: no-store. Three codes add a header: unauthorized a
www-authenticate challenge, forbidden with a required_scope in the
context an insufficient_scope challenge, and any context with a
retry_after (seconds) a retry-after header.
The request id is conn.assigns.phoenix_kit_api_request_id when the
PhoenixKit.API.Auth plug has run, else the x-request-id response
header that Plug.RequestId set. A header value with characters outside
[A-Za-z0-9_=+/-] (Plug.RequestId accepts any client-sent id of 20..200
bytes) is replaced by a fresh id, in the header and in the body, the same
rule Auth applies.
@spec status(atom()) :: pos_integer()
The HTTP status of an error code.
A code that is not in statuses/0 answers 500.
@spec statuses() :: %{required(atom()) => pos_integer()}
Every error code this module knows, with its HTTP status.