# `Tipalti.Webhook`
[🔗](https://github.com/iamkanishka/tipalti/blob/main/lib/tipalti/webhook.ex#L1)

Parses Tipalti IPN (Instant Payment Notification) callbacks.

Tipalti posts IPNs as `application/x-www-form-urlencoded` bodies (in the
style of the classic PayPal IPN pattern) whenever a payee's status or a
payment's state changes. Point your webhook endpoint's raw request body
at `parse/1` to get a plain map of the notification fields with atom
keys, and use `event_type/1` to dispatch on the notification kind.

    def handle_tipalti_webhook(conn) do
      {:ok, body, conn} = Plug.Conn.read_body(conn)

      case Tipalti.Webhook.parse(body) do
        {:ok, event} ->
          handle_event(Tipalti.Webhook.event_type(event), event)
          Plug.Conn.send_resp(conn, 200, "")

        {:error, _reason} ->
          Plug.Conn.send_resp(conn, 400, "")
      end
    end

Tipalti IPNs are delivered over HTTPS to a URL you register in the AP
Hub, and don't carry a signature header the way many other providers'
webhooks do — treat the endpoint URL itself as the secret (keep it
unguessable / unpublished), and optionally verify the payload by calling
back into the SOAP or REST API to confirm the referenced payee/payment
actually changed as described before acting on it for anything
financially significant.

# `event`

```elixir
@type event() :: %{optional(atom()) =&gt; String.t()}
```

# `event_type`

```elixir
@spec event_type(event()) :: atom()
```

Returns the notification's event type, if present, as an atom (e.g.
`:payee_status_change`, `:payment_completed`) — falls back to `:unknown`
when the field isn't present.

# `parse`

```elixir
@spec parse(binary()) :: {:ok, event()} | {:error, :empty_body}
```

Parses a raw `application/x-www-form-urlencoded` IPN body into a map
with atom keys.

---

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