defmodule Channels.Webhook do @moduledoc """ Delivers notifications to an HTTP webhook endpoint. POSTs a JSON body to the configured URL whenever a notification fires. An optional bearer token can be set for endpoint authentication. ## Params * `:url` — the endpoint URL to POST to (required) * `:secret` — sent as `Authorization: Bearer ` (optional) ## Payload shape ```json { "monitor_id": "my-http-check", "monitor_name": "My HTTP Check", "status": "down", "previous_status": "up", "occurred_at": "2026-05-30T12:00:00Z", "node": "raven@indigo", "instance_id": "inst-1", "alert_started_at": "2026-05-30T11:46:00Z", "duration_down_s": 840 } ``` `node`, `instance_id`, `alert_started_at`, and `duration_down_s` are `null` whenever the firing handler didn't have that information — always present in the payload, never omitted. A 2xx response is considered successful. Any other status code or connection error is returned as `{:error, reason}`. """ use CodeNameRaven.Channel, category: :webhook alias CodeNameRaven.Channel.Notification @impl true def display_name, do: "Webhook" @impl true def params_template, do: %{url: "https://", secret: nil} @impl true def params_schema do [ url: [type: :string, required: true], secret: [type: :string] ] end @impl true def deliver(%Notification{} = n, %{url: url} = params) when is_binary(url) and url != "" do payload = %{ monitor_id: n.monitor_id, monitor_name: n.monitor_name, status: to_string(n.status), previous_status: n.previous_status && to_string(n.previous_status), occurred_at: DateTime.to_iso8601(n.occurred_at), node: n.node && to_string(n.node), instance_id: n.instance_id, alert_started_at: n.alert_started_at && DateTime.to_iso8601(n.alert_started_at), duration_down_s: Notification.duration_down(n) } headers = [{"content-type", "application/json"}] ++ if(params[:secret], do: [{"authorization", "Bearer #{params[:secret]}"}], else: []) case CodeNameRaven.Protocol.Http.request(:post, url, body: Jason.encode!(payload), headers: headers) do {:ok, %{status: status}} when status in 200..299 -> :ok {:ok, %{status: status}} -> {:error, "HTTP #{status}"} {:error, reason} -> {:error, to_string(reason)} end end def deliver(_notification, _params), do: {:error, "url is required"} end