diff --git a/Makefile b/Makefile
index 1894075c..a5513bad 100644
--- a/Makefile
+++ b/Makefile
@@ -206,7 +206,7 @@ release-check-tags:
# Installing targets
install-amd64: remove-binary
- sudo cp -a dist/ntfy_amd64_linux_amd64/ntfy /usr/bin/ntfy
+ sudo cp -a dist/ntfy_amd64_linux_amd64_v1/ntfy /usr/bin/ntfy
install-armv6: remove-binary
sudo cp -a dist/ntfy_armv6_linux_armv6/ntfy /usr/bin/ntfy
diff --git a/client/options.go b/client/options.go
index 1d3b7ac2..7d599699 100644
--- a/client/options.go
+++ b/client/options.go
@@ -56,6 +56,12 @@ func WithClick(url string) PublishOption {
return WithHeader("X-Click", url)
}
+// WithActions adds custom user actions to the notification. The value can be either a JSON array or the
+// simple format definition. See https://ntfy.sh/docs/publish/#action-buttons for details.
+func WithActions(value string) PublishOption {
+ return WithHeader("X-Actions", value)
+}
+
// WithAttach sets a URL that will be used by the client to download an attachment
func WithAttach(attach string) PublishOption {
return WithHeader("X-Attach", attach)
diff --git a/cmd/publish.go b/cmd/publish.go
index 6a4c7f0b..e210308a 100644
--- a/cmd/publish.go
+++ b/cmd/publish.go
@@ -26,6 +26,7 @@ var cmdPublish = &cli.Command{
&cli.StringFlag{Name: "tags", Aliases: []string{"tag", "T"}, EnvVars: []string{"NTFY_TAGS"}, Usage: "comma separated list of tags and emojis"},
&cli.StringFlag{Name: "delay", Aliases: []string{"at", "in", "D"}, EnvVars: []string{"NTFY_DELAY"}, Usage: "delay/schedule message"},
&cli.StringFlag{Name: "click", Aliases: []string{"U"}, EnvVars: []string{"NTFY_CLICK"}, Usage: "URL to open when notification is clicked"},
+ &cli.StringFlag{Name: "actions", Aliases: []string{"A"}, EnvVars: []string{"NTFY_ACTIONS"}, Usage: "actions JSON array or simple definition"},
&cli.StringFlag{Name: "attach", Aliases: []string{"a"}, EnvVars: []string{"NTFY_ATTACH"}, Usage: "URL to send as an external attachment"},
&cli.StringFlag{Name: "filename", Aliases: []string{"name", "n"}, EnvVars: []string{"NTFY_FILENAME"}, Usage: "filename for the attachment"},
&cli.StringFlag{Name: "file", Aliases: []string{"f"}, EnvVars: []string{"NTFY_FILE"}, Usage: "file to upload as an attachment"},
@@ -72,6 +73,7 @@ func execPublish(c *cli.Context) error {
tags := c.String("tags")
delay := c.String("delay")
click := c.String("click")
+ actions := c.String("actions")
attach := c.String("attach")
filename := c.String("filename")
file := c.String("file")
@@ -112,6 +114,9 @@ func execPublish(c *cli.Context) error {
if click != "" {
options = append(options, client.WithClick(click))
}
+ if actions != "" {
+ options = append(options, client.WithActions(strings.ReplaceAll(actions, "\n", " ")))
+ }
if attach != "" {
options = append(options, client.WithAttach(attach))
}
diff --git a/docs/publish.md b/docs/publish.md
index 54d2642c..2948c4e4 100644
--- a/docs/publish.md
+++ b/docs/publish.md
@@ -789,21 +789,21 @@ The JSON message format closely mirrors the format of the message you can consum
(see [JSON message format](subscribe/api.md#json-message-format) for details), but is not exactly identical. Here's an overview of
all the supported fields:
-| Field | Required | Type | Example | Description |
-|------------|----------|----------------------------------|---------------------------------------|-----------------------------------------------------------------------|
-| `topic` | ✔️ | *string* | `topic1` | Target topic name |
-| `message` | - | *string* | `Some message` | Message body; set to `triggered` if empty or not passed |
-| `title` | - | *string* | `Some title` | Message [title](#message-title) |
-| `tags` | - | *string array* | `["tag1","tag2"]` | List of [tags](#tags-emojis) that may or not map to emojis |
-| `priority` | - | *int (one of: 1, 2, 3, 4, or 5)* | `4` | Message [priority](#message-priority) with 1=min, 3=default and 5=max |
-| `actions` | - | *JSON array* | *(see [user actions](#user-actions))* | Custom [user action buttons](#user-actions) for notifications |
-| `click` | - | *URL* | `https://example.com` | Website opened when notification is [clicked](#click-action) |
-| `attach` | - | *URL* | `https://example.com/file.jpg` | URL of an attachment, see [attach via URL](#attach-file-from-url) |
-| `filename` | - | *string* | `file.jpg` | File name of the attachment |
-| `delay` | - | *string* | `30min`, `9am` | Timestamp or duration for delayed delivery |
-| `email` | - | *e-mail address* | `phil@example.com` | E-mail address for e-mail notifications |
+| Field | Required | Type | Example | Description |
+|------------|----------|----------------------------------|-------------------------------------------|-----------------------------------------------------------------------|
+| `topic` | ✔️ | *string* | `topic1` | Target topic name |
+| `message` | - | *string* | `Some message` | Message body; set to `triggered` if empty or not passed |
+| `title` | - | *string* | `Some title` | Message [title](#message-title) |
+| `tags` | - | *string array* | `["tag1","tag2"]` | List of [tags](#tags-emojis) that may or not map to emojis |
+| `priority` | - | *int (one of: 1, 2, 3, 4, or 5)* | `4` | Message [priority](#message-priority) with 1=min, 3=default and 5=max |
+| `actions` | - | *JSON array* | *(see [actiom buttons](#action-buttons))* | Custom [user action buttons](#action-buttons) for notifications |
+| `click` | - | *URL* | `https://example.com` | Website opened when notification is [clicked](#click-action) |
+| `attach` | - | *URL* | `https://example.com/file.jpg` | URL of an attachment, see [attach via URL](#attach-file-from-url) |
+| `filename` | - | *string* | `file.jpg` | File name of the attachment |
+| `delay` | - | *string* | `30min`, `9am` | Timestamp or duration for delayed delivery |
+| `email` | - | *e-mail address* | `phil@example.com` | E-mail address for e-mail notifications |
-## User actions
+## Action buttons
You can add action buttons to notifications to allow yourself to react to a notification directly. This is incredibly
useful and has countless applications. As of today, the following actions are supported:
@@ -812,6 +812,9 @@ useful and has countless applications. As of today, the following actions are su
when the action button is tapped
* [`http`](#send-http-request): Sends HTTP POST/GET/PUT request when the action button is tapped
+To define the user actions, you can either pass the `actions` field as part of the JSON body (if you're
+[publishing via JSON](#publish-as-json)), or use the `X-Actions` header (or any of its aliases: `Actions`, `Action`).
+
Here's an example of what that a notification with actions can look like:
+Using the `X-Actions` header and the **simple format** (details see below), you can create the above notification like
+this. This format is much easier to write, but less powerful:
+
+=== "Command line (curl)"
+ ```
+ curl \
+ -d "You left the house. Turn down the A/C?" \
+ -H "Actions: view, Open portal, https://home.nest.com/; \
+ http, Turn down, https://api.nest.com/device/XZ1D2, body=target_temp_f=65" \
+ ntfy.sh/myhome
+ ```
+
+=== "ntfy CLI"
+ ```
+ ntfy publish \
+ --actions="view, Open portal, https://home.nest.com/; \
+ http, Turn down, https://api.nest.com/device/XZ1D2, body=target_temp_f=65" \
+ myhome \
+ "You left the house. Turn down the A/C?"
+ ```
+
+=== "HTTP"
+ ``` http
+ POST /myhome HTTP/1.1
+ Host: ntfy.sh
+ Actions: view, Open portal, https://home.nest.com/; http, Turn down, https://api.nest.com/device/XZ1D2, body=target_temp_f=65
+
+ You left the house. Turn down the A/C?
+ ```
+
+=== "JavaScript"
+ ``` javascript
+ fetch('https://ntfy.sh/myhome', {
+ method: 'POST',
+ body: 'You left the house. Turn down the A/C?',
+ headers: {
+ 'Actions': 'view, Open portal, https://home.nest.com/; http, Turn down, https://api.nest.com/device/XZ1D2, body=target_temp_f=65'
+ }
+ })
+ ```
+
+=== "Go"
+ ``` go
+ req, _ := http.NewRequest("POST", "https://ntfy.sh/myhome", strings.NewReader("You left the house. Turn down the A/C?"))
+ req.Header.Set("Actions", "view, Open portal, https://home.nest.com/; http, Turn down, https://api.nest.com/device/XZ1D2, body=target_temp_f=65")
+ http.DefaultClient.Do(req)
+ ```
+
+=== "PowerShell"
+ ``` powershell
+ $uri = "https://ntfy.sh/myhome"
+ $headers = @{ Actions="view, Open portal, https://home.nest.com/; http, Turn down, https://api.nest.com/device/XZ1D2, body=target_temp_f=65" }
+ $body = "You left the house. Turn down the A/C?"
+ Invoke-RestMethod -Method 'Post' -Uri $uri -Headers $headers -Body $body -UseBasicParsing
+ ```
+
+=== "Python"
+ ``` python
+ requests.post("https://ntfy.sh/myhome",
+ data="You left the house. Turn down the A/C?",
+ headers={ "Actions": "view, Open portal, https://home.nest.com/; http, Turn down, https://api.nest.com/device/XZ1D2, body=target_temp_f=65" })
+ ```
+
+=== "PHP"
+ ``` php-inline
+ file_get_contents('https://ntfy.sh/reddit_alerts', false, stream_context_create([
+ 'http' => [
+ 'method' => 'POST',
+ 'header' =>
+ "Content-Type: text/plain\r\n" .
+ "Actions: view, Open portal, https://home.nest.com/; http, Turn down, https://api.nest.com/device/XZ1D2, body=target_temp_f=65",
+ 'content' => 'You left the house. Turn down the A/C?'
+ ]
+ ]));
+ ```
+
+Alternatively, you can define actions as **JSON array** (details see below), and pass them as part of the JSON body
+(see [publish as JSON](#publish-as-json)):
+
=== "Command line (curl)"
```
curl ntfy.sh \
-d '{
"topic": "myhome",
- "message": "You seem to have left the house. Want to turn down the A/C?",
+ "message": "You left the house. Turn down the A/C?",
"actions": [
{
"action": "view",
@@ -834,39 +916,54 @@ Here's an example of what that a notification with actions can look like:
{
"action": "http",
"label": "Turn down",
- "method": "POST",
- "url": "https://developer-api.nest.com/devices/thermostats/XZA124D",
- "headers": {
- "Authorization": "Bearer ...",
- "Content-Type": "application/json"
- },
- "body": "{\"target_temperature_f\": 65}"
- },
- {
- "action": "broadcast",
- "label": "Enter deep sleep 💤",
- "extras": {
- "command": "deepsleep"
- }
+ "url": "https://api.nest.com/device/XZ1D2",
+ "body": "target_temp_f=65"
}
]
}'
```
+=== "ntfy CLI"
+ ```
+ ntfy publish \
+ --actions '[
+ {
+ "action": "view",
+ "label": "Open portal",
+ "url": "https://home.nest.com/"
+ },
+ {
+ "action": "http",
+ "label": "Turn down",
+ "url": "https://api.nest.com/device/XZ1D2",
+ "body": "target_temp_f=65"
+ }
+ ]' \
+ myhome \
+ "You left the house. Turn down the A/C?"
+ ```
+
=== "HTTP"
``` http
POST / HTTP/1.1
Host: ntfy.sh
{
- "topic": "mytopic",
- "message": "Disk space is low at 5.1 GB",
- "title": "Low disk space alert",
- "tags": ["warning","cd"],
- "priority": 4,
- "attach": "https://filesrv.lan/space.jpg",
- "filename": "diskspace.jpg",
- "click": "https://homecamera.lan/xasds1h2xsSsa/"
+ "topic": "myhome",
+ "message": "You left the house. Turn down the A/C?",
+ "actions": [
+ {
+ "action": "view",
+ "label": "Open portal",
+ "url": "https://home.nest.com/"
+ },
+ {
+ "action": "http",
+ "label": "Turn down",
+ "url": "https://api.nest.com/device/XZ1D2",
+ "body": "target_temp_f=65"
+ }
+ ]
}
```
@@ -875,14 +972,21 @@ Here's an example of what that a notification with actions can look like:
fetch('https://ntfy.sh', {
method: 'POST',
body: JSON.stringify({
- "topic": "mytopic",
- "message": "Disk space is low at 5.1 GB",
- "title": "Low disk space alert",
- "tags": ["warning","cd"],
- "priority": 4,
- "attach": "https://filesrv.lan/space.jpg",
- "filename": "diskspace.jpg",
- "click": "https://homecamera.lan/xasds1h2xsSsa/"
+ topic: "myhome",
+ message": "You left the house. Turn down the A/C?",
+ actions: [
+ {
+ action: "view",
+ label: "Open portal",
+ url: "https://home.nest.com/"
+ },
+ {
+ action: "http",
+ label: "Turn down",
+ url: "https://api.nest.com/device/XZ1D2",
+ body: "target_temp_f=65"
+ }
+ ]
})
})
```
@@ -890,18 +994,24 @@ Here's an example of what that a notification with actions can look like:
=== "Go"
``` go
// You should probably use json.Marshal() instead and make a proper struct,
- // or even just use req.Header.Set() like in the other examples, but for the
- // sake of the example, this is easier.
+ // but for the sake of the example, this is easier.
body := `{
- "topic": "mytopic",
- "message": "Disk space is low at 5.1 GB",
- "title": "Low disk space alert",
- "tags": ["warning","cd"],
- "priority": 4,
- "attach": "https://filesrv.lan/space.jpg",
- "filename": "diskspace.jpg",
- "click": "https://homecamera.lan/xasds1h2xsSsa/"
+ "topic": "myhome",
+ "message": "You left the house. Turn down the A/C?",
+ "actions": [
+ {
+ "action": "view",
+ "label": "Open portal",
+ "url": "https://home.nest.com/"
+ },
+ {
+ "action": "http",
+ "label": "Turn down",
+ "url": "https://api.nest.com/device/XZ1D2",
+ "body": "target_temp_f=65"
+ }
+ ]
}`
req, _ := http.NewRequest("POST", "https://ntfy.sh/", strings.NewReader(body))
http.DefaultClient.Do(req)
@@ -911,15 +1021,22 @@ Here's an example of what that a notification with actions can look like:
``` powershell
$uri = "https://ntfy.sh"
$body = @{
- "topic"="powershell"
- "title"="Low disk space alert"
- "message"="Disk space is low at 5.1 GB"
- "priority"=4
- "attach"="https://filesrv.lan/space.jpg"
- "filename"="diskspace.jpg"
- "tags"=@("warning","cd")
- "click"= "https://homecamera.lan/xasds1h2xsSsa/"
- } | ConvertTo-Json
+ "topic"="myhome"
+ "message"="You left the house. Turn down the A/C?"
+ "actions"=@(
+ @{
+ "action"="view"
+ "label"="Open portal"
+ "url"="https://home.nest.com/"
+ },
+ @{
+ "action"="http",
+ "label"="Turn down"
+ "url"="https://api.nest.com/device/XZ1D2"
+ "body"="target_temp_f=65"
+ }
+ )
+ } | ConvertTo-Json
Invoke-RestMethod -Method 'Post' -Uri $uri -Body $body -ContentType "application/json" -UseBasicParsing
```
@@ -927,14 +1044,21 @@ Here's an example of what that a notification with actions can look like:
``` python
requests.post("https://ntfy.sh/",
data=json.dumps({
- "topic": "mytopic",
- "message": "Disk space is low at 5.1 GB",
- "title": "Low disk space alert",
- "tags": ["warning","cd"],
- "priority": 4,
- "attach": "https://filesrv.lan/space.jpg",
- "filename": "diskspace.jpg",
- "click": "https://homecamera.lan/xasds1h2xsSsa/"
+ "topic": "myhome",
+ "message": "You left the house. Turn down the A/C?",
+ "actions": [
+ {
+ "action": "view",
+ "label": "Open portal",
+ "url": "https://home.nest.com/"
+ },
+ {
+ "action": "http",
+ "label": "Turn down",
+ "url": "https://api.nest.com/device/XZ1D2",
+ "body": "target_temp_f=65"
+ }
+ ]
})
)
```
@@ -946,31 +1070,57 @@ Here's an example of what that a notification with actions can look like:
'method' => 'POST',
'header' => "Content-Type: application/json",
'content' => json_encode([
- "topic": "mytopic",
- "message": "Disk space is low at 5.1 GB",
- "title": "Low disk space alert",
- "tags": ["warning","cd"],
- "priority": 4,
- "attach": "https://filesrv.lan/space.jpg",
- "filename": "diskspace.jpg",
- "click": "https://homecamera.lan/xasds1h2xsSsa/"
+ "topic": "myhome",
+ "message": "You left the house. Turn down the A/C?",
+ "actions": [
+ [
+ "action": "view",
+ "label": "Open portal",
+ "url": "https://home.nest.com/"
+ ],
+ [
+ "action": "http",
+ "label": "Turn down",
+ "url": "https://api.nest.com/device/XZ1D2",
+ "headers": [
+ "Authorization": "Bearer ..."
+ ],
+ "body": "target_temp_f=65"
+ ]
+ ]
])
]
]));
```
+**Simple format syntax:**
+
+Generally, the `X-Actions` header is formatted like this:
+```
+Actions: ,