All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

[0.1.0] — 2026-07-05

Added

  • format: :ws — WebSocket subscriptions (GET /<topics>/ws) via the optional :mint_web_socket dependency, with identical semantics to the HTTP stream formats (reconnect/resume, watchdog, all consumption styles); ExNtfy.Subscription now drives connections through a transport boundary
  • ExNtfy.subscribe/2, unsubscribe/1, and ExNtfy.Subscription — long-lived streaming subscriptions over /json (plus /sse and /raw via format:), with automatic reconnect (exponential backoff + jitter, since=<last id> resume), a keepalive watchdog (idle_timeout:), owner monitoring, and [:ex_ntfy, :subscription, ...] telemetry
  • ExNtfy.Handler — optional behaviour for callback-style consumption inside the subscription process (supervision-tree friendly)
  • ExNtfy.stream/2 — a lazy Enumerable of messages that blocks the caller and halts cleanly
  • Pure incremental stream parsers (ndjson, SSE, raw) with partial-line buffering across arbitrary chunk boundaries
  • ExNtfy.poll/2 and poll!/2 — one-shot retrieval of cached messages (GET /<topics>/json?poll=1) with the full since/scheduled/filter surface, multi-topic support, ndjson parsing (unparsable lines skipped with a warning), and [:ex_ntfy, :poll, ...] telemetry spans
  • ExNtfy.Subscribe.Options — subscribe option schema and URL builder (path/2, topics_segment/1, to_query/1), shared infrastructure for polling and the upcoming streaming subscriptions
  • ExNtfy.publish_file/3 and publish_file!/3 — binary attachment uploads (PUT /<topic>) accepting iodata, chunk streams, or {:file, path} (streamed from disk, filename defaulting to the basename), with all publish options riding along as headers
  • ExNtfy.update/4, clear/3, and delete/3 — the sequence-ID notification lifecycle: republish with the same sequence ID, PUT /<topic>/<seq>/clear, and DELETE /<topic>/<seq>
  • Topic names and sequence IDs are percent-escaped in request paths
  • ExNtfy.publish/3, publish!/3, publish_raw/3, and trigger/2 — publishing with full option coverage (title, priority, tags, markdown, delay, click, icon, attach, filename, actions, email, call, sequence_id, cache, firebase, unified_push, template, poll_id), delegating to ExNtfy.Publisher
  • ExNtfy.Publish.Options — one NimbleOptions schema validating publish options and encoding them as JSON body fields, canonical X- headers (RFC 2047 for non-ASCII values), or query parameters
  • ExNtfy.Action.to_json_map/1 and to_short/1 — outgoing action-button encoding (JSON and ntfy short format), round-tripping with from_map/1
  • Telemetry: [:ex_ntfy, :publish, :start | :stop | :exception] span events with %{topic, base_url} metadata

  • ExNtfy.Client — Req-based HTTP client with new/1 and request/2, Basic/Bearer authentication via header or ?auth= query parameter, and a req_options escape hatch
  • ExNtfy.Config — option resolution with per-call > application config > defaults precedence, validated with NimbleOptions
  • ExNtfy.Error — exception type covering ntfy JSON errors, plain-text/empty error bodies, and transport failures
  • ExNtfy.Message, ExNtfy.Action, ExNtfy.Attachment — lenient parsing of the ntfy message schema (from_map/1, from_json/1), retaining the raw map
  • Project scaffolded