# `AshHooks.BodyReader`
[🔗](https://github.com/baselabs/ash_hooks/blob/v2.0.3/lib/ash_hooks/body_reader.ex#L5)

Endpoint `body_reader` that caches the raw, pre-parser request bytes for
signature verification.

Configure it on the endpoint's `Plug.Parsers` (the installer patches
this; a router plug cannot recover pre-parse bytes):

    plug Plug.Parsers,
      ...,
      body_reader: {AshHooks.BodyReader, :read_body, []}

The controller then passes `conn.private[:ash_hooks_raw_body]` to
`AshHooks.Ingress.ingest/4` — signature schemes sign over the exact wire
bytes, so any re-encoding (parsed params, JSON roundtrip) would break
verification.

The reader is endpoint-wide by default: every parsed request (not only
webhook routes) carries a second copy of its body in `conn.private` for
the request lifetime, bounded by the parser's `:length`. To scope the
memory cost to the webhook routes, pass `:only` path prefixes:

    plug Plug.Parsers,
      ...,
      body_reader: {AshHooks.BodyReader, :read_body, [only: ["/webhooks"]]}

Only requests whose path starts with one of the prefixes are cached;
everything else passes through with zero retained bytes.

# `read_body`

```elixir
@spec read_body(Plug.Conn.t(), keyword(), keyword() | {atom(), term()}) ::
  {:ok, binary(), Plug.Conn.t()}
  | {:more, binary(), Plug.Conn.t()}
  | {:error, term()}
```

`body_reader` entry point. `Plug.Parsers` invokes the configured MFA as
`apply(mod, fun, [conn, opts | extra_args])` — extras arrive as a THIRD
positional argument, so the arity-3 clause is the one the parser calls;
`opts` and `extra` merge.

---

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