# Publishing Publishing messages can be done via HTTP PUT/POST or via the [ntfy CLI](subscribe/cli.md#publish-messages) ([install instructions](install.md)). Topics are created on the fly by subscribing or publishing to them. Because there is no sign-up, **the topic is essentially a password**, so pick something that's not easily guessable. Here's an example showing how to publish a simple message using a POST request: === "Command line (curl)" ``` curl -d "Backup successful 😀" ntfy.sh/mytopic ``` === "ntfy CLI" ``` ntfy publish mytopic "Backup successful 😀" ``` === "HTTP" ``` http POST /mytopic HTTP/1.1 Host: ntfy.sh Backup successful 😀 ``` === "JavaScript" ``` javascript fetch('https://ntfy.sh/mytopic', { method: 'POST', // PUT works too body: 'Backup successful 😀' }) ``` === "Go" ``` go http.Post("https://ntfy.sh/mytopic", "text/plain", strings.NewReader("Backup successful 😀")) ``` === "PowerShell" ``` powershell $Request = @{ Method = "POST" URI = "https://ntfy.sh/mytopic" Body = "Backup successful" } Invoke-RestMethod @Request ``` === "Python" ``` python requests.post("https://ntfy.sh/mytopic", data="Backup successful 😀".encode(encoding='utf-8')) ``` === "PHP" ``` php-inline file_get_contents('https://ntfy.sh/mytopic', false, stream_context_create([ 'http' => [ 'method' => 'POST', // PUT also works 'header' => 'Content-Type: text/plain', 'content' => 'Backup successful 😀' ] ])); ``` If you have the [Android app](subscribe/phone.md) installed on your phone, this will create a notification that looks like this:
![basic notification](static/img/android-screenshot-basic-notification.png){ width=500 }
Android notification
There are more features related to publishing messages: You can set a [notification priority](#message-priority), a [title](#message-title), and [tag messages](#tags-emojis) 🥳 🎉. Here's an example that uses some of them at together: === "Command line (curl)" ``` curl \ -H "Title: Unauthorized access detected" \ -H "Priority: urgent" \ -H "Tags: warning,skull" \ -d "Remote access to phils-laptop detected. Act right away." \ ntfy.sh/phil_alerts ``` === "ntfy CLI" ``` ntfy publish \ --title "Unauthorized access detected" \ --tags warning,skull \ --priority urgent \ mytopic \ "Remote access to phils-laptop detected. Act right away." ``` === "HTTP" ``` http POST /phil_alerts HTTP/1.1 Host: ntfy.sh Title: Unauthorized access detected Priority: urgent Tags: warning,skull Remote access to phils-laptop detected. Act right away. ``` === "JavaScript" ``` javascript fetch('https://ntfy.sh/phil_alerts', { method: 'POST', // PUT works too body: 'Remote access to phils-laptop detected. Act right away.', headers: { 'Title': 'Unauthorized access detected', 'Priority': 'urgent', 'Tags': 'warning,skull' } }) ``` === "Go" ``` go req, _ := http.NewRequest("POST", "https://ntfy.sh/phil_alerts", strings.NewReader("Remote access to phils-laptop detected. Act right away.")) req.Header.Set("Title", "Unauthorized access detected") req.Header.Set("Priority", "urgent") req.Header.Set("Tags", "warning,skull") http.DefaultClient.Do(req) ``` === "PowerShell" ``` powershell $Request = @{ Method = "POST" URI = "https://ntfy.sh/phil_alerts" Headers = @{ Title = "Unauthorized access detected" Priority = "urgent" Tags = "warning,skull" } Body = "Remote access to phils-laptop detected. Act right away." } Invoke-RestMethod @Request ``` === "Python" ``` python requests.post("https://ntfy.sh/phil_alerts", data="Remote access to phils-laptop detected. Act right away.", headers={ "Title": "Unauthorized access detected", "Priority": "urgent", "Tags": "warning,skull" }) ``` === "PHP" ``` php-inline file_get_contents('https://ntfy.sh/phil_alerts', false, stream_context_create([ 'http' => [ 'method' => 'POST', // PUT also works 'header' => "Content-Type: text/plain\r\n" . "Title: Unauthorized access detected\r\n" . "Priority: urgent\r\n" . "Tags: warning,skull", 'content' => 'Remote access to phils-laptop detected. Act right away.' ] ])); ```
![priority notification](static/img/priority-notification.png){ width=500 }
Urgent notification with tags and title
You can also do multi-line messages. Here's an example using a [click action](#click-action), an [action button](#action-buttons), an [external image attachment](#attach-file-from-a-url) and [email publishing](#e-mail-publishing): === "Command line (curl)" ``` curl \ -H "Click: https://home.nest.com/" \ -H "Attach: https://nest.com/view/yAxkasd.jpg" \ -H "Actions: http, Open door, https://api.nest.com/open/yAxkasd, clear=true" \ -H "Email: phil@example.com" \ -d "There's someone at the door. 🐶 Please check if it's a good boy or a hooman. Doggies have been known to ring the doorbell." \ ntfy.sh/mydoorbell ``` === "ntfy CLI" ``` ntfy publish \ --click="https://home.nest.com/" \ --attach="https://nest.com/view/yAxkasd.jpg" \ --actions="http, Open door, https://api.nest.com/open/yAxkasd, clear=true" \ --email="phil@example.com" \ mydoorbell \ "There's someone at the door. 🐶 Please check if it's a good boy or a hooman. Doggies have been known to ring the doorbell." ``` === "HTTP" ``` http POST /mydoorbell HTTP/1.1 Host: ntfy.sh Click: https://home.nest.com/ Attach: https://nest.com/view/yAxkasd.jpg Actions: http, Open door, https://api.nest.com/open/yAxkasd, clear=true Email: phil@example.com There's someone at the door. 🐶 Please check if it's a good boy or a hooman. Doggies have been known to ring the doorbell. ``` === "JavaScript" ``` javascript fetch('https://ntfy.sh/mydoorbell', { method: 'POST', // PUT works too headers: { 'Click': 'https://home.nest.com/', 'Attach': 'https://nest.com/view/yAxkasd.jpg', 'Actions': 'http, Open door, https://api.nest.com/open/yAxkasd, clear=true', 'Email': 'phil@example.com' }, body: `There's someone at the door. 🐶 Please check if it's a good boy or a hooman. Doggies have been known to ring the doorbell.`, }) ``` === "Go" ``` go req, _ := http.NewRequest("POST", "https://ntfy.sh/mydoorbell", strings.NewReader(`There's someone at the door. 🐶 Please check if it's a good boy or a hooman. Doggies have been known to ring the doorbell.`)) req.Header.Set("Click", "https://home.nest.com/") req.Header.Set("Attach", "https://nest.com/view/yAxkasd.jpg") req.Header.Set("Actions", "http, Open door, https://api.nest.com/open/yAxkasd, clear=true") req.Header.Set("Email", "phil@example.com") http.DefaultClient.Do(req) ``` === "PowerShell" ``` powershell $Request = @{ Method = "POST" URI = "https://ntfy.sh/mydoorbell" Headers = @{ Click = "https://home.nest.com" Attach = "https://nest.com/view/yAxksd.jpg" Actions = "http, Open door, https://api.nest.com/open/yAxkasd, clear=true" Email = "phil@example.com" } Body = "There's someone at the door. 🐶`n `n Please check if it's a good boy or a hooman.`n Doggies have been known to ring the doorbell.`n" } Invoke-RestMethod @Request ``` === "Python" ``` python requests.post("https://ntfy.sh/mydoorbell", data="""There's someone at the door. 🐶 Please check if it's a good boy or a hooman. Doggies have been known to ring the doorbell.""".encode('utf-8'), headers={ "Click": "https://home.nest.com/", "Attach": "https://nest.com/view/yAxkasd.jpg", "Actions": "http, Open door, https://api.nest.com/open/yAxkasd, clear=true", "Email": "phil@example.com" }) ``` === "PHP" ``` php-inline file_get_contents('https://ntfy.sh/mydoorbell', false, stream_context_create([ 'http' => [ 'method' => 'POST', // PUT also works 'header' => "Content-Type: text/plain\r\n" . "Click: https://home.nest.com/\r\n" . "Attach: https://nest.com/view/yAxkasd.jpg\r\n" . "Actions": "http, Open door, https://api.nest.com/open/yAxkasd, clear=true\r\n" . "Email": "phil@example.com\r\n", 'content' => 'There\'s someone at the door. 🐶 Please check if it\'s a good boy or a hooman. Doggies have been known to ring the doorbell.' ] ])); ```
![priority notification](static/img/android-screenshot-notification-multiline.jpg){ width=500 }
Notification using a click action, a user action, with an external image attachment and forwarded via email
## Message title _Supported on:_ :material-android: :material-apple: :material-firefox: The notification title is typically set to the topic short URL (e.g. `ntfy.sh/mytopic`). To override the title, you can set the `X-Title` header (or any of its aliases: `Title`, `ti`, or `t`). === "Command line (curl)" ``` curl -H "X-Title: Dogs are better than cats" -d "Oh my ..." ntfy.sh/controversial curl -H "Title: Dogs are better than cats" -d "Oh my ..." ntfy.sh/controversial curl -H "t: Dogs are better than cats" -d "Oh my ..." ntfy.sh/controversial ``` === "ntfy CLI" ``` ntfy publish \ -t "Dogs are better than cats" \ controversial "Oh my ..." ``` === "HTTP" ``` http POST /controversial HTTP/1.1 Host: ntfy.sh Title: Dogs are better than cats Oh my ... ``` === "JavaScript" ``` javascript fetch('https://ntfy.sh/controversial', { method: 'POST', body: 'Oh my ...', headers: { 'Title': 'Dogs are better than cats' } }) ``` === "Go" ``` go req, _ := http.NewRequest("POST", "https://ntfy.sh/controversial", strings.NewReader("Oh my ...")) req.Header.Set("Title", "Dogs are better than cats") http.DefaultClient.Do(req) ``` === "PowerShell" ``` powershell $Request = @{ Method = "POST" URI = "https://ntfy.sh/controversial" Headers = @{ Title = "Dogs are better than cats" } Body = "Oh my ..." } Invoke-RestMethod @Request ``` === "Python" ``` python requests.post("https://ntfy.sh/controversial", data="Oh my ...", headers={ "Title": "Dogs are better than cats" }) ``` === "PHP" ``` php-inline file_get_contents('https://ntfy.sh/controversial', false, stream_context_create([ 'http' => [ 'method' => 'POST', 'header' => "Content-Type: text/plain\r\n" . "Title: Dogs are better than cats", 'content' => 'Oh my ...' ] ])); ```
![notification with title](static/img/notification-with-title.png){ width=500 }
Detail view of notification with title
!!! info ntfy supports UTF-8 in HTTP headers, but [not every library or programming language does](https://www.jmix.io/blog/utf-8-in-http-headers/). If non-ASCII characters are causing issues for you in the title (i.e. you're seeing `?` symbols), you may also encode any header (including the title) as [RFC 2047](https://datatracker.ietf.org/doc/html/rfc2047#section-2), e.g. `=?UTF-8?B?8J+HqfCfh6o=?=` ([base64](https://en.wikipedia.org/wiki/Base64)), or `=?UTF-8?Q?=C3=84pfel?=` ([quoted-printable](https://en.wikipedia.org/wiki/Quoted-printable)). ## Message priority _Supported on:_ :material-android: :material-apple: :material-firefox: All messages have a priority, which defines how urgently your phone notifies you. On Android, you can set custom notification sounds and vibration patterns on your phone to map to these priorities (see [Android config](subscribe/phone.md)). The following priorities exist: | Priority | Icon | ID | Name | Description | |----------------------|--------------------------------------------|-----|----------------|--------------------------------------------------------------------------------------------------------| | Max priority | ![min priority](static/img/priority-5.svg) | `5` | `max`/`urgent` | Really long vibration bursts, default notification sound with a pop-over notification. | | High priority | ![min priority](static/img/priority-4.svg) | `4` | `high` | Long vibration burst, default notification sound with a pop-over notification. | | **Default priority** | *(none)* | `3` | `default` | Short default vibration and sound. Default notification behavior. | | Low priority | ![min priority](static/img/priority-2.svg) | `2` | `low` | No vibration or sound. Notification will not visibly show up until notification drawer is pulled down. | | Min priority | ![min priority](static/img/priority-1.svg) | `1` | `min` | No vibration or sound. The notification will be under the fold in "Other notifications". | You can set the priority with the header `X-Priority` (or any of its aliases: `Priority`, `prio`, or `p`). === "Command line (curl)" ``` curl -H "X-Priority: 5" -d "An urgent message" ntfy.sh/phil_alerts curl -H "Priority: low" -d "Low priority message" ntfy.sh/phil_alerts curl -H p:4 -d "A high priority message" ntfy.sh/phil_alerts ``` === "ntfy CLI" ``` ntfy publish \ -p 5 \ phil_alerts An urgent message ``` === "HTTP" ``` http POST /phil_alerts HTTP/1.1 Host: ntfy.sh Priority: 5 An urgent message ``` === "JavaScript" ``` javascript fetch('https://ntfy.sh/phil_alerts', { method: 'POST', body: 'An urgent message', headers: { 'Priority': '5' } }) ``` === "Go" ``` go req, _ := http.NewRequest("POST", "https://ntfy.sh/phil_alerts", strings.NewReader("An urgent message")) req.Header.Set("Priority", "5") http.DefaultClient.Do(req) ``` === "PowerShell" ``` powershell $Request = @{ Method = 'POST' URI = "https://ntfy.sh/phil_alerts" Headers = @{ Priority = "5" } Body = "An urgent message" } Invoke-RestMethod @Request ``` === "Python" ``` python requests.post("https://ntfy.sh/phil_alerts", data="An urgent message", headers={ "Priority": "5" }) ``` === "PHP" ``` php-inline file_get_contents('https://ntfy.sh/phil_alerts', false, stream_context_create([ 'http' => [ 'method' => 'POST', 'header' => "Content-Type: text/plain\r\n" . "Priority: 5", 'content' => 'An urgent message' ] ])); ```
![priority notification](static/img/priority-detail-overview.png){ width=500 }
Detail view of priority notifications
## Tags & emojis 🥳 🎉 _Supported on:_ :material-android: :material-apple: :material-firefox: You can tag messages with emojis and other relevant strings: * **Emojis**: If a tag matches an [emoji short code](emojis.md), it'll be converted to an emoji and prepended to title or message. * **Other tags:** If a tag doesn't match, it will be listed below the notification. This feature is useful for things like warnings (⚠️, ️🚨, or 🚩), but also to simply tag messages otherwise (e.g. script names, hostnames, etc.). Use [the emoji short code list](emojis.md) to figure out what tags can be converted to emojis. Here's an **excerpt of emojis** I've found very useful in alert messages:
TagEmoji
+1👍
partying_face🥳
tada🎉
heavy_check_mark✔️
loudspeaker📢
......
TagEmoji
-1👎️
warning⚠️
rotating_light️🚨
triangular_flag_on_post🚩
skull💀
......
TagEmoji
facepalm🤦
no_entry
no_entry_sign🚫
cd💿
computer💻
......
You can set tags with the `X-Tags` header (or any of its aliases: `Tags`, `tag`, or `ta`). Specify multiple tags by separating them with a comma, e.g. `tag1,tag2,tag3`. === "Command line (curl)" ``` curl -H "X-Tags: warning,mailsrv13,daily-backup" -d "Backup of mailsrv13 failed" ntfy.sh/backups curl -H "Tags: horse,unicorn" -d "Unicorns are just horses with unique horns" ntfy.sh/backups curl -H ta:dog -d "Dogs are awesome" ntfy.sh/backups ``` === "ntfy CLI" ``` ntfy publish \ --tags=warning,mailsrv13,daily-backup \ backups "Backup of mailsrv13 failed" ``` === "HTTP" ``` http POST /backups HTTP/1.1 Host: ntfy.sh Tags: warning,mailsrv13,daily-backup Backup of mailsrv13 failed ``` === "JavaScript" ``` javascript fetch('https://ntfy.sh/backups', { method: 'POST', body: 'Backup of mailsrv13 failed', headers: { 'Tags': 'warning,mailsrv13,daily-backup' } }) ``` === "Go" ``` go req, _ := http.NewRequest("POST", "https://ntfy.sh/backups", strings.NewReader("Backup of mailsrv13 failed")) req.Header.Set("Tags", "warning,mailsrv13,daily-backup") http.DefaultClient.Do(req) ``` === "PowerShell" ``` powershell $Request = @{ Method = "POST" URI = "https://ntfy.sh/backups" Headers = @{ Tags = "warning,mailsrv13,daily-backup" } Body = "Backup of mailsrv13 failed" } Invoke-RestMethod @Request ``` === "Python" ``` python requests.post("https://ntfy.sh/backups", data="Backup of mailsrv13 failed", headers={ "Tags": "warning,mailsrv13,daily-backup" }) ``` === "PHP" ``` php-inline file_get_contents('https://ntfy.sh/backups', false, stream_context_create([ 'http' => [ 'method' => 'POST', 'header' => "Content-Type: text/plain\r\n" . "Tags: warning,mailsrv13,daily-backup", 'content' => 'Backup of mailsrv13 failed' ] ])); ```
![priority notification](static/img/notification-with-tags.png){ width=500 }
Detail view of notifications with tags
!!! info ntfy supports UTF-8 in HTTP headers, but [not every library or programming language does](https://www.jmix.io/blog/utf-8-in-http-headers/). If non-ASCII characters are causing issues for you in the title (i.e. you're seeing `?` symbols), you may also encode the tags header or individual tags as [RFC 2047](https://datatracker.ietf.org/doc/html/rfc2047#section-2), e.g. `tag1,=?UTF-8?B?8J+HqfCfh6o=?=` ([base64](https://en.wikipedia.org/wiki/Base64)), or `=?UTF-8?Q?=C3=84pfel?=,tag2` ([quoted-printable](https://en.wikipedia.org/wiki/Quoted-printable)). ## Markdown formatting _Supported on:_ :material-android: :material-firefox: You can format messages using [Markdown](https://www.markdownguide.org/basic-syntax/) 🤩. That means you can use **bold text**, *italicized text*, links, images, and more. Supported Markdown features (web app only for now): - [Emphasis](https://www.markdownguide.org/basic-syntax/#emphasis) such as **bold** (`**bold**`), *italics* (`*italics*`) - [Links](https://www.markdownguide.org/basic-syntax/#links) (`[some tool](https://ntfy.sh)`) - [Images](https://www.markdownguide.org/basic-syntax/#images) (`![some image](https://bing.com/logo.png)`) - [Code blocks](https://www.markdownguide.org/basic-syntax/#code-blocks) (` ```code blocks``` `) and [inline code](https://www.markdownguide.org/basic-syntax/#inline-code) (`` `inline code` ``) - [Headings](https://www.markdownguide.org/basic-syntax/#headings) (`# headings`, `## headings`, etc.) - [Lists](https://www.markdownguide.org/basic-syntax/#lists) (`- lists`, `1. lists`, etc.) - [Blockquotes](https://www.markdownguide.org/basic-syntax/#blockquotes) (`> blockquotes`) - [Horizontal rules](https://www.markdownguide.org/basic-syntax/#horizontal-rules) (`---`) By default, messages sent to ntfy are rendered as plain text. To enable Markdown, set the `X-Markdown` header (or any of its aliases: `Markdown`, or `md`) to `true` (or `1` or `yes`), or set the `Content-Type` header to `text/markdown`. Here's an example of how to enable Markdown formatting: === "Command line (curl)" ``` curl \ -d "Look ma, **bold text**, *italics*, ..." \ -H "Markdown: yes" \ ntfy.sh/mytopic ``` === "ntfy CLI" ``` ntfy publish \ --markdown \ mytopic \ "Look ma, **bold text**, *italics*, ..." ``` === "HTTP" ``` http POST /mytopic HTTP/1.1 Host: ntfy.sh Markdown: yes Look ma, **bold text**, *italics*, ... ``` === "JavaScript" ``` javascript fetch('https://ntfy.sh/mytopic', { method: 'POST', // PUT works too body: 'Look ma, **bold text**, *italics*, ...', headers: { 'Markdown': 'yes' } }) ``` === "Go" ``` go http.Post("https://ntfy.sh/mytopic", "text/markdown", strings.NewReader("Look ma, **bold text**, *italics*, ...")) // or req, _ := http.NewRequest("POST", "https://ntfy.sh/mytopic", strings.NewReader("Look ma, **bold text**, *italics*, ...")) req.Header.Set("Markdown", "yes") http.DefaultClient.Do(req) ``` === "PowerShell" ``` powershell $Request = @{ Method = "POST" URI = "https://ntfy.sh/mytopic" Body = "Look ma, **bold text**, *italics*, ..." Headers = @{ Markdown = "yes" } } Invoke-RestMethod @Request ``` === "Python" ``` python requests.post("https://ntfy.sh/mytopic", data="Look ma, **bold text**, *italics*, ...", headers={ "Markdown": "yes" }) ``` === "PHP" ``` php-inline file_get_contents('https://ntfy.sh/mytopic', false, stream_context_create([ 'http' => [ 'method' => 'POST', // PUT also works 'header' => 'Content-Type: text/markdown', // ! 'content' => 'Look ma, **bold text**, *italics*, ...' ] ])); ``` Here's what that looks like in the web app:
![markdown](static/img/web-markdown.png){ width=500 }
Markdown formatting in the web app
## Click action _Supported on:_ :material-android: :material-apple: :material-firefox: You can define which URL to open when a notification is clicked. This may be useful if your notification is related to a Zabbix alert or a transaction that you'd like to provide the deep-link for. Tapping the notification will open the web browser (or the app) and open the website. To define a click action for the notification, pass a URL as the value of the `X-Click` header (or its alias `Click`). If you pass a website URL (`http://` or `https://`) the web browser will open. If you pass another URI that can be handled by another app, the responsible app may open. Examples: * `http://` or `https://` will open your browser (or an app if it registered for a URL) * `mailto:` links will open your mail app, e.g. `mailto:phil@example.com` * `geo:` links will open Google Maps, e.g. `geo:0,0?q=1600+Amphitheatre+Parkway,+Mountain+View,+CA` * `ntfy://` links will open ntfy (see [ntfy:// links](subscribe/phone.md#ntfy-links)), e.g. `ntfy://ntfy.sh/stats` * `twitter://` links will open Twitter, e.g. `twitter://user?screen_name=..` * ... Here's an example that will open Reddit when the notification is clicked: === "Command line (curl)" ``` curl \ -d "New messages on Reddit" \ -H "Click: https://www.reddit.com/message/messages" \ ntfy.sh/reddit_alerts ``` === "ntfy CLI" ``` ntfy publish \ --click="https://www.reddit.com/message/messages" \ reddit_alerts "New messages on Reddit" ``` === "HTTP" ``` http POST /reddit_alerts HTTP/1.1 Host: ntfy.sh Click: https://www.reddit.com/message/messages New messages on Reddit ``` === "JavaScript" ``` javascript fetch('https://ntfy.sh/reddit_alerts', { method: 'POST', body: 'New messages on Reddit', headers: { 'Click': 'https://www.reddit.com/message/messages' } }) ``` === "Go" ``` go req, _ := http.NewRequest("POST", "https://ntfy.sh/reddit_alerts", strings.NewReader("New messages on Reddit")) req.Header.Set("Click", "https://www.reddit.com/message/messages") http.DefaultClient.Do(req) ``` === "PowerShell" ``` powershell $Request = @{ Method = "POST" URI = "https://ntfy.sh/reddit_alerts" Headers = @{ Click="https://www.reddit.com/message/messages" } Body = "New messages on Reddit" } Invoke-RestMethod @Request ``` === "Python" ``` python requests.post("https://ntfy.sh/reddit_alerts", data="New messages on Reddit", headers={ "Click": "https://www.reddit.com/message/messages" }) ``` === "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" . "Click: https://www.reddit.com/message/messages", 'content' => 'New messages on Reddit' ] ])); ``` ## Icons _Supported on:_ :material-android: You can include an icon that will appear next to the text of the notification. Simply pass the `X-Icon` header or query parameter (or its alias `Icon`) to specify the URL that the icon is located at. The client will automatically download the icon (unless it is already cached locally, and less than 24 hours old), and show it in the notification. Icons are cached locally in the client until the notification is deleted. **Only JPEG and PNG images are supported at this time**. Here's an example showing how to include an icon: === "Command line (curl)" ``` curl \ -H "Icon: https://styles.redditmedia.com/t5_32uhe/styles/communityIcon_xnt6chtnr2j21.png" \ -H "Title: Kodi: Resuming Playback" \ -H "Tags: arrow_forward" \ -d "The Wire, S01E01" \ ntfy.sh/tvshows ``` === "ntfy CLI" ``` ntfy publish \ --icon="https://styles.redditmedia.com/t5_32uhe/styles/communityIcon_xnt6chtnr2j21.png" \ --title="Kodi: Resuming Playback" \ --tags="arrow_forward" \ tvshows \ "The Wire, S01E01" ``` === "HTTP" ``` http POST /tvshows HTTP/1.1 Host: ntfy.sh Icon: https://styles.redditmedia.com/t5_32uhe/styles/communityIcon_xnt6chtnr2j21.png Tags: arrow_forward Title: Kodi: Resuming Playback The Wire, S01E01 ``` === "JavaScript" ``` javascript fetch('https://ntfy.sh/tvshows', { method: 'POST', headers: { 'Icon': 'https://styles.redditmedia.com/t5_32uhe/styles/communityIcon_xnt6chtnr2j21.png', 'Title': 'Kodi: Resuming Playback', 'Tags': 'arrow_forward' }, body: "The Wire, S01E01" }) ``` === "Go" ``` go req, _ := http.NewRequest("POST", "https://ntfy.sh/tvshows", strings.NewReader("The Wire, S01E01")) req.Header.Set("Icon", "https://styles.redditmedia.com/t5_32uhe/styles/communityIcon_xnt6chtnr2j21.png") req.Header.Set("Tags", "arrow_forward") req.Header.Set("Title", "Kodi: Resuming Playback") http.DefaultClient.Do(req) ``` === "PowerShell" ``` powershell $Request = @{ Method = "POST" URI = "https://ntfy.sh/tvshows" Headers = @{ Title = "Kodi: Resuming Playback" Tags = "arrow_forward" Icon = "https://styles.redditmedia.com/t5_32uhe/styles/communityIcon_xnt6chtnr2j21.png" } Body = "The Wire, S01E01" } Invoke-RestMethod @Request ``` === "Python" ``` python requests.post("https://ntfy.sh/tvshows", data="The Wire, S01E01", headers={ "Title": "Kodi: Resuming Playback", "Tags": "arrow_forward", "Icon": "https://styles.redditmedia.com/t5_32uhe/styles/communityIcon_xnt6chtnr2j21.png" }) ``` === "PHP" ``` php-inline file_get_contents('https://ntfy.sh/tvshows', false, stream_context_create([ 'http' => [ 'method' => 'PUT', 'header' => "Content-Type: text/plain\r\n" . // Does not matter "Title: Kodi: Resuming Playback\r\n" . "Tags: arrow_forward\r\n" . "Icon: https://styles.redditmedia.com/t5_32uhe/styles/communityIcon_xnt6chtnr2j21.png", ], 'content' => "The Wire, S01E01" ])); ``` Here's an example of how it will look on Android:
![file attachment](static/img/android-screenshot-icon.png){ width=500 }
Custom icon from an external URL
## Attachments _Supported on:_ :material-android: :material-firefox: You can **send images and other files to your phone** as attachments to a notification. The attachments are then downloaded onto your phone (depending on size and setting automatically), and can be used from the Downloads folder. There are two different ways to send attachments: * sending [a local file](#attach-local-file) via PUT, e.g. from `~/Flowers/flower.jpg` or `ringtone.mp3` * or by [passing an external URL](#attach-file-from-a-url) as an attachment, e.g. `https://f-droid.org/F-Droid.apk` ### Attach local file To **send a file from your computer** as an attachment, you can send it as the PUT request body. If a message is greater than the maximum message size (4,096 bytes) or consists of non UTF-8 characters, the ntfy server will automatically detect the mime type and size, and send the message as an attachment file. To send smaller text-only messages or files as attachments, you must pass a filename by passing the `X-Filename` header or query parameter (or any of its aliases `Filename`, `File` or `f`). By default, and how ntfy.sh is configured, the **max attachment size is 15 MB** (with 100 MB total per visitor). Attachments **expire after 3 hours**, which typically is plenty of time for the user to download it, or for the Android app to auto-download it. Please also check out the [other limits below](#limitations). Here's an example showing how to upload an image: === "Command line (curl)" ``` curl \ -T flower.jpg \ -H "Filename: flower.jpg" \ ntfy.sh/flowers ``` === "ntfy CLI" ``` ntfy publish \ --file=flower.jpg \ flowers ``` === "HTTP" ``` http PUT /flowers HTTP/1.1 Host: ntfy.sh Filename: flower.jpg Content-Type: 52312 (binary JPEG data) ``` === "JavaScript" ``` javascript fetch('https://ntfy.sh/flowers', { method: 'PUT', body: document.getElementById("file").files[0], headers: { 'Filename': 'flower.jpg' } }) ``` === "Go" ``` go file, _ := os.Open("flower.jpg") req, _ := http.NewRequest("PUT", "https://ntfy.sh/flowers", file) req.Header.Set("Filename", "flower.jpg") http.DefaultClient.Do(req) ``` === "PowerShell" ``` powershell $Request = @{ Method = "POST" Uri = "ntfy.sh/flowers" InFile = "flower.jpg" Headers = @{"Filename" = "flower.jpg"} } Invoke-RestMethod @Request ``` === "Python" ``` python requests.put("https://ntfy.sh/flowers", data=open("flower.jpg", 'rb'), headers={ "Filename": "flower.jpg" }) ``` === "PHP" ``` php-inline file_get_contents('https://ntfy.sh/flowers', false, stream_context_create([ 'http' => [ 'method' => 'PUT', 'header' => "Content-Type: application/octet-stream\r\n" . // Does not matter "Filename: flower.jpg", 'content' => file_get_contents('flower.jpg') // Dangerous for large files ] ])); ``` Here's what that looks like on Android:
![image attachment](static/img/android-screenshot-attachment-image.png){ width=500 }
Image attachment sent from a local file
### Attach file from a URL Instead of sending a local file to your phone, you can use **an external URL** to specify where the attachment is hosted. This could be a Dropbox link, a file from social media, or any other publicly available URL. Since the files are externally hosted, the expiration or size limits from above do not apply here. To attach an external file, simple pass the `X-Attach` header or query parameter (or any of its aliases `Attach` or `a`) to specify the attachment URL. It can be any type of file. ntfy will automatically try to derive the file name from the URL (e.g `https://example.com/flower.jpg` will yield a filename `flower.jpg`). To override this filename, you may send the `X-Filename` header or query parameter (or any of its aliases `Filename`, `File` or `f`). Here's an example showing how to attach an APK file: === "Command line (curl)" ``` curl \ -X POST \ -H "Attach: https://f-droid.org/F-Droid.apk" \ ntfy.sh/mydownloads ``` === "ntfy CLI" ``` ntfy publish \ --attach="https://f-droid.org/F-Droid.apk" \ mydownloads ``` === "HTTP" ``` http POST /mydownloads HTTP/1.1 Host: ntfy.sh Attach: https://f-droid.org/F-Droid.apk ``` === "JavaScript" ``` javascript fetch('https://ntfy.sh/mydownloads', { method: 'POST', headers: { 'Attach': 'https://f-droid.org/F-Droid.apk' } }) ``` === "Go" ``` go req, _ := http.NewRequest("POST", "https://ntfy.sh/mydownloads", file) req.Header.Set("Attach", "https://f-droid.org/F-Droid.apk") http.DefaultClient.Do(req) ``` === "PowerShell" ``` powershell $Request = @{ Method = "POST" URI = "https://ntfy.sh/mydownloads" Headers = @{ Attach="https://f-droid.org/F-Droid.apk" } } Invoke-RestMethod @Request ``` === "Python" ``` python requests.put("https://ntfy.sh/mydownloads", headers={ "Attach": "https://f-droid.org/F-Droid.apk" }) ``` === "PHP" ``` php-inline file_get_contents('https://ntfy.sh/mydownloads', false, stream_context_create([ 'http' => [ 'method' => 'PUT', 'header' => "Content-Type: text/plain\r\n" . // Does not matter "Attach: https://f-droid.org/F-Droid.apk", ] ])); ```
![file attachment](static/img/android-screenshot-attachment-file.png){ width=500 }
File attachment sent from an external URL
## Action buttons _Supported on:_ :material-android: :material-apple: :material-firefox: You can add action buttons to notifications to allow yourself to react to a notification directly. This is incredibly useful and has countless applications. You can control your home appliances (open/close garage door, change temperature on thermostat, ...), react to common monitoring alerts (clear logs when disk is full, ...), and many other things. The sky is the limit. As of today, the following actions are supported: * [`view`](#open-websiteapp): Opens a website or app when the action button is tapped * [`broadcast`](#send-android-broadcast): Sends an [Android broadcast](https://developer.android.com/guide/components/broadcasts) intent when the action button is tapped (only supported on Android) * [`http`](#send-http-request): Sends HTTP POST/GET/PUT request when the action button is tapped * [`copy`](#copy-to-clipboard): Copies a given value to the clipboard when the action button is tapped Here's an example of what a notification with actions can look like:
![notification with actions](static/img/android-screenshot-notification-actions.png){ width=500 }
Notification with two user actions
### Defining actions You can define **up to three user actions** in your notifications, using either of the following methods: * In the [`X-Actions` header](#using-a-header), using a simple comma-separated format * As a [JSON array](#using-a-json-array) in the `actions` key, when [publishing as JSON](#publish-as-json) #### Using a header To define actions using the `X-Actions` header (or any of its aliases: `Actions`, `Action`), use the following format: === "Header format (long)" ``` action=, label=, paramN=... [; action=, label=, ...] ``` === "Header format (short)" ``` , , paramN=... [; , , ...] ``` Multiple actions are separated by a semicolon (`;`), and key/value pairs are separated by commas (`,`). Values may be quoted with double quotes (`"`) or single quotes (`'`) if the value itself contains commas or semicolons. Each action type has a short format where some key prefixes can be omitted: * [`view`](#open-websiteapp): `view,