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"}
]
endPublish
# 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.