# `Fivetrex.WebhookPlug`
[🔗](https://github.com/lostbean/fivetrex/blob/v0.2.3/lib/fivetrex/webhook_plug.ex#L1)

A Plug for handling incoming Fivetran webhooks in Phoenix/Bandit applications.

This plug verifies webhook signatures and parses the payload into a
`Fivetrex.Models.WebhookEvent` struct, making it easy to integrate Fivetran
webhooks into your Phoenix application.

## Features

  * Verifies HMAC-SHA256 signatures to ensure requests are from Fivetran
  * Parses webhook payloads into typed structs
  * Returns appropriate HTTP error responses for invalid requests
  * Assigns the parsed event to the connection for downstream handlers

## Installation

### Step 1: Capture Raw Body

This plug requires access to the raw request body for signature verification.
Add a body reader to your endpoint:

    # In lib/my_app_web/endpoint.ex
    plug Plug.Parsers,
      parsers: [:urlencoded, :multipart, :json],
      pass: ["*/*"],
      body_reader: {Fivetrex.WebhookPlug, :cache_raw_body, []},  # Add this
      json_decoder: Phoenix.json_library()

### Step 2: Add Route

Add a route for the webhook endpoint:

    # In lib/my_app_web/router.ex
    scope "/webhooks", MyAppWeb do
      pipe_through :api

      post "/fivetran", FivetranWebhookController, :receive
    end

### Step 3: Use the Plug

Add the plug to your controller:

    defmodule MyAppWeb.FivetranWebhookController do
      use MyAppWeb, :controller

      plug Fivetrex.WebhookPlug,
        secret: {MyApp.Config, :fivetran_webhook_secret, []}
        # Or: secret: "my_static_secret"
        # Or: secret: {:system, "FIVETRAN_WEBHOOK_SECRET"}

      def receive(conn, _params) do
        event = conn.assigns.fivetran_event

        case event.event do
          "sync_end" ->
            # Handle sync completion
            handle_sync_end(event)

          "sync_start" ->
            # Handle sync start
            handle_sync_start(event)
        end

        json(conn, %{status: "ok"})
      end
    end

## Configuration Options

  * `:secret` - Required. The webhook secret for signature verification.
    Can be provided as:
    * A string: `secret: "my_secret"`
    * A tuple for runtime fetching: `secret: {Module, :function, args}`
    * A system env tuple: `secret: {:system, "ENV_VAR_NAME"}`

  * `:event_key` - Optional. The key to use in `conn.assigns` for the parsed
    event. Defaults to `:fivetran_event`.

  * `:on_error` - Optional. A function to customize error responses.
    Signature: `fn conn, error_type -> conn`. Defaults to sending JSON errors.

## Assigns

On successful verification, this plug adds:

  * `conn.assigns.fivetran_event` - The `%Fivetrex.Models.WebhookEvent{}` struct
  * `conn.assigns.raw_body` - The raw request body (for debugging)

## Error Handling

Invalid requests receive appropriate HTTP responses:

  * `400 Bad Request` - Missing signature header
  * `401 Unauthorized` - Invalid signature
  * `422 Unprocessable Entity` - Invalid JSON payload

## See Also

  * `Fivetrex.WebhookSignature` - Low-level signature verification
  * `Fivetrex.Models.WebhookEvent` - The event struct
  * `Fivetrex.Webhooks` - API for managing webhooks

# `cache_raw_body`

```elixir
@spec cache_raw_body(
  Plug.Conn.t(),
  keyword()
) :: {:ok, binary(), Plug.Conn.t()} | {:more, binary(), Plug.Conn.t()}
```

Custom body reader that caches the raw body for signature verification.

Use this as the `:body_reader` option in `Plug.Parsers`:

    plug Plug.Parsers,
      parsers: [:json],
      body_reader: {Fivetrex.WebhookPlug, :cache_raw_body, []},
      json_decoder: Jason

---

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