From 3d54260f7932024f99bee94ccfb6828629a51d55 Mon Sep 17 00:00:00 2001 From: binwiederhier Date: Thu, 15 Jan 2026 09:30:37 -0500 Subject: [PATCH] Docs --- client/options.go | 5 ++ cmd/publish.go | 6 ++ docs/publish.md | 159 ++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 150 insertions(+), 20 deletions(-) diff --git a/client/options.go b/client/options.go index f4711834..b99f1673 100644 --- a/client/options.go +++ b/client/options.go @@ -88,6 +88,11 @@ func WithFilename(filename string) PublishOption { return WithHeader("X-Filename", filename) } +// WithSequenceID sets a sequence ID for the message, allowing updates to existing notifications +func WithSequenceID(sequenceID string) PublishOption { + return WithHeader("X-Sequence-ID", sequenceID) +} + // WithEmail instructs the server to also send the message to the given e-mail address func WithEmail(email string) PublishOption { return WithHeader("X-Email", email) diff --git a/cmd/publish.go b/cmd/publish.go index f3139a63..c80c140b 100644 --- a/cmd/publish.go +++ b/cmd/publish.go @@ -34,6 +34,7 @@ var flagsPublish = append( &cli.BoolFlag{Name: "markdown", Aliases: []string{"md"}, EnvVars: []string{"NTFY_MARKDOWN"}, Usage: "Message is formatted as Markdown"}, &cli.StringFlag{Name: "template", Aliases: []string{"tpl"}, EnvVars: []string{"NTFY_TEMPLATE"}, Usage: "use templates to transform JSON message body"}, &cli.StringFlag{Name: "filename", Aliases: []string{"name", "n"}, EnvVars: []string{"NTFY_FILENAME"}, Usage: "filename for the attachment"}, + &cli.StringFlag{Name: "sequence-id", Aliases: []string{"sequence_id", "sid", "S"}, EnvVars: []string{"NTFY_SEQUENCE_ID"}, Usage: "sequence ID for updating notifications"}, &cli.StringFlag{Name: "file", Aliases: []string{"f"}, EnvVars: []string{"NTFY_FILE"}, Usage: "file to upload as an attachment"}, &cli.StringFlag{Name: "email", Aliases: []string{"mail", "e"}, EnvVars: []string{"NTFY_EMAIL"}, Usage: "also send to e-mail address"}, &cli.StringFlag{Name: "user", Aliases: []string{"u"}, EnvVars: []string{"NTFY_USER"}, Usage: "username[:password] used to auth against the server"}, @@ -70,6 +71,7 @@ Examples: ntfy pub --icon="http://some.tld/icon.png" 'Icon!' # Send notification with custom icon ntfy pub --attach="http://some.tld/file.zip" files # Send ZIP archive from URL as attachment ntfy pub --file=flower.jpg flowers 'Nice!' # Send image.jpg as attachment + ntfy pub -S my-id mytopic 'Update me' # Send with sequence ID for updates echo 'message' | ntfy publish mytopic # Send message from stdin ntfy pub -u phil:mypass secret Psst # Publish with username/password ntfy pub --wait-pid 1234 mytopic # Wait for process 1234 to exit before publishing @@ -101,6 +103,7 @@ func execPublish(c *cli.Context) error { markdown := c.Bool("markdown") template := c.String("template") filename := c.String("filename") + sequenceID := c.String("sequence-id") file := c.String("file") email := c.String("email") user := c.String("user") @@ -154,6 +157,9 @@ func execPublish(c *cli.Context) error { if filename != "" { options = append(options, client.WithFilename(filename)) } + if sequenceID != "" { + options = append(options, client.WithSequenceID(sequenceID)) + } if email != "" { options = append(options, client.WithEmail(email)) } diff --git a/docs/publish.md b/docs/publish.md index 6d5dca26..b8afb092 100644 --- a/docs/publish.md +++ b/docs/publish.md @@ -963,36 +963,95 @@ You can either: 1. **Use the message ID**: First publish without a sequence ID, then use the returned message `id` as the sequence ID for updates 2. **Use a custom sequence ID**: Publish directly to `//` with your own identifier -=== "Using the message ID" - ```bash - # First, publish a message and capture the message ID - $ curl -d "Downloading file..." ntfy.sh/mytopic - {"id":"xE73Iyuabi","time":1673542291,...} +Here's an example using a custom sequence ID to update a notification: - # Then use the message ID to update it - $ curl -d "Download complete!" ntfy.sh/mytopic/xE73Iyuabi - ``` - -=== "Using a custom sequence ID" +=== "Command line (curl)" ```bash # Publish with a custom sequence ID - $ curl -d "Downloading file..." ntfy.sh/mytopic/my-download-123 + curl -d "Downloading file..." ntfy.sh/mytopic/my-download-123 # Update using the same sequence ID - $ curl -d "Download complete!" ntfy.sh/mytopic/my-download-123 + curl -d "Download complete!" ntfy.sh/mytopic/my-download-123 ``` -=== "Using the X-Sequence-ID header" +=== "ntfy CLI" ```bash - # Publish with a sequence ID via header - $ curl -H "X-Sequence-ID: my-download-123" -d "Downloading..." ntfy.sh/mytopic + # Publish with a sequence ID + ntfy pub --sequence-id=my-download-123 mytopic "Downloading file..." # Update using the same sequence ID - $ curl -H "X-Sequence-ID: my-download-123" -d "Done!" ntfy.sh/mytopic + ntfy pub --sequence-id=my-download-123 mytopic "Download complete!" ``` -You can also set the sequence ID via the `sid` query parameter or when [publishing as JSON](#publish-as-json) using the -`sequence_id` field. +=== "HTTP" + ``` http + POST /mytopic/my-download-123 HTTP/1.1 + Host: ntfy.sh + + Downloading file... + ``` + +=== "JavaScript" + ``` javascript + // First message + await fetch('https://ntfy.sh/mytopic/my-download-123', { + method: 'POST', + body: 'Downloading file...' + }); + + // Update with same sequence ID + await fetch('https://ntfy.sh/mytopic/my-download-123', { + method: 'POST', + body: 'Download complete!' + }); + ``` + +=== "Go" + ``` go + // Publish with sequence ID in URL path + http.Post("https://ntfy.sh/mytopic/my-download-123", "text/plain", + strings.NewReader("Downloading file...")) + + // Update with same sequence ID + http.Post("https://ntfy.sh/mytopic/my-download-123", "text/plain", + strings.NewReader("Download complete!")) + ``` + +=== "PowerShell" + ``` powershell + # Publish with sequence ID + Invoke-RestMethod -Method POST -Uri "https://ntfy.sh/mytopic/my-download-123" -Body "Downloading file..." + + # Update with same sequence ID + Invoke-RestMethod -Method POST -Uri "https://ntfy.sh/mytopic/my-download-123" -Body "Download complete!" + ``` + +=== "Python" + ``` python + import requests + + # Publish with sequence ID + requests.post("https://ntfy.sh/mytopic/my-download-123", data="Downloading file...") + + # Update with same sequence ID + requests.post("https://ntfy.sh/mytopic/my-download-123", data="Download complete!") + ``` + +=== "PHP" + ``` php-inline + // Publish with sequence ID + file_get_contents('https://ntfy.sh/mytopic/my-download-123', false, stream_context_create([ + 'http' => ['method' => 'POST', 'content' => 'Downloading file...'] + ])); + + // Update with same sequence ID + file_get_contents('https://ntfy.sh/mytopic/my-download-123', false, stream_context_create([ + 'http' => ['method' => 'POST', 'content' => 'Download complete!'] + ])); + ``` + +You can also set the sequence ID via the `X-Sequence-ID` header, the `sid` query parameter, or when +[publishing as JSON](#publish-as-json) using the `sequence_id` field. ### Clearing notifications To clear a notification (mark it as read and dismiss it from the notification drawer), send a PUT request to @@ -1004,11 +1063,41 @@ To clear a notification (mark it as read and dismiss it from the notification dr ``` === "HTTP" - ```http + ``` http PUT /mytopic/my-download-123/clear HTTP/1.1 Host: ntfy.sh ``` +=== "JavaScript" + ``` javascript + await fetch('https://ntfy.sh/mytopic/my-download-123/clear', { + method: 'PUT' + }); + ``` + +=== "Go" + ``` go + req, _ := http.NewRequest("PUT", "https://ntfy.sh/mytopic/my-download-123/clear", nil) + http.DefaultClient.Do(req) + ``` + +=== "PowerShell" + ``` powershell + Invoke-RestMethod -Method PUT -Uri "https://ntfy.sh/mytopic/my-download-123/clear" + ``` + +=== "Python" + ``` python + requests.put("https://ntfy.sh/mytopic/my-download-123/clear") + ``` + +=== "PHP" + ``` php-inline + file_get_contents('https://ntfy.sh/mytopic/my-download-123/clear', false, stream_context_create([ + 'http' => ['method' => 'PUT'] + ])); + ``` + This publishes a `message_clear` event, which tells clients to: - Mark the notification as read in the app @@ -1023,11 +1112,41 @@ To delete a notification entirely, send a DELETE request to `// ['method' => 'DELETE'] + ])); + ``` + This publishes a `message_delete` event, which tells clients to: - Delete the notification from the database