Validates publish options and encodes them for ntfy's three publish shapes:
to_json_body/2— the JSON publish (POST /, §1.3): most options become body fields; header-only options come back as headers for the same request.to_headers/1— the raw-body publish (POST /<topic>, §1.2): every option becomes its canonicalX-header, RFC 2047-encoding non-ASCII values.to_query/1— the webhook-style publish (GET /<topic>/trigger): every option becomes a query parameter.
All three validate first, so a mistyped option raises
NimbleOptions.ValidationError instead of silently dropping a feature.
Options
:message(String.t/0) - Message body. On the JSON path this is normally the positional argument toExNtfy.publish/3; as an option it matters forExNtfy.publish_raw/3(e.g. an inline template) andExNtfy.trigger/2.:title(String.t/0) - Notification title.:priority-1..5or:min|:low|:default|:high|:max|:urgent.:tags- Tags/emoji shortcodes; atoms and strings mix, order is preserved.:markdown(boolean/0) - Render the message body as Markdown.:delay- Scheduled delivery: aDateTime, a non-negative unix timestamp, or a string ntfy understands ("30m","tomorrow, 3pm").:click(String.t/0) - URL opened when the notification is tapped.:icon(String.t/0) - JPEG/PNG URL used as the notification icon.:attach(String.t/0) - Attach a file by URL.:filename(String.t/0) - Attachment filename override.:actions- Up to 3 action buttons:ExNtfy.Actionstructs, ntfy-shaped maps, or a raw JSON string passed through untouched.:email- Forward to an e-mail address, ortruefor the account's verified address.:call- Phone call with the message read out: a number, ortruefor the verified one.:sequence_id(String.t/0) - Sequence ID for the update/clear/delete lifecycle.:cache(boolean/0) -falsesendsX-Cache: no(deliver only to live subscribers).:firebase(boolean/0) -falsesendsX-Firebase: no(don't forward to FCM).:unified_push(boolean/0) -truesendsX-UnifiedPush: 1. Only for UnifiedPush apps.:template-truefor inline templating,:github|:grafana|:alertmanagerfor pre-defined templates, or a custom server-side template name.:poll_id(String.t/0) - Internal (iOS instant notifications); pass-through only.
Summary
Functions
RFC 2047 B-encodes a header value if it contains non-ASCII bytes; pure ASCII passes through untouched.
Encodes every option to its canonical X- header for a raw-body publish.
Encodes options for a JSON publish (POST /).
Encodes every option to a query parameter for the webhook-style publish.
Types
Functions
RFC 2047 B-encodes a header value if it contains non-ASCII bytes; pure ASCII passes through untouched.
Examples
iex> ExNtfy.Publish.Options.rfc2047_encode("all ASCII")
"all ASCII"
iex> ExNtfy.Publish.Options.rfc2047_encode("Grüße 👋")
"=?UTF-8?B?R3LDvMOfZSDwn5GL?="
Encodes every option to its canonical X- header for a raw-body publish.
Non-ASCII values are RFC 2047 B-encoded via rfc2047_encode/1.
Examples
iex> ExNtfy.Publish.Options.to_headers(title: "Backup done", tags: [:warning, :backup])
[{"x-title", "Backup done"}, {"x-tags", "warning,backup"}]
iex> ExNtfy.Publish.Options.to_headers(delay: ~U[2026-07-05 12:00:00Z], template: true)
[{"x-delay", "1783252800"}, {"x-template", "yes"}]
Encodes options for a JSON publish (POST /).
Returns {body, headers}: the JSON body map (string keys, always including
"topic") plus the headers for the header-only options (:cache,
:firebase, :unified_push, :template, :poll_id).
Examples
iex> ExNtfy.Publish.Options.to_json_body("alerts", message: "hi", priority: :high)
{%{"topic" => "alerts", "message" => "hi", "priority" => 4}, []}
iex> ExNtfy.Publish.Options.to_json_body("alerts", email: true, cache: false)
{%{"topic" => "alerts", "email" => "yes"}, [{"x-cache", "no"}]}
Encodes every option to a query parameter for the webhook-style publish.
Values match the header encoding, but are left raw — URL encoding covers UTF-8, so no RFC 2047 here.
Examples
iex> ExNtfy.Publish.Options.to_query(priority: :urgent, unified_push: true)
[priority: "5", up: "1"]