An Elixir SDK for ntfy β a simple HTTP-based pub-sub notification service.
ExNtfy lets you publish notifications to ntfy topics and subscribe to messages, targeting either the public ntfy.sh instance or a self-hosted server.
Publishing
# The simplest possible publish
{:ok, message} = ExNtfy.publish("mytopic", "Backup completed π")
# With options (see `ExNtfy.Publish.Options` for the full list)
ExNtfy.publish("alerts", "Disk almost full",
title: "Server alert",
priority: :urgent,
tags: [:warning, :floppy_disk],
click: "https://grafana.example.com/d/disk"
)
# Action buttons
ExNtfy.publish("deploys", "Deploy finished",
actions: [
%ExNtfy.Action{type: :view, label: "Open", url: "https://example.com/deploys/42"}
]
)
# Raw body + templating: forward a webhook payload and let the server
# render it (reference Β§1.6)
ExNtfy.publish_raw("builds", github_webhook_json, template: :github)
# Webhook-style GET, e.g. from a curl one-liner or cron job
ExNtfy.trigger("mytopic", message: "cron ran", tags: [:clock])Attachments
Attach by URL with the :attach option, or upload a binary with
publish_file/3 β iodata, a stream, or {:file, path} (streamed from
disk; filename defaults to the basename):
ExNtfy.publish_file("mytopic", {:file, "/tmp/report.pdf"},
message: "Monthly report attached",
title: "Reports"
)
{:ok, message} = ExNtfy.publish_file("mytopic", png_bytes, filename: "graph.png")
message.attachment.url
#=> "https://ntfy.sh/file/oaFAdEY1KC.png"Lifecycle: update, clear, delete
Messages sharing a sequence ID form a sequence; clients show the latest
state. The simplest idiom is to reuse the id of the message you published:
{:ok, message} = ExNtfy.publish("deploys", "Deploy startedβ¦")
# Update it in place as things progress (the returned id is the sequence id)
ExNtfy.update("deploys", message.id, "Deploy at 80%β¦")
ExNtfy.update("deploys", message.id, "Deploy finished β
")
# Then dismiss it from clients, or delete it outright
ExNtfy.clear("deploys", message.id)
ExNtfy.delete("deploys", message.id)Alternatively pick your own sequence ID up front
(ExNtfy.publish("deploys", "β¦", sequence_id: "deploy-42")) and use it for
every follow-up.
Polling
Fetch cached messages without holding a connection open (see
ExNtfy.Subscribe.Options for the full since/filter surface):
# Everything cached for one or more topics
{:ok, messages} = ExNtfy.poll("mytopic")
{:ok, messages} = ExNtfy.poll(["alerts", "backups"], since: "10m")
# Resume after the last message you processed
ExNtfy.poll("mytopic", since: last_message.id)
# Filters: priority is any-match, tags must all match
ExNtfy.poll("mytopic", priority: [:high, :urgent], tags: [:warning], scheduled: true)Subscribing (streaming)
Long-lived subscriptions reconnect automatically with backoff, resume from
the last seen message (since=<id>), and watch for dead connections via a
keepalive watchdog. Three consumption styles:
Message-passing β events arrive in the calling process's mailbox:
{:ok, sub} = ExNtfy.subscribe("alerts", since: "10m")
receive do
{:ntfy, ^sub, %ExNtfy.Message{} = message} -> handle(message)
{:ntfy_lifecycle, ^sub, :connected} -> :ok
{:ntfy_lifecycle, ^sub, :disconnected} -> :ok
{:ntfy_lifecycle, ^sub, {:message_clear, message}} -> dismiss(message)
{:ntfy_lifecycle, ^sub, {:down, reason}} -> react(reason)
end
ExNtfy.unsubscribe(sub)The subscription stops when its owner (the caller, or owner:) dies.
Handler behaviour β callbacks run in the subscription process; put it in your supervision tree:
children = [
{ExNtfy.Subscription,
topics: ["alerts", "backups"],
handler: {MyApp.NtfyHandler, []},
auth: {:token, "tk_..."},
name: MyApp.NtfySub}
]See ExNtfy.Handler for the callbacks. A crashing handler takes the
subscription down with it β the supervisor restarts both.
Lazy stream β blocks the calling process, halts cleanly:
ExNtfy.stream("alerts")
|> Stream.filter(&(&1.priority >= 4))
|> Enum.take(5)All subscribe filters (ExNtfy.Subscribe.Options) and client options
apply; format: :sse | :raw | :ws selects the other transports (:raw
carries bodies only β no metadata, no resume; :ws subscribes over
WebSocket and requires the optional :mint_web_socket dependency).
Client options mix into the same keyword list β for a self-hosted server with authentication:
ExNtfy.publish("private", "hi",
base_url: "https://ntfy.example.com",
auth: {:token, "tk_..."}
)publish/3 returns {:ok, %ExNtfy.Message{}} (the created message as the
server recorded it) or {:error, %ExNtfy.Error{}}; publish!/3 returns the
message or raises.
Summary
Functions
Clears (dismisses) a delivered notification. See ExNtfy.Publisher.clear/3.
Deletes a notification from clients. See ExNtfy.Publisher.delete/3.
Fetches cached messages one-shot. See ExNtfy.Poller.poll/2.
Like poll/2, but raises on failure. See ExNtfy.Poller.poll!/2.
Publishes a message as JSON. See ExNtfy.Publisher.publish/3.
Like publish/3, but raises ExNtfy.Error on failure. See ExNtfy.Publisher.publish!/3.
Uploads a binary attachment. See ExNtfy.Publisher.publish_file/3.
Like publish_file/3, but raises on failure. See ExNtfy.Publisher.publish_file!/3.
Publishes a raw body with options as headers. See ExNtfy.Publisher.publish_raw/3.
A lazy stream of messages. See ExNtfy.Subscription.stream/2.
Starts a streaming subscription. See ExNtfy.Subscription.subscribe/2.
Webhook-style GET publish. See ExNtfy.Publisher.trigger/2.
Stops a subscription cleanly. See ExNtfy.Subscription.unsubscribe/1.
Updates a notification via its sequence ID. See ExNtfy.Publisher.update/4.
Functions
Clears (dismisses) a delivered notification. See ExNtfy.Publisher.clear/3.
Deletes a notification from clients. See ExNtfy.Publisher.delete/3.
Fetches cached messages one-shot. See ExNtfy.Poller.poll/2.
Like poll/2, but raises on failure. See ExNtfy.Poller.poll!/2.
Publishes a message as JSON. See ExNtfy.Publisher.publish/3.
Like publish/3, but raises ExNtfy.Error on failure. See ExNtfy.Publisher.publish!/3.
Uploads a binary attachment. See ExNtfy.Publisher.publish_file/3.
Like publish_file/3, but raises on failure. See ExNtfy.Publisher.publish_file!/3.
Publishes a raw body with options as headers. See ExNtfy.Publisher.publish_raw/3.
A lazy stream of messages. See ExNtfy.Subscription.stream/2.
Starts a streaming subscription. See ExNtfy.Subscription.subscribe/2.
Webhook-style GET publish. See ExNtfy.Publisher.trigger/2.
Stops a subscription cleanly. See ExNtfy.Subscription.unsubscribe/1.
Updates a notification via its sequence ID. See ExNtfy.Publisher.update/4.