# `Magpie.Webhook.Plug`
[🔗](https://github.com/alexcassol/magpie/blob/v0.8.0/lib/magpie/webhook/plug.ex#L1)

Optional Plug-compatible Dropbox webhook endpoint. The consumer supplies Plug;
Magpie keeps it test-only, just as Phoenix integrations have no runtime dependency.

Install **before** `Plug.Parsers` or anything that reads request bodies:

    plug Magpie.Webhook.Plug,
      path: "/webhooks/dropbox",
      app_secret: &MyApp.DropboxConfig.app_secret/0,
      notify: &MyApp.DropboxJobs.enqueue/1

Options: `:path` (required exact request path), `:app_secret` (non-empty binary
or zero-arity function), `:notify` (function receiving the entire account list,
returning `:ok` after durable enqueue, or `{:error, reason}`), and
`:max_body_bytes` (positive integer, default 1 MiB). Callbacks run synchronously:
enqueue only, never scan Dropbox in the callback. With compile-time Plug
initialization use remote captures (`&Module.function/arity`), since anonymous
functions cannot be escaped into compiled pipeline options. A secret function
must return a non-empty binary; invalid resolved secrets raise `ArgumentError`.
Unexpected callback returns also raise `ArgumentError`, without exposing values.
Exceptions propagate so the application can observe configuration/programming
failures; returned enqueue failures send 503 for retry.

Matching GETs echo the challenge with safe headers. POSTs read the untouched
body in chunks, reject missing/duplicate/invalid signatures with 403, malformed
signed JSON with 400, oversized bodies with 413 and read failures with 400.
Successful enqueue returns 200. Empty account lists and signed JSON objects
without `list_folder` return 200 without calling `:notify`. Ignored formats set
`conn.private[:magpie_webhook_notification]` to `:ignored` for observation.
Other methods return 405. Matching requests
are halted; other paths pass through. The raw body is stored in
`conn.private[:magpie_webhook_raw_body]` for successful reads, without reencoding.
It is never logged or retained by Magpie after the request.

# `call`

```elixir
@spec call(map(), keyword()) :: map()
```

Handles matching webhook requests before body parsers.

# `init`

```elixir
@spec init(keyword()) :: keyword()
```

Validates endpoint options.

---

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