CI Hex.pm Documentation

An Elixir SDK for ntfy β€” the simple HTTP-based pub-sub notification service. Publish notifications to any topic and subscribe to them, on ntfy.sh or your self-hosted server. Built on Req, tested against the real API.

Installation

def deps do
  [
    {:ex_ntfy, "~> 0.1.0"},
    # optional β€” only for WebSocket subscriptions (format: :ws)
    {:mint_web_socket, "~> 1.0"}
  ]
end

Publish

# a topic is all you need β€” no signup
{:ok, message} = ExNtfy.publish("mytopic", "Backup completed πŸŽ‰")

# full option surface: title, priority, tags, actions, delay, attachments, ...
ExNtfy.publish("alerts", "Disk almost full",
  title: "Server alert",
  priority: :urgent,
  tags: [:warning, :floppy_disk],
  click: "https://grafana.example.com/d/disk",
  actions: [
    %ExNtfy.Action{type: :view, label: "Open dashboard", url: "https://grafana.example.com"}
  ]
)

# upload a file as an attachment (streamed from disk)
ExNtfy.publish_file("mytopic", {:file, "/tmp/report.pdf"}, message: "Monthly report")

# update / dismiss / delete a delivered notification later
{:ok, msg} = ExNtfy.publish("deploys", "Deploy started…")
ExNtfy.update("deploys", msg.id, "Deploy finished βœ…")
ExNtfy.clear("deploys", msg.id)

See the Publishing cookbook for every option, including templates, scheduled delivery, e-mail forwarding, and webhook-style ExNtfy.trigger/2.

Poll

Fetch cached messages one-shot, with the full since/filter surface:

{:ok, messages} = ExNtfy.poll("mytopic", since: "10m")
{:ok, urgent} = ExNtfy.poll(["alerts", "backups"], priority: [:high, :urgent])

Subscribe

Long-lived subscriptions reconnect automatically (resuming from the last seen message) and watch for dead connections. Pick a consumption style:

# 1. messages to your process
{:ok, sub} = ExNtfy.subscribe("alerts")
receive do
  {:ntfy, ^sub, %ExNtfy.Message{} = message} -> IO.puts(message.message)
end

# 2. a handler in your supervision tree
defmodule MyApp.NtfyHandler do
  @behaviour ExNtfy.Handler
  @impl true
  def init(arg), do: {:ok, arg}
  @impl true
  def handle_message(message, state) do
    MyApp.Alerts.process(message)
    {:ok, state}
  end
end

children = [
  {ExNtfy.Subscription, topics: "alerts", handler: {MyApp.NtfyHandler, []}}
]

# 3. a lazy stream
ExNtfy.stream("alerts") |> Stream.filter(&(&1.priority >= 4)) |> Enum.take(5)

The Subscriptions guide covers lifecycle events, reconnect semantics, and the :sse/:raw/:ws transports.

Authentication & self-hosted servers

Every function takes client options; app-wide defaults go in config:

# per call
ExNtfy.publish("private", "hi",
  base_url: "https://ntfy.example.com",
  auth: {:token, "tk_..."}          # or {:basic, "user", "pass"}
)

# config/runtime.exs
config :ex_ntfy,
  base_url: "https://ntfy.example.com",
  auth: {:token, System.fetch_env!("NTFY_TOKEN")}

For clients that can't set headers, auth_via: :query sends credentials as the ?auth= parameter instead.

Documentation

Full API reference and guides: https://hexdocs.pm/ex_ntfy.

License

MIT