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

Framework-independent Dropbox webhook verification and notification decoding.

Notifications contain account IDs, not file changes. Enqueue those IDs and
use each account's client and saved cursor with `Magpie.Storage.continue_list/3`.
Respond promptly; serialize workers per account and apply pages idempotently.
This module provides no persistence, queue, deduplication or replay protection.
See the [incremental and webhook guide](incremental.md).

# `challenge`

```elixir
@spec challenge(term()) :: {:ok, map()} | {:error, :invalid_challenge}
```

Returns `{:ok, %{status: 200, headers: headers, body: challenge}}` for a
non-empty binary challenge, otherwise `{:error, :invalid_challenge}`.
Headers are `content-type: text/plain` and `x-content-type-options: nosniff`.
Echo the decoded query value as plain text without HTML interpolation.

# `notification`

```elixir
@spec notification(binary(), term(), binary()) ::
  {:ok, [binary()] | :ignored} | {:error, :invalid_signature | :invalid_payload}
```

Validates the signature before decoding JSON; returns `{:ok, [account_id]}`,
`{:ok, :ignored}`, `{:error, :invalid_signature}` or
`{:error, :invalid_payload}`.

A signed JSON object without `list_folder` is an unsupported notification:
return `{:ok, :ignored}` so the endpoint can acknowledge it without enqueueing
accounts. Consumers may observe this result to detect formats they don't handle.
This acknowledges receipt, not support for Business/team notifications.

When `list_folder` is present, `accounts` must be a list of non-empty binary
IDs. Malformed JSON, non-object JSON and malformed recognized notifications
return `:invalid_payload`. Empty lists are valid; order and repeated IDs are
preserved. Extra fields, including legacy `delta`, are ignored.

# `valid_signature?`

```elixir
@spec valid_signature?(binary(), term(), binary()) :: boolean()
```

Checks the hexadecimal X-Dropbox-Signature using HMAC-SHA256 over original
binary body bytes and the app secret. Compares equal-sized digests in constant
time. Returns false for absent, malformed or invalid signatures/arguments.
Never encode parsed JSON to reconstruct the signed body.

---

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