Repository: prometheus/alertmanager
Pull request #3600 (2023-11-12 15:02:27)

feat: add rocketchat notifier
This adds native support for Rocketchat notifications. It uses the Rocketchat REST API. For authentication access tokens are used. This is a solution for #3546

TheMeier: > I'm not a maintainer but in general looks good! You might have to remove use of the SDK as 1. these tend to be quite large and 2. all of the other integrations avoid using their respective SDKs.
> 
> However, I'd wait until @simonpasquier or @gotjosh review before making further changes as I don't want to give you incorrect advice! 🙂

@grobinson-grafana thanks for your feedback. 
Actually since I had a night to sleep on it, i already am planning to refactor it since it feels so alien, and some things do not really work like the retry mechanism.
So I will probably set this to Draft and work on it a bit more in the coming days.
TheMeier: I removed the rocketchat SDK. Do you prefer that i squash the commits in this PR?
TheMeier: @roidelapluie I am aware you have a lot on your plate. I just wanted to kindly ask if there is any chance of this getting some love, since you mentioned some interest in the referenced issue.

TheMeier: @gotjosh can I get a review please?
grobinson-grafana: @TheMeier I started to look at this, but I'm not sure how to test this. I've run into a number of issues getting RocketChat to run on Apple M1 as it seems there are no docker images for `linux/arm64`. I've also tried building from source and I'm running into a number of errors with `yarn`. I'm following these instructions [here](https://github.com/RocketChat/Rocket.Chat?tab=readme-ov-file#prerequisites).

Have you been able to test these changes?
TheMeier: @grobinson-grafana I have tested them with a real-live inhouse rocketchat server. 
TheMeier: Any chance for some progress here or in  #3776 ?
TheMeier: Am I missing something, or is this functionality not available yet https://github.com/golang/go/issues/38479

TheMeier: I did adapt the code with a custom RoundTripper
TheMeier: Can someone point me to the cause of the linter failing:

```
  Error: ui/react-app/embed.go:20:12: pattern dist/26.js.gz: no matching files found (typecheck)
  //go:embed dist/26.js.gz dist/26.js.map.gz dist/732.js.gz dist/732.js.map.gz dist/959.js.gz dist/959.js.map.gz dist/97.js.gz dist/97.js.map.gz dist/index.html.gz dist/main.js.gz dist/main.js.map.gz
             ^
  ```
  https://github.com/prometheus/alertmanager/actions/runs/11475295003/job/31932844239?pr=3600#step:5:30
grobinson-grafana: > I think for slack we expect the user to provide the suffix api/v1/chat.postMessage. Let's do the same here. https://github.com/prometheus/alertmanager/blob/main/notify/slack/slack.go#L197-L206

The reason I asked if we can remove the suffix is I think RocketChat also supports webhooks (and appending a URL suffix would break them). Do you think you could also test this to make sure it works too?

https://docs.rocket.chat/docs/integrations#create-a-new-incoming-webhook

Thank you very much for your patience. We would like to merge your PR as soon as these last two nits are addressed.
TheMeier: > asked if we can remove the suffix is I think Ro


TheMeier: > > I think for slack we expect the user to provide the suffix api/v1/chat.postMessage. Let's do the same here. https://github.com/prometheus/alertmanager/blob/main/notify/slack/slack.go#L197-L206
> 
> The reason I asked if we can remove the suffix is I think RocketChat also supports webhooks (and appending a URL suffix would break them). Do you think you could also test this to make sure it works too?
> 
> https://docs.rocket.chat/docs/integrations#create-a-new-incoming-webhook
> 
> Thank you very much for your patience. We would like to merge your PR as soon as these last two nits are addressed.


I did remove it from the code and added it to the docs for configuration. This notifier 
grobinson-grafana: > Point about the webhooks is as you can see from the docs, it requires a custom webhook script. And that may be very different for different rocketchat servers. In fact one of the motivations for this notifier was to get away from a "broken" custom webhook script on the server side.

Oh... I've misunderstood. I thought it would be a 1:1 with Slack where it would support both: webhooks and bot tokens.

In which case, I think I might make sense to append the URL suffix like you had the first time around, as this will allow us to support other features like replies and reactions in future, if we ever need it. I'm sorry to ask you to undo this change.
TheMeier: > > Point about the webhooks is as you can see from the docs, it requires a custom webhook script. And that may be very different for different rocketchat servers. In fact one of the motivations for this notifier was to get away from a "broken" custom webhook script on the server side.
> 
> Oh... I've misunderstood. I thought it would be a 1:1 with Slack where it would support both: webhooks and bot tokens.
> 
> In which case, I think I might make sense to append the URL suffix like you had the first time around, as this will allow us to support other features like replies and reactions in future, if we ever need it. I'm sorry to ask you to undo this change.

No problem. Already done ;)
TheMeier: damn flaky tests :(


Patch:
diff --git a/asset/assets_vfsdata.go b/asset/assets_vfsdata.go
index eeacb03a..65805efb 100644
--- a/asset/assets_vfsdata.go
+++ b/asset/assets_vfsdata.go
@@ -163,9 +163,9 @@ var Assets = func() http.FileSystem {
 		"/templates/default.tmpl": &vfsgen۰CompressedFileInfo{
 			name:             "default.tmpl",
 			modTime:          time.Date(1970, 1, 1, 0, 0, 1, 0, time.UTC),
-			uncompressedSize: 7656,
+			uncompressedSize: 8053,
 
-			compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\xec\x59\x4d\x6f\xe3\x36\x10\xbd\xfb\x57\x0c\xb4\x3d\xc4\x87\x68\x8b\x1e\x03\x04\xc5\xa2\xe8\xc7\x21\x2d\x0a\x2f\xd2\x4b\x51\x08\x8c\x34\x56\x98\x50\xa4\x42\x8e\xec\x18\x8e\xfe\x7b\x41\x49\x96\xa9\x0f\xdb\x94\xd7\x3d\xd5\xb7\x98\x9a\x79\x33\x7c\x6f\x34\x43\x2a\xdb\x2d\x24\xb8\xe4\x12\x21\x88\x22\x26\x50\x53\xc6\x24\x4b\x51\x07\x50\x96\x5f\x9c\xdf\xdb\x2d\xa0\x4c\xa0\x2c\x67\x07\x5d\x1e\x17\x0f\xd6\x6b\xbb\x85\xf0\xe7\x77\x42\x2d\x99\x78\x5c\x3c\x40\x59\x7e\xfe\xf4\xb9\xb2\x33\x3f\x6a\x8c\x91\xaf\x50\xdf\x5b\xa3\x45\xf3\x03\x3e\xa0\xd0\xe2\xad\x40\xbd\xa9\xdd\x9b\x40\xdd\x48\xa6\x78\x7a\xc1\x98\x6c\x84\xbf\xad\xf7\x57\x62\x54\x18\xf8\x00\x52\x8f\x79\x8e\xba\x76\xe5\x4b\xc0\xb7\xf6\x61\xb0\xe4\x9a\xcb\xd4\xfa\xdc\x59\x9f\x6a\x43\x26\xfc\xa5\x5a\x85\x0f\x10\x28\xdd\x88\xff\x80\x35\xfa\x55\xab\x22\x7f\x60\x4f\x28\x4c\xf8\x55\x69\xc2\xe4\x4f\xc6\xb5\x09\xff\x62\xa2\x40\x1b\xf0\x45\x71\x09\x01\x58\x54\xa8\x43\xa6\x04\x37\x16\x2b\xfc\x49\x65\x99\x92\xb5\xf3\xbc\x59\x73\xf0\xe6\x50\x96\x37\xdb\x2d\xac\x39\x3d\x77\x8d\xc3\x05\x66\x6a\x85\xdd\xe8\x7f\xb0\x0c\x4d\xc3\xe8\x58\xf4\x36\xf1\x79\xfb\xd7\x01\x99\x12\x34\xb1\xe6\x39\x71\x25\x83\x23\x1c\x13\xbe\x53\x2d\x69\x24\xb8\xa1\xc6\x54\x33\x99\x22\x84\x50\x96\x75\x5e\x77\xb3\xfd\xe2\x90\x27\xcb\xca\x6d\x45\xa4\x4d\xdf\xfe\xba\x87\x76\x03\x4d\x62\x75\xf0\x2f\x52\x2a\x62\x36\xa7\x0e\xa4\xb3\x7c\x1e\xee\x57\x55\xe8\x18\xef\x6a\x31\x51\xa2\x66\xa4\x74\x5d\x89\xb3\x11\xa2\x8e\x52\x10\x65\x4c\xbf\x26\x6a\x2d\x07\x5c\xcc\x7c\xc9\xf0\xcc\x7a\x36\x9d\x0e\x5f\x64\x2f\x42\x66\xe3\x8c\x18\xc1\xe2\xd7\x30\xc1\x25\x2b\x04\x85\xc4\x49\x60\x43\x05\x61\x96\x0b\x46\xdd\x97\x33\x3c\x54\x83\x5d\x9c\xc2\xd8\xf6\x90\x8d\x41\x75\x9b\x90\x27\xde\x92\x09\xf1\xc4\xe2\xd7\x01\xde\x68\xfa\x16\x14\x3e\xe0\x94\xa1\xe0\xf2\xd5\x3b\x83\xb8\xc9\x80\x27\x81\x9f\x43\xae\xd1\xd6\x9a\xa7\xb5\x93\xd0\x51\xc6\xaa\x1e\xec\x99\x32\x8f\x95\xc4\x4c\xbd\xf0\xc0\xdf\xbe\xd0\xc2\x37\x63\xff\xcd\x2d\x95\xa2\x7a\xe2\x38\x35\xe8\x9a\xe7\x76\x6b\x49\x41\x9b\xd6\x65\xd8\xd0\xa6\x95\xe3\x10\x31\x16\x1c\x25\x9d\x5f\x90\x87\x10\xf7\x53\xf1\x3c\xcd\x86\xb8\x5c\x1a\x62\x32\x46\x33\x82\x3b\xe8\xe0\xe1\x61\x56\x55\x6e\x52\x94\x1c\x5b\xe0\x0c\x8d\x61\xe9\x79\xef\xf7\x00\x6c\xa8\x50\x33\xf0\x0e\x34\xb4\xd1\x09\x37\xeb\xcd\xd7\xce\x00\x9f\xc3\xf7\x70\x6b\x1b\x67\xb5\x08\xf5\x62\xd5\x3a\x8f\x33\xd2\x3d\x05\x54\x41\x6e\x9d\x1d\x8d\xc4\x5b\xa0\x51\x62\x85\x49\x2f\xe2\x6e\xd9\x3f\xe6\xce\x63\x10\xf5\xd6\x87\x52\x53\xf5\xf1\xe9\xd5\xd4\x51\x7d\x8d\xf1\x33\xa3\xa9\x9a\xcf\xae\xfa\x1d\xd1\xcf\x3d\x28\x3f\x6a\x31\xc0\x1b\xd5\xe7\x80\xea\x3d\x7d\x48\x45\x76\x58\x1e\xec\xa4\x43\xf3\x9c\x69\xda\x4c\xb0\x27\x96\xfa\x5a\xb3\x14\x25\x45\xfd\x11\xd7\xad\xaf\x15\x8f\x49\x69\x95\x9b\x7d\xd9\x12\x23\x8c\xba\x85\x76\xad\xa5\x69\xbd\x60\xc8\x2a\x4a\xe2\xb4\x89\x12\x6e\x72\xc1\x36\xd1\x81\xd3\xd4\xe9\xc6\x3d\x44\xce\x94\xe4\xa4\x2c\x21\x11\x29\x25\x26\x8e\xc4\xce\xec\x2a\xcc\xb3\x5a\xa1\xbe\xc0\xf9\x71\x00\xf5\xdf\xd7\xd3\x65\xca\xc9\xbf\x9a\x2e\x57\x4c\xc3\x23\xfd\x31\x26\xf7\x67\xba\x29\x33\xc5\x3d\xcd\x49\xe7\x65\xdf\x5f\xd3\xa7\xdf\x11\x1c\x9c\xab\xbc\x53\xe4\x75\x59\x24\x14\x98\x6a\x96\x8d\x51\xf9\xbf\x25\x25\xe1\x26\x56\x3a\xb9\x40\x23\xea\x23\x5d\xd9\xb5\xc7\x84\x27\x7c\xbf\xbe\xba\xdf\xcc\x63\x66\x08\x59\xe6\x36\xd3\x2c\x63\x7a\x73\x56\x9d\xf6\xb1\xce\xaf\xf8\x01\x52\x73\xb3\xf7\x91\xe9\x13\x4c\x12\xca\xf9\xdc\xf6\xcd\x8a\xb5\xa1\x7d\x35\x1b\x09\x7e\x86\x78\xab\x1f\x2e\x47\xb9\x8b\x75\x25\x7d\x8c\xf4\x17\xae\xd9\x45\x5e\x97\x0e\x50\xef\xdb\xc5\x95\xf3\x59\x75\x2d\x19\xe5\x2a\xd7\x5c\x69\x6e\x6f\x9c\xb7\xcd\xed\xe5\xbb\xdd\x12\xdc\xdd\x43\x10\xec\x2e\x35\xbb\xef\xd9\x9d\xdd\x5a\x1f\x00\x80\xca\xcf\xe0\x0a\x77\x7e\x5c\x26\xf8\xbe\xfb\xa4\x0e\xc1\xee\x51\xd0\xf1\xe0\x4b\xb8\xc1\x37\xc7\x31\x88\x35\x27\x1e\x33\x11\xcc\x5b\xc3\x16\xbe\x4d\xeb\x1e\x82\xdf\x78\xfa\xdc\xc5\x42\x61\xb0\x02\x64\x32\xe9\xa3\xae\x99\x96\x5c\xa6\xc1\x1c\x6e\x24\x3a\x40\x35\xcc\xfc\x44\xac\xdf\x31\xe1\x45\xe6\x1f\x8d\xcb\xa5\xb2\xa1\xec\xea\x3e\xd4\xc9\x30\x0f\x6a\xdd\x8b\x21\x93\x56\x13\xf7\xef\xfa\x7f\x64\x2e\x74\xc7\xad\xab\x53\x5b\x18\x83\xd8\x93\xd4\x9a\xac\x98\x87\x6a\x17\x57\xce\x4b\xbd\xcb\x29\x78\x5a\xc5\xbe\x92\xa7\x94\xdd\x23\xf5\x9f\xfe\x1b\x00\x00\xff\xff\x7a\xa6\xa9\x68\xe8\x1d\x00\x00"),
+			compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\xec\x59\xcf\x6f\xeb\x36\x0c\xbe\xe7\xaf\x20\xfc\x76\x68\x0e\xf5\x1b\x76\x2c\x50\x0c\x0f\xc3\x7e\x1c\xba\x61\x68\xd1\x5d\x86\x21\x50\x6d\xc6\x55\x2b\x4b\xae\x44\x27\x0d\xd2\xfc\xef\x83\x6c\xc7\x91\x6c\x27\x91\xd3\xec\xb4\xdc\x12\x99\xfc\x48\x7f\x1f\x4d\x4a\xf6\x7a\x0d\x29\xce\xb9\x44\x88\x66\x33\x26\x50\x53\xce\x24\xcb\x50\x47\xb0\xd9\x7c\x73\xfe\xaf\xd7\x80\x32\x85\xcd\x66\xb2\xd7\xe5\xf1\xfe\xce\x7a\xad\xd7\x10\xff\xfc\x4e\xa8\x25\x13\x8f\xf7\x77\xb0\xd9\x7c\xfd\xf2\xb5\xb2\x33\x3f\x6a\x4c\x90\x2f\x50\xdf\x5a\xa3\xfb\xe6\x0f\x7c\x40\xa9\xc5\x5b\x89\x7a\x55\xbb\x37\x81\xfc\x48\xa6\x7c\x7a\xc1\x84\x6c\x84\xbf\xad\xf7\x03\x31\x2a\x0d\x7c\x00\xa9\xc7\xa2\x40\x5d\xbb\xf2\x39\xe0\x5b\x7b\x31\x9a\x73\xcd\x65\x66\x7d\x6e\xac\x4f\x75\x43\x26\xfe\xa5\x5a\x85\x0f\x10\x28\xdd\x88\xff\x80\x35\xfa\x55\xab\xb2\xb8\x63\x4f\x28\x4c\xfc\xa0\x34\x61\xfa\x27\xe3\xda\xc4\x7f\x31\x51\xa2\x0d\xf8\xa2\xb8\x84\x08\x2c\x2a\xd4\x21\x33\x82\x2b\x8b\x15\xff\xa4\xf2\x5c\xc9\xda\x79\xda\xac\x39\x78\x53\xd8\x6c\xae\xd6\x6b\x58\x72\x7a\xf6\x8d\xe3\x7b\xcc\xd5\x02\xfd\xe8\x7f\xb0\x1c\x4d\xc3\xe8\x50\xf4\x36\xf1\x69\xfb\x6b\x8f\x4c\x29\x9a\x44\xf3\x82\xb8\x92\xd1\x01\x8e\x09\xdf\xa9\x96\x74\x26\xb8\xa1\xc6\x54\x33\x99\x21\xc4\xb0\xd9\xd4\x79\xdd\x4c\x76\x8b\x7d\x9e\x2c\x2b\xd7\x15\x91\x36\x7d\xfb\xef\x16\xda\x1b\x68\x12\xab\x83\x7f\x93\x52\x11\xb3\x39\x79\x90\xce\xf2\x69\xb8\x0f\xaa\xd4\x09\xde\xd4\x62\xa2\x44\xcd\x48\xe9\xba\x12\x27\x03\x44\x1d\xa4\x60\x96\x33\xfd\x9a\xaa\xa5\xec\x71\x31\x09\x25\x23\x30\xeb\xc9\x78\x3a\x42\x91\x83\x08\x99\x0c\x33\x62\x04\x4b\x5e\xe3\x14\xe7\xac\x14\x14\x13\x27\x81\x0d\x15\x84\x79\x21\x18\xf9\x0f\x67\xbc\xaf\x06\x7d\x9c\xd2\xd8\xf6\x90\x0f\x41\xf9\x4d\x28\x10\x6f\xce\x84\x78\x62\xc9\x6b\x0f\x6f\x30\x7d\x0b\x0a\x1f\x70\xcc\x50\x70\xf9\x1a\x9c\x41\xd2\x64\xc0\xd3\x28\xcc\xa1\xd0\x68\x6b\x2d\xd0\xda\x49\xe8\x20\x63\x55\x0f\x0e\x4c\x99\x27\x4a\x62\xae\x5e\x78\x14\x6e\x5f\x6a\x11\x9a\x71\xf8\xcd\xcd\x95\xa2\x7a\xe2\x38\x35\xe8\x9a\x17\xf6\xd6\xd2\x92\x56\xad\x4b\xbf\xa1\x8d\x2b\xc7\x3e\x62\x22\x38\x4a\x3a\xbd\x20\xf7\x21\xee\xa6\xe2\x69\x9a\xf5\x71\xb9\x34\xc4\x64\x82\x66\x00\xb7\xd7\xc1\xe3\xfd\xac\xaa\xc2\x64\x28\x39\xb6\xc0\x39\x1a\xc3\xb2\xd3\x9e\xef\x1e\x58\x5f\xa1\x66\xe0\xed\x69\x68\x83\x13\x6e\xd2\x99\xaf\xde\x00\x9f\xc2\xf7\x70\x6d\x1b\x67\xb5\x08\xf5\x62\xd5\x3a\x0f\x33\xe2\xef\x02\xaa\x20\xd7\xce\x1d\x0d\xc4\xbb\x47\xa3\xc4\x02\xd3\x4e\xc4\xed\x72\x78\xcc\xad\x47\x2f\xea\x75\x08\xa5\xa6\xea\xe3\xe3\xab\xc9\x53\x7d\x89\xc9\x33\xa3\xb1\x9a\x4f\x2e\xfa\x1d\xd0\xcf\xdd\x28\x3f\x6a\xd1\xc3\x1b\xd4\x67\x8f\xea\x1d\x7d\x48\xcd\xec\xb0\xdc\xdb\x49\xfb\xe6\x05\xd3\xb4\x1a\x61\x4f\x2c\x0b\xb5\x66\x19\x4a\x9a\x75\x47\x9c\x5f\x5f\x0b\x9e\x90\xd2\xaa\x30\xbb\xb2\x25\x46\x38\xf3\x0b\xed\x52\x4b\xe3\x7a\x41\x9f\x55\x94\xc4\x69\x35\x4b\xb9\x29\x04\x5b\xcd\xf6\xec\xa6\x8e\x37\xee\x3e\x72\xae\x24\x27\x65\x09\x99\x91\x52\x62\xe4\x48\xf4\x66\x57\x69\x9e\xd5\x02\xf5\x19\xf6\x8f\x3d\xa8\xff\xbe\x9e\xce\x53\x4e\xe1\xd5\x74\xbe\x62\xea\x6f\xe9\x0f\x31\xb9\xdb\xd3\x8d\x99\x29\xee\x6e\x4e\x3a\x0f\xfb\xee\x98\x3e\xfe\x8c\xe0\xe0\x5c\xe4\x1d\x23\xaf\xcb\x22\xa1\xc0\x4c\xb3\x7c\x88\xca\xff\x2d\x29\x29\x37\x89\xd2\xe9\x19\x1a\x51\x17\xe9\xc2\xae\xdd\x26\x3c\xe1\xfb\xe5\xd1\xfd\x34\x8f\xb9\x21\x64\xb9\xdb\x4c\xf3\x9c\xe9\xd5\x49\x75\xda\xc5\x3a\xbd\xe2\x7b\x48\xcd\xc9\x3e\x44\xa6\x2f\x30\x4a\x28\xe7\x75\xdb\xa7\x15\x6b\x43\x87\x6a\x36\x10\xfc\x04\xf1\x16\x3f\x9c\x8f\x72\x17\xeb\x42\xfa\x10\xe9\x2f\x5c\xb3\xb3\x3c\x2e\x1e\x50\xe7\xdd\xc5\x85\xf3\x49\x75\x2c\x19\xe4\xaa\xd0\x5c\x69\x6e\x4f\x9c\xd7\xcd\xe9\xe5\xbb\xed\x12\xdc\xdc\x42\x14\x6d\x0f\x35\xdb\xf7\xd9\xde\xdd\x5a\x1f\x00\x80\xca\xcf\xe0\x02\xb7\x7e\x5c\xa6\xf8\xbe\x7d\xa5\x0e\xd1\xf6\x52\xe4\x79\xf0\x39\x5c\xe1\x9b\xe3\x18\x25\x9a\x13\x4f\x98\x88\xa6\xad\x61\x0b\xdf\xa6\x75\x0b\xd1\x6f\x3c\x7b\xf6\xb1\x50\x18\xac\x00\x99\x4c\xbb\xa8\x4b\xa6\x25\x97\x59\x34\x85\x2b\x89\x0e\x50\x0d\x33\x3d\x12\xeb\x77\x4c\x79\x99\x87\x47\xe3\x72\xae\x6c\x28\xbb\xba\x0b\x75\x34\xcc\x9d\x5a\x76\x62\xc8\xb4\xd5\xc4\xfd\x5d\x7f\x23\x73\xa1\x3d\x37\x5f\xa7\xb6\x30\x7a\xb1\x47\xa9\x35\x5a\xb1\x00\xd5\xce\xae\x5c\x90\x7a\xe7\x53\xf0\xb8\x8a\x5d\x25\x8f\x29\xbb\x43\xea\x5e\x75\x5b\x9d\x56\xc9\x2b\x92\xff\x1a\xe8\xe4\x49\x35\x00\xc6\x04\x67\xe6\xf4\x17\xe9\xfb\xd2\xfb\xf4\xd7\x8f\x01\xe0\xc3\x9f\x3f\x06\x1c\x8e\x7d\x03\x19\x4a\xbe\xf7\x21\xe4\xdf\x00\x00\x00\xff\xff\xde\xd1\xa3\x9d\x75\x1f\x00\x00"),
 		},
 		"/templates/email.tmpl": &vfsgen۰CompressedFileInfo{
 			name:             "email.tmpl",
diff --git a/config/config.go b/config/config.go
index 5b8c7074..890592ca 100644
--- a/config/config.go
+++ b/config/config.go
@@ -269,6 +269,9 @@ func resolveFilepaths(baseDir string, cfg *Config) {
 		for _, cfg := range receiver.JiraConfigs {
 			cfg.HTTPConfig.SetDirectory(baseDir)
 		}
+		for _, cfg := range receiver.RocketchatConfigs {
+			cfg.HTTPConfig.SetDirectory(baseDir)
+		}
 	}
 }
 
@@ -364,6 +367,14 @@ func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error {
 		return errors.New("at most one of smtp_auth_password & smtp_auth_password_file must be configured")
 	}
 
+	if c.Global.RocketchatToken != nil && len(c.Global.RocketchatTokenFile) > 0 {
+		return errors.New("at most one of rocketchat_token & rocketchat_token_file must be configured")
+	}
+
+	if c.Global.RocketchatTokenID != nil && len(c.Global.RocketchatTokenIDFile) > 0 {
+		return errors.New("at most one of rocketchat_token_id & rocketchat_token_id_file must be configured")
+	}
+
 	names := map[string]struct{}{}
 
 	for _, rcv := range c.Receivers {
@@ -573,6 +584,28 @@ func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error {
 				jira.APIURL = c.Global.JiraAPIURL
 			}
 		}
+		for _, rocketchat := range rcv.RocketchatConfigs {
+			if rocketchat.HTTPConfig == nil {
+				rocketchat.HTTPConfig = c.Global.HTTPConfig
+			}
+			if rocketchat.APIURL == nil {
+				rocketchat.APIURL = c.Global.RocketchatAPIURL
+			}
+			if rocketchat.TokenID == nil && len(rocketchat.TokenIDFile) == 0 {
+				if c.Global.RocketchatTokenID == nil && len(c.Global.RocketchatTokenIDFile) == 0 {
+					return errors.New("no global Rocketchat TokenID set either inline or in a file")
+				}
+				rocketchat.TokenID = c.Global.RocketchatTokenID
+				rocketchat.TokenIDFile = c.Global.RocketchatTokenIDFile
+			}
+			if rocketchat.Token == nil && len(rocketchat.TokenFile) == 0 {
+				if c.Global.RocketchatToken == nil && len(c.Global.RocketchatTokenFile) == 0 {
+					return errors.New("no global Rocketchat Token set either inline or in a file")
+				}
+				rocketchat.Token = c.Global.RocketchatToken
+				rocketchat.TokenFile = c.Global.RocketchatTokenFile
+			}
+		}
 
 		names[rcv.Name] = struct{}{}
 	}
@@ -665,17 +698,18 @@ func DefaultGlobalConfig() GlobalConfig {
 	defaultSMTPTLSConfig := commoncfg.TLSConfig{}
 
 	return GlobalConfig{
-		ResolveTimeout:  model.Duration(5 * time.Minute),
-		HTTPConfig:      &defaultHTTPConfig,
-		SMTPHello:       "localhost",
-		SMTPRequireTLS:  true,
-		SMTPTLSConfig:   &defaultSMTPTLSConfig,
-		PagerdutyURL:    mustParseURL("https://events.pagerduty.com/v2/enqueue"),
-		OpsGenieAPIURL:  mustParseURL("https://api.opsgenie.com/"),
-		WeChatAPIURL:    mustParseURL("https://qyapi.weixin.qq.com/cgi-bin/"),
-		VictorOpsAPIURL: mustParseURL("https://alert.victorops.com/integrations/generic/20131114/alert/"),
-		TelegramAPIUrl:  mustParseURL("https://api.telegram.org"),
-		WebexAPIURL:     mustParseURL("https://webexapis.com/v1/messages"),
+		ResolveTimeout:   model.Duration(5 * time.Minute),
+		HTTPConfig:       &defaultHTTPConfig,
+		SMTPHello:        "localhost",
+		SMTPRequireTLS:   true,
+		SMTPTLSConfig:    &defaultSMTPTLSConfig,
+		PagerdutyURL:     mustParseURL("https://events.pagerduty.com/v2/enqueue"),
+		OpsGenieAPIURL:   mustParseURL("https://api.opsgenie.com/"),
+		WeChatAPIURL:     mustParseURL("https://qyapi.weixin.qq.com/cgi-bin/"),
+		VictorOpsAPIURL:  mustParseURL("https://alert.victorops.com/integrations/generic/20131114/alert/"),
+		TelegramAPIUrl:   mustParseURL("https://api.telegram.org"),
+		WebexAPIURL:      mustParseURL("https://webexapis.com/v1/messages"),
+		RocketchatAPIURL: mustParseURL("https://open.rocket.chat/"),
 	}
 }
 
@@ -777,31 +811,36 @@ type GlobalConfig struct {
 
 	HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"`
 
-	JiraAPIURL           *URL                 `yaml:"jira_api_url,omitempty" json:"jira_api_url,omitempty"`
-	SMTPFrom             string               `yaml:"smtp_from,omitempty" json:"smtp_from,omitempty"`
-	SMTPHello            string               `yaml:"smtp_hello,omitempty" json:"smtp_hello,omitempty"`
-	SMTPSmarthost        HostPort             `yaml:"smtp_smarthost,omitempty" json:"smtp_smarthost,omitempty"`
-	SMTPAuthUsername     string               `yaml:"smtp_auth_username,omitempty" json:"smtp_auth_username,omitempty"`
-	SMTPAuthPassword     Secret               `yaml:"smtp_auth_password,omitempty" json:"smtp_auth_password,omitempty"`
-	SMTPAuthPasswordFile string               `yaml:"smtp_auth_password_file,omitempty" json:"smtp_auth_password_file,omitempty"`
-	SMTPAuthSecret       Secret               `yaml:"smtp_auth_secret,omitempty" json:"smtp_auth_secret,omitempty"`
-	SMTPAuthIdentity     string               `yaml:"smtp_auth_identity,omitempty" json:"smtp_auth_identity,omitempty"`
-	SMTPRequireTLS       bool                 `yaml:"smtp_require_tls" json:"smtp_require_tls,omitempty"`
-	SMTPTLSConfig        *commoncfg.TLSConfig `yaml:"smtp_tls_config,omitempty" json:"smtp_tls_config,omitempty"`
-	SlackAPIURL          *SecretURL           `yaml:"slack_api_url,omitempty" json:"slack_api_url,omitempty"`
-	SlackAPIURLFile      string               `yaml:"slack_api_url_file,omitempty" json:"slack_api_url_file,omitempty"`
-	PagerdutyURL         *URL                 `yaml:"pagerduty_url,omitempty" json:"pagerduty_url,omitempty"`
-	OpsGenieAPIURL       *URL                 `yaml:"opsgenie_api_url,omitempty" json:"opsgenie_api_url,omitempty"`
-	OpsGenieAPIKey       Secret               `yaml:"opsgenie_api_key,omitempty" json:"opsgenie_api_key,omitempty"`
-	OpsGenieAPIKeyFile   string               `yaml:"opsgenie_api_key_file,omitempty" json:"opsgenie_api_key_file,omitempty"`
-	WeChatAPIURL         *URL                 `yaml:"wechat_api_url,omitempty" json:"wechat_api_url,omitempty"`
-	WeChatAPISecret      Secret               `yaml:"wechat_api_secret,omitempty" json:"wechat_api_secret,omitempty"`
-	WeChatAPICorpID      string               `yaml:"wechat_api_corp_id,omitempty" json:"wechat_api_corp_id,omitempty"`
-	VictorOpsAPIURL      *URL                 `yaml:"victorops_api_url,omitempty" json:"victorops_api_url,omitempty"`
-	VictorOpsAPIKey      Secret               `yaml:"victorops_api_key,omitempty" json:"victorops_api_key,omitempty"`
-	VictorOpsAPIKeyFile  string               `yaml:"victorops_api_key_file,omitempty" json:"victorops_api_key_file,omitempty"`
-	TelegramAPIUrl       *URL                 `yaml:"telegram_api_url,omitempty" json:"telegram_api_url,omitempty"`
-	WebexAPIURL          *URL                 `yaml:"webex_api_url,omitempty" json:"webex_api_url,omitempty"`
+	JiraAPIURL            *URL                 `yaml:"jira_api_url,omitempty" json:"jira_api_url,omitempty"`
+	SMTPFrom              string               `yaml:"smtp_from,omitempty" json:"smtp_from,omitempty"`
+	SMTPHello             string               `yaml:"smtp_hello,omitempty" json:"smtp_hello,omitempty"`
+	SMTPSmarthost         HostPort             `yaml:"smtp_smarthost,omitempty" json:"smtp_smarthost,omitempty"`
+	SMTPAuthUsername      string               `yaml:"smtp_auth_username,omitempty" json:"smtp_auth_username,omitempty"`
+	SMTPAuthPassword      Secret               `yaml:"smtp_auth_password,omitempty" json:"smtp_auth_password,omitempty"`
+	SMTPAuthPasswordFile  string               `yaml:"smtp_auth_password_file,omitempty" json:"smtp_auth_password_file,omitempty"`
+	SMTPAuthSecret        Secret               `yaml:"smtp_auth_secret,omitempty" json:"smtp_auth_secret,omitempty"`
+	SMTPAuthIdentity      string               `yaml:"smtp_auth_identity,omitempty" json:"smtp_auth_identity,omitempty"`
+	SMTPRequireTLS        bool                 `yaml:"smtp_require_tls" json:"smtp_require_tls,omitempty"`
+	SMTPTLSConfig         *commoncfg.TLSConfig `yaml:"smtp_tls_config,omitempty" json:"smtp_tls_config,omitempty"`
+	SlackAPIURL           *SecretURL           `yaml:"slack_api_url,omitempty" json:"slack_api_url,omitempty"`
+	SlackAPIURLFile       string               `yaml:"slack_api_url_file,omitempty" json:"slack_api_url_file,omitempty"`
+	PagerdutyURL          *URL                 `yaml:"pagerduty_url,omitempty" json:"pagerduty_url,omitempty"`
+	OpsGenieAPIURL        *URL                 `yaml:"opsgenie_api_url,omitempty" json:"opsgenie_api_url,omitempty"`
+	OpsGenieAPIKey        Secret               `yaml:"opsgenie_api_key,omitempty" json:"opsgenie_api_key,omitempty"`
+	OpsGenieAPIKeyFile    string               `yaml:"opsgenie_api_key_file,omitempty" json:"opsgenie_api_key_file,omitempty"`
+	WeChatAPIURL          *URL                 `yaml:"wechat_api_url,omitempty" json:"wechat_api_url,omitempty"`
+	WeChatAPISecret       Secret               `yaml:"wechat_api_secret,omitempty" json:"wechat_api_secret,omitempty"`
+	WeChatAPICorpID       string               `yaml:"wechat_api_corp_id,omitempty" json:"wechat_api_corp_id,omitempty"`
+	VictorOpsAPIURL       *URL                 `yaml:"victorops_api_url,omitempty" json:"victorops_api_url,omitempty"`
+	VictorOpsAPIKey       Secret               `yaml:"victorops_api_key,omitempty" json:"victorops_api_key,omitempty"`
+	VictorOpsAPIKeyFile   string               `yaml:"victorops_api_key_file,omitempty" json:"victorops_api_key_file,omitempty"`
+	TelegramAPIUrl        *URL                 `yaml:"telegram_api_url,omitempty" json:"telegram_api_url,omitempty"`
+	WebexAPIURL           *URL                 `yaml:"webex_api_url,omitempty" json:"webex_api_url,omitempty"`
+	RocketchatAPIURL      *URL                 `yaml:"rocketchat_api_url,omitempty" json:"rocketchat_api_url,omitempty"`
+	RocketchatToken       *Secret              `yaml:"rocketchat_token,omitempty" json:"rocketchat_token,omitempty"`
+	RocketchatTokenFile   string               `yaml:"rocketchat_token_file,omitempty" json:"rocketchat_token_file,omitempty"`
+	RocketchatTokenID     *Secret              `yaml:"rocketchat_token_id,omitempty" json:"rocketchat_token_id,omitempty"`
+	RocketchatTokenIDFile string               `yaml:"rocketchat_token_id_file,omitempty" json:"rocketchat_token_id_file,omitempty"`
 }
 
 // UnmarshalYAML implements the yaml.Unmarshaler interface for GlobalConfig.
@@ -933,21 +972,22 @@ type Receiver struct {
 	// A unique identifier for this receiver.
 	Name string `yaml:"name" json:"name"`
 
-	DiscordConfigs   []*DiscordConfig   `yaml:"discord_configs,omitempty" json:"discord_configs,omitempty"`
-	EmailConfigs     []*EmailConfig     `yaml:"email_configs,omitempty" json:"email_configs,omitempty"`
-	PagerdutyConfigs []*PagerdutyConfig `yaml:"pagerduty_configs,omitempty" json:"pagerduty_configs,omitempty"`
-	SlackConfigs     []*SlackConfig     `yaml:"slack_configs,omitempty" json:"slack_configs,omitempty"`
-	WebhookConfigs   []*WebhookConfig   `yaml:"webhook_configs,omitempty" json:"webhook_configs,omitempty"`
-	OpsGenieConfigs  []*OpsGenieConfig  `yaml:"opsgenie_configs,omitempty" json:"opsgenie_configs,omitempty"`
-	WechatConfigs    []*WechatConfig    `yaml:"wechat_configs,omitempty" json:"wechat_configs,omitempty"`
-	PushoverConfigs  []*PushoverConfig  `yaml:"pushover_configs,omitempty" json:"pushover_configs,omitempty"`
-	VictorOpsConfigs []*VictorOpsConfig `yaml:"victorops_configs,omitempty" json:"victorops_configs,omitempty"`
-	SNSConfigs       []*SNSConfig       `yaml:"sns_configs,omitempty" json:"sns_configs,omitempty"`
-	TelegramConfigs  []*TelegramConfig  `yaml:"telegram_configs,omitempty" json:"telegram_configs,omitempty"`
-	WebexConfigs     []*WebexConfig     `yaml:"webex_configs,omitempty" json:"webex_configs,omitempty"`
-	MSTeamsConfigs   []*MSTeamsConfig   `yaml:"msteams_configs,omitempty" json:"msteams_configs,omitempty"`
-	MSTeamsV2Configs []*MSTeamsV2Config `yaml:"msteamsv2_configs,omitempty" json:"msteamsv2_configs,omitempty"`
-	JiraConfigs      []*JiraConfig      `yaml:"jira_configs,omitempty" json:"jira_configs,omitempty"`
+	DiscordConfigs    []*DiscordConfig    `yaml:"discord_configs,omitempty" json:"discord_configs,omitempty"`
+	EmailConfigs      []*EmailConfig      `yaml:"email_configs,omitempty" json:"email_configs,omitempty"`
+	PagerdutyConfigs  []*PagerdutyConfig  `yaml:"pagerduty_configs,omitempty" json:"pagerduty_configs,omitempty"`
+	SlackConfigs      []*SlackConfig      `yaml:"slack_configs,omitempty" json:"slack_configs,omitempty"`
+	WebhookConfigs    []*WebhookConfig    `yaml:"webhook_configs,omitempty" json:"webhook_configs,omitempty"`
+	OpsGenieConfigs   []*OpsGenieConfig   `yaml:"opsgenie_configs,omitempty" json:"opsgenie_configs,omitempty"`
+	WechatConfigs     []*WechatConfig     `yaml:"wechat_configs,omitempty" json:"wechat_configs,omitempty"`
+	PushoverConfigs   []*PushoverConfig   `yaml:"pushover_configs,omitempty" json:"pushover_configs,omitempty"`
+	VictorOpsConfigs  []*VictorOpsConfig  `yaml:"victorops_configs,omitempty" json:"victorops_configs,omitempty"`
+	SNSConfigs        []*SNSConfig        `yaml:"sns_configs,omitempty" json:"sns_configs,omitempty"`
+	TelegramConfigs   []*TelegramConfig   `yaml:"telegram_configs,omitempty" json:"telegram_configs,omitempty"`
+	WebexConfigs      []*WebexConfig      `yaml:"webex_configs,omitempty" json:"webex_configs,omitempty"`
+	MSTeamsConfigs    []*MSTeamsConfig    `yaml:"msteams_configs,omitempty" json:"msteams_configs,omitempty"`
+	MSTeamsV2Configs  []*MSTeamsV2Config  `yaml:"msteamsv2_configs,omitempty" json:"msteamsv2_configs,omitempty"`
+	JiraConfigs       []*JiraConfig       `yaml:"jira_configs,omitempty" json:"jira_configs,omitempty"`
+	RocketchatConfigs []*RocketchatConfig `yaml:"rocketchat_configs,omitempty" json:"rocketchat_configs,omitempty"`
 }
 
 // UnmarshalYAML implements the yaml.Unmarshaler interface for Receiver.
diff --git a/config/notifiers.go b/config/notifiers.go
index 519e5efb..4e23fb92 100644
--- a/config/notifiers.go
+++ b/config/notifiers.go
@@ -99,6 +99,18 @@ var (
 		CallbackID: `{{ template "slack.default.callbackid" . }}`,
 		Footer:     `{{ template "slack.default.footer" . }}`,
 	}
+	// DefaultRocketchatConfig defines default values for Rocketchat configurations.
+	DefaultRocketchatConfig = RocketchatConfig{
+		NotifierConfig: NotifierConfig{
+			VSendResolved: false,
+		},
+		Color:     `{{ if eq .Status "firing" }}red{{ else }}green{{ end }}`,
+		Emoji:     `{{ template "rocketchat.default.emoji" . }}`,
+		IconURL:   `{{ template "rocketchat.default.iconurl" . }}`,
+		Text:      `{{ template "rocketchat.default.text" . }}`,
+		Title:     `{{ template "rocketchat.default.title" . }}`,
+		TitleLink: `{{ template "rocketchat.default.titlelink" . }}`,
+	}
 
 	// DefaultOpsGenieConfig defines default values for OpsGenie configurations.
 	DefaultOpsGenieConfig = OpsGenieConfig{
@@ -907,6 +919,73 @@ func (c *JiraConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
 	if c.IssueType == "" {
 		return errors.New("missing issue_type in jira_config")
 	}
+	return nil
+}
 
+type RocketchatAttachmentField struct {
+	Short *bool  `json:"short"`
+	Title string `json:"title,omitempty"`
+	Value string `json:"value,omitempty"`
+}
+
+const (
+	ProcessingTypeSendMessage        = "sendMessage"
+	ProcessingTypeRespondWithMessage = "respondWithMessage"
+)
+
+type RocketchatAttachmentAction struct {
+	Type               string `json:"type,omitempty"`
+	Text               string `json:"text,omitempty"`
+	URL                string `json:"url,omitempty"`
+	ImageURL           string `json:"image_url,omitempty"`
+	IsWebView          bool   `json:"is_webview"`
+	WebviewHeightRatio string `json:"webview_height_ratio,omitempty"`
+	Msg                string `json:"msg,omitempty"`
+	MsgInChatWindow    bool   `json:"msg_in_chat_window"`
+	MsgProcessingType  string `json:"msg_processing_type,omitempty"`
+}
+
+// RocketchatConfig configures notifications via Rocketchat.
+type RocketchatConfig struct {
+	NotifierConfig `yaml:",inline" json:",inline"`
+
+	HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"`
+
+	APIURL      *URL    `yaml:"api_url,omitempty" json:"api_url,omitempty"`
+	TokenID     *Secret `yaml:"token_id,omitempty" json:"token_id,omitempty"`
+	TokenIDFile string  `yaml:"token_id_file,omitempty" json:"token_id_file,omitempty"`
+	Token       *Secret `yaml:"token,omitempty" json:"token,omitempty"`
+	TokenFile   string  `yaml:"token_file,omitempty" json:"token_file,omitempty"`
+
+	// RocketChat channel override, (like #other-channel or @username).
+	Channel string `yaml:"channel,omitempty" json:"channel,omitempty"`
+
+	Color       string                        `yaml:"color,omitempty" json:"color,omitempty"`
+	Title       string                        `yaml:"title,omitempty" json:"title,omitempty"`
+	TitleLink   string                        `yaml:"title_link,omitempty" json:"title_link,omitempty"`
+	Text        string                        `yaml:"text,omitempty" json:"text,omitempty"`
+	Fields      []*RocketchatAttachmentField  `yaml:"fields,omitempty" json:"fields,omitempty"`
+	ShortFields bool                          `yaml:"short_fields" json:"short_fields,omitempty"`
+	Emoji       string                        `yaml:"emoji,omitempty" json:"emoji,omitempty"`
+	IconURL     string                        `yaml:"icon_url,omitempty" json:"icon_url,omitempty"`
+	ImageURL    string                        `yaml:"image_url,omitempty" json:"image_url,omitempty"`
+	ThumbURL    string                        `yaml:"thumb_url,omitempty" json:"thumb_url,omitempty"`
+	LinkNames   bool                          `yaml:"link_names" json:"link_names,omitempty"`
+	Actions     []*RocketchatAttachmentAction `yaml:"actions,omitempty" json:"actions,omitempty"`
+}
+
+// UnmarshalYAML implements the yaml.Unmarshaler interface.
+func (c *RocketchatConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
+	*c = DefaultRocketchatConfig
+	type plain RocketchatConfig
+	if err := unmarshal((*plain)(c)); err != nil {
+		return err
+	}
+	if c.Token != nil && len(c.TokenFile) > 0 {
+		return errors.New("at most one of token & token_file must be configured")
+	}
+	if c.TokenID != nil && len(c.TokenIDFile) > 0 {
+		return errors.New("at most one of token_id & token_id_file must be configured")
+	}
 	return nil
 }
diff --git a/config/receiver/receiver.go b/config/receiver/receiver.go
index 7edc1775..f66c4a70 100644
--- a/config/receiver/receiver.go
+++ b/config/receiver/receiver.go
@@ -28,6 +28,7 @@ import (
 	"github.com/prometheus/alertmanager/notify/opsgenie"
 	"github.com/prometheus/alertmanager/notify/pagerduty"
 	"github.com/prometheus/alertmanager/notify/pushover"
+	"github.com/prometheus/alertmanager/notify/rocketchat"
 	"github.com/prometheus/alertmanager/notify/slack"
 	"github.com/prometheus/alertmanager/notify/sns"
 	"github.com/prometheus/alertmanager/notify/telegram"
@@ -100,6 +101,9 @@ func BuildReceiverIntegrations(nc config.Receiver, tmpl *template.Template, logg
 	for i, c := range nc.JiraConfigs {
 		add("jira", i, c, func(l log.Logger) (notify.Notifier, error) { return jira.New(c, tmpl, l, httpOpts...) })
 	}
+	for i, c := range nc.RocketchatConfigs {
+		add("rocketchat", i, c, func(l log.Logger) (notify.Notifier, error) { return rocketchat.New(c, tmpl, l, httpOpts...) })
+	}
 
 	if errs.Len() > 0 {
 		return nil, &errs
diff --git a/docs/configuration.md b/docs/configuration.md
index 7db87af5..16e4aeeb 100644
--- a/docs/configuration.md
+++ b/docs/configuration.md
@@ -97,7 +97,7 @@ global:
   # The default TLS configuration for SMTP receivers
   [ smtp_tls_config: <tls_config> ]
 
-  # Default settings for the JIRA integration. 
+  # Default settings for the JIRA integration.
   [ jira_api_url: <string> ]
 
   # The API URL to use for Slack notifications.
@@ -110,6 +110,11 @@ global:
   [ opsgenie_api_key: <secret> ]
   [ opsgenie_api_key_file: <filepath> ]
   [ opsgenie_api_url: <string> | default = "https://api.opsgenie.com/" ]
+  [ rocketchat_api_url: <string> | default = "https://open.rocket.chat/" ]
+  [ rocketchat_token: <secret> ]
+  [ rocketchat_token_file: <filepath> ]
+  [ rocketchat_token_id: <secret> ]
+  [ rocketchat_token_id_file: <filepath> ]
   [ wechat_api_url: <string> | default = "https://qyapi.weixin.qq.com/cgi-bin/" ]
   [ wechat_api_secret: <secret> ]
   [ wechat_api_corp_id: <string> ]
@@ -710,6 +715,8 @@ pagerduty_configs:
   [ - <pagerduty_config>, ... ]
 pushover_configs:
   [ - <pushover_config>, ... ]
+rocket_configs:
+  [ - <rocketchat_config>, ... ]
 slack_configs:
   [ - <slack_config>, ... ]
 sns_configs:
@@ -981,7 +988,7 @@ Microsoft Teams v2 notifications using the new message format with adaptive card
 JIRA notifications are sent via [JIRA Rest API v2](https://developer.atlassian.com/cloud/jira/platform/rest/v2/intro/)
 or [JIRA REST API v3](https://developer.atlassian.com/cloud/jira/platform/rest/v3/intro/#version).
 
-Note: This integration is only tested against a Jira Cloud instance. 
+Note: This integration is only tested against a Jira Cloud instance.
 Jira Data Center (on premise instance) can work, but it's not guaranteed.
 
 Both APIs have the same feature set. The difference is that V2 supports [Wiki Markup](https://jira.atlassian.com/secure/WikiRendererHelpAction.jspa?section=all)
@@ -1006,7 +1013,7 @@ project: <string>
 [ description: <tmpl_string> | default = '{{ template "jira.default.description" . }}' ]
 
 # Labels to be added to the issue.
-labels: 
+labels:
   [ - <tmpl_string> ... ]
 
 # Priority of the issue.
@@ -1283,6 +1290,62 @@ token_file: <filepath>
 [ http_config: <http_config> | default = global.http_config ]
 ```
 
+### `<rocketchat_config>`
+
+Rocketchat notifications are sent via the [Rocketchat REST API](https://developer.rocket.chat/reference/api/rest-api/endpoints/messaging/chat-endpoints/postmessage).
+
+```yaml
+# Whether to notify about resolved alerts.
+[ send_resolved: <boolean> | default = true ]
+[ api_url: <string> | default = global.rocketchat_api_url ]
+[ channel: <tmpl_string> | default = global.rocketchat_api_url ]
+
+# The sender token and token_id
+# See https://docs.rocket.chat/use-rocket.chat/user-guides/user-panel/my-account#personal-access-tokens
+# token and token_file are mutually exclusive.
+# token_id and token_id_file are mutually exclusive.
+token: <secret>
+token_file: <filepath>
+token_id: <secret>
+token_id_file: <filepath>
+
+
+[ color: <tmpl_string | default '{{ if eq .Status "firing" }}red{{ else }}green{{ end }}' ]
+[ emoji <tmpl_string | default = '{{ template "rocketchat.default.emoji" . }}'
+[ icon_url <tmpl_string | default = '{{ template "rocketchat.default.iconurl" . }}'
+[ text <tmpl_string | default = '{{ template "rocketchat.default.text" . }}'
+[ title <tmpl_string | default = '{{ template "rocketchat.default.title" . }}'
+[ titleLink <tmpl_string | default = '{{ template "rocketchat.default.titlelink" . }}'
+[ text: <tmpl_string | default = '{{ template "rocketchat.default.text" . }}'
+fields:
+  [ <rocketchat_field_config> ... ]
+[ image_url <tmpl_string> ]
+[ thumb_url <tmpl_string> ]
+[ link_names <tmpl_string> ]
+[ short_fields: <boolean> | default = false ]
+actions:
+  [ <rocketchat_action_config> ... ]
+```
+
+#### `<rocketchat_field_config>`
+
+The fields are documented in the [Rocketchat API documentation](https://developer.rocket.chat/reference/api/rest-api/endpoints/messaging/chat-endpoints/postmessage#attachment-field-objects).
+
+```yaml
+[ title: <tmpl_string> ]
+[ value: <tmpl_string> ]
+[ short: <boolean> | default = rocketchat_config.short_fields ]
+```
+
+#### `<rocketchat_action_config>`
+The fields are documented in the [Rocketchat API api models](https://github.com/RocketChat/Rocket.Chat.Go.SDK/blob/master/models/message.go).
+
+```yaml
+[ type: <tmpl_string> | ignored, only "button" is supported ]
+[ text: <tmpl_string> ]
+[ url: <tmpl_string> ]
+[ msg: <tmpl_string> ]
+
 ### `<slack_config>`
 
 Slack notifications can be sent via [Incoming webhooks](https://api.slack.com/messaging/webhooks) or [Bot tokens](https://api.slack.com/authentication/token-types).
diff --git a/notify/notify.go b/notify/notify.go
index 25d04cb2..65106534 100644
--- a/notify/notify.go
+++ b/notify/notify.go
@@ -367,6 +367,7 @@ func (m *Metrics) InitializeFor(receiver map[string][]Integration) {
 		"msteams",
 		"msteamsv2",
 		"jira",
+		"rocketchat",
 	} {
 		m.numNotifications.WithLabelValues(integration)
 		m.numNotificationRequestsTotal.WithLabelValues(integration)
diff --git a/notify/rocketchat/rocketchat.go b/notify/rocketchat/rocketchat.go
new file mode 100644
index 00000000..7a41c261
--- /dev/null
+++ b/notify/rocketchat/rocketchat.go
@@ -0,0 +1,270 @@
+// Copyright 2022 Prometheus Team
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rocketchat
+
+import (
+	"bytes"
+	"context"
+	"encoding/json"
+	"fmt"
+	"io"
+	"net/http"
+	"os"
+	"strings"
+
+	"github.com/go-kit/log"
+	"github.com/go-kit/log/level"
+	commoncfg "github.com/prometheus/common/config"
+
+	"github.com/prometheus/alertmanager/config"
+	"github.com/prometheus/alertmanager/notify"
+	"github.com/prometheus/alertmanager/template"
+	"github.com/prometheus/alertmanager/types"
+)
+
+const maxTitleLenRunes = 1024
+
+type Notifier struct {
+	conf    *config.RocketchatConfig
+	tmpl    *template.Template
+	logger  log.Logger
+	client  *http.Client
+	retrier *notify.Retrier
+	token   string
+	tokenID string
+
+	postJSONFunc func(ctx context.Context, client *http.Client, url string, body io.Reader) (*http.Response, error)
+}
+
+// PostMessage Payload for postmessage rest API
+//
+// https://rocket.chat/docs/developer-guides/rest-api/chat/postmessage/
+type Attachment struct {
+	Title     string                              `json:"title,omitempty"`
+	TitleLink string                              `json:"title_link,omitempty"`
+	Text      string                              `json:"text,omitempty"`
+	ImageURL  string                              `json:"image_url,omitempty"`
+	ThumbURL  string                              `json:"thumb_url,omitempty"`
+	Color     string                              `json:"color,omitempty"`
+	Fields    []config.RocketchatAttachmentField  `json:"fields,omitempty"`
+	Actions   []config.RocketchatAttachmentAction `json:"actions,omitempty"`
+}
+
+// PostMessage Payload for postmessage rest API
+//
+// https://rocket.chat/docs/developer-guides/rest-api/chat/postmessage/
+type PostMessage struct {
+	Channel     string                              `json:"channel,omitempty"`
+	Text        string                              `json:"text,omitempty"`
+	ParseUrls   bool                                `json:"parseUrls,omitempty"`
+	Alias       string                              `json:"alias,omitempty"`
+	Emoji       string                              `json:"emoji,omitempty"`
+	Avatar      string                              `json:"avatar,omitempty"`
+	Attachments []Attachment                        `json:"attachments,omitempty"`
+	Actions     []config.RocketchatAttachmentAction `json:"actions,omitempty"`
+}
+
+type rocketchatRoundTripper struct {
+	wrapped http.RoundTripper
+	token   string
+	tokenID string
+}
+
+func (t *rocketchatRoundTripper) RoundTrip(req *http.Request) (res *http.Response, e error) {
+	req.Header.Set("X-Auth-Token", t.token)
+	req.Header.Set("X-User-Id", t.tokenID)
+	return t.wrapped.RoundTrip(req)
+}
+
+// New returns a new Rocketchat notification handler.
+func New(c *config.RocketchatConfig, t *template.Template, l log.Logger, httpOpts ...commoncfg.HTTPClientOption) (*Notifier, error) {
+	client, err := commoncfg.NewClientFromConfig(*c.HTTPConfig, "rocketchat", httpOpts...)
+	if err != nil {
+		return nil, err
+	}
+	token, err := getToken(c)
+	if err != nil {
+		return nil, err
+	}
+	tokenID, err := getTokenID(c)
+	if err != nil {
+		return nil, err
+	}
+
+	client.Transport = &rocketchatRoundTripper{wrapped: client.Transport, token: token, tokenID: tokenID}
+	return &Notifier{
+		conf:         c,
+		tmpl:         t,
+		logger:       l,
+		client:       client,
+		retrier:      &notify.Retrier{},
+		postJSONFunc: notify.PostJSON,
+		token:        token,
+		tokenID:      tokenID,
+	}, nil
+}
+
+func getTokenID(c *config.RocketchatConfig) (string, error) {
+	if len(c.TokenIDFile) > 0 {
+		content, err := os.ReadFile(c.TokenIDFile)
+		if err != nil {
+			return "", fmt.Errorf("could not read %s: %w", c.TokenIDFile, err)
+		}
+		return strings.TrimSpace(string(content)), nil
+	}
+	return string(*c.TokenID), nil
+}
+
+func getToken(c *config.RocketchatConfig) (string, error) {
+	if len(c.TokenFile) > 0 {
+		content, err := os.ReadFile(c.TokenFile)
+		if err != nil {
+			return "", fmt.Errorf("could not read %s: %w", c.TokenFile, err)
+		}
+		return strings.TrimSpace(string(content)), nil
+	}
+	return string(*c.Token), nil
+}
+
+// Notify implements the Notifier interface.
+func (n *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error) {
+	var err error
+
+	data := notify.GetTemplateData(ctx, n.tmpl, as, n.logger)
+	tmplText := notify.TmplText(n.tmpl, data, &err)
+	if err != nil {
+		return false, err
+	}
+	title := tmplText(n.conf.Title)
+	if err != nil {
+		return false, err
+	}
+
+	title, truncated := notify.TruncateInRunes(title, maxTitleLenRunes)
+	if truncated {
+		key, err := notify.ExtractGroupKey(ctx)
+		if err != nil {
+			return false, err
+		}
+		level.Warn(n.logger).Log("msg", "Truncated title", "key", key, "max_runes", maxTitleLenRunes)
+	}
+	att := &Attachment{
+		Title:     title,
+		TitleLink: tmplText(n.conf.TitleLink),
+		Text:      tmplText(n.conf.Text),
+		ImageURL:  tmplText(n.conf.ImageURL),
+		ThumbURL:  tmplText(n.conf.ThumbURL),
+		Color:     tmplText(n.conf.Color),
+	}
+	numFields := len(n.conf.Fields)
+	if numFields > 0 {
+		fields := make([]config.RocketchatAttachmentField, numFields)
+		for index, field := range n.conf.Fields {
+			// Check if short was defined for the field otherwise fallback to the global setting
+			var short bool
+			if field.Short != nil {
+				short = *field.Short
+			} else {
+				short = n.conf.ShortFields
+			}
+
+			// Rebuild the field by executing any templates and setting the new value for short
+			fields[index] = config.RocketchatAttachmentField{
+				Title: tmplText(field.Title),
+				Value: tmplText(field.Value),
+				Short: &short,
+			}
+		}
+		att.Fields = fields
+	}
+	numActions := len(n.conf.Actions)
+	if numActions > 0 {
+		actions := make([]config.RocketchatAttachmentAction, numActions)
+		for index, action := range n.conf.Actions {
+			actions[index] = config.RocketchatAttachmentAction{
+				Type: "button", // Only button type is supported
+				Text: tmplText(action.Text),
+				URL:  tmplText(action.URL),
+				Msg:  tmplText(action.Msg),
+			}
+		}
+		att.Actions = actions
+	}
+
+	body := &PostMessage{
+		Channel:     n.conf.Channel,
+		Emoji:       tmplText(n.conf.Emoji),
+		Avatar:      tmplText(n.conf.IconURL),
+		Attachments: []Attachment{*att},
+	}
+	if err != nil {
+		return false, err
+	}
+
+	var buf bytes.Buffer
+	if err := json.NewEncoder(&buf).Encode(body); err != nil {
+		return false, err
+	}
+	url := n.conf.APIURL.JoinPath("api/v1/chat.postMessage").String()
+	resp, err := n.postJSONFunc(ctx, n.client, url, &buf)
+	if err != nil {
+		return true, notify.RedactURL(err)
+	}
+	defer notify.Drain(resp)
+
+	// Use a retrier to generate an error message for non-200 responses and
+	// classify them as retriable or not.
+	retry, err := n.retrier.Check(resp.StatusCode, resp.Body)
+	if err != nil {
+		err = fmt.Errorf("channel %q: %w", body.Channel, err)
+		return retry, notify.NewErrorWithReason(notify.GetFailureReasonFromStatusCode(resp.StatusCode), err)
+	}
+
+	// Rocketchat web API might return errors with a 200 response code.
+	retry, err = checkResponseError(resp)
+	if err != nil {
+		err = fmt.Errorf("channel %q: %w", body.Channel, err)
+		return retry, notify.NewErrorWithReason(notify.ClientErrorReason, err)
+	}
+
+	return retry, nil
+}
+
+// checkResponseError parses out the error message from Rocketchat API response.
+func checkResponseError(resp *http.Response) (bool, error) {
+	body, err := io.ReadAll(resp.Body)
+	if err != nil {
+		return true, fmt.Errorf("could not read response body: %w", err)
+	}
+
+	return checkJSONResponseError(body)
+}
+
+// checkJSONResponseError classifies JSON responses from Rocketchat.
+func checkJSONResponseError(body []byte) (bool, error) {
+	// response is for parsing out errors from the JSON response.
+	type response struct {
+		Success bool   `json:"success"`
+		Error   string `json:"error"`
+	}
+
+	var data response
+	if err := json.Unmarshal(body, &data); err != nil {
+		return true, fmt.Errorf("could not unmarshal JSON response %q: %w", string(body), err)
+	}
+	if !data.Success {
+		return false, fmt.Errorf("error response from Rocketchat: %s", data.Error)
+	}
+	return false, nil
+}
diff --git a/template/default.tmpl b/template/default.tmpl
index 6475e0a2..a08caa86 100644
--- a/template/default.tmpl
+++ b/template/default.tmpl
@@ -209,3 +209,10 @@ Alerts Resolved:
 {{- end -}}
 {{- $priority -}}
 {{- end -}}
+
+{{ define "rocketchat.default.title" }}{{ template "__subject" . }}{{ end }}
+{{ define "rocketchat.default.alias" }}{{ template "__alertmanager" . }}{{ end }}
+{{ define "rocketchat.default.titlelink" }}{{ template "__alertmanagerURL" . }}{{ end }}
+{{ define "rocketchat.default.emoji" }}{{ end }}
+{{ define "rocketchat.default.iconurl" }}{{ end }}
+{{ define "rocketchat.default.text" }}{{ end }}


Test patch:
diff --git a/config/config_test.go b/config/config_test.go
index fa21bb49..15edfeed 100644
--- a/config/config_test.go
+++ b/config/config_test.go
@@ -872,14 +872,15 @@ func TestEmptyFieldsAndRegex(t *testing.T) {
 			SMTPTLSConfig: &commoncfg.TLSConfig{
 				InsecureSkipVerify: false,
 			},
-			SlackAPIURL:     (*SecretURL)(mustParseURL("http://slack.example.com/")),
-			SMTPRequireTLS:  true,
-			PagerdutyURL:    mustParseURL("https://events.pagerduty.com/v2/enqueue"),
-			OpsGenieAPIURL:  mustParseURL("https://api.opsgenie.com/"),
-			WeChatAPIURL:    mustParseURL("https://qyapi.weixin.qq.com/cgi-bin/"),
-			VictorOpsAPIURL: mustParseURL("https://alert.victorops.com/integrations/generic/20131114/alert/"),
-			TelegramAPIUrl:  mustParseURL("https://api.telegram.org"),
-			WebexAPIURL:     mustParseURL("https://webexapis.com/v1/messages"),
+			SlackAPIURL:      (*SecretURL)(mustParseURL("http://slack.example.com/")),
+			SMTPRequireTLS:   true,
+			PagerdutyURL:     mustParseURL("https://events.pagerduty.com/v2/enqueue"),
+			OpsGenieAPIURL:   mustParseURL("https://api.opsgenie.com/"),
+			WeChatAPIURL:     mustParseURL("https://qyapi.weixin.qq.com/cgi-bin/"),
+			VictorOpsAPIURL:  mustParseURL("https://alert.victorops.com/integrations/generic/20131114/alert/"),
+			TelegramAPIUrl:   mustParseURL("https://api.telegram.org"),
+			WebexAPIURL:      mustParseURL("https://webexapis.com/v1/messages"),
+			RocketchatAPIURL: mustParseURL("https://open.rocket.chat/"),
 		},
 
 		Templates: []string{
@@ -1203,6 +1204,100 @@ func TestInvalidSNSConfig(t *testing.T) {
 	}
 }
 
+func TestRocketchatDefaultToken(t *testing.T) {
+	conf, err := LoadFile("testdata/conf.rocketchat-default-token.yml")
+	if err != nil {
+		t.Fatalf("Error parsing %s: %s", "testdata/conf.rocketchat-default-token.yml", err)
+	}
+
+	defaultToken := conf.Global.RocketchatToken
+	overrideToken := Secret("token456")
+	if defaultToken != conf.Receivers[0].RocketchatConfigs[0].Token {
+		t.Fatalf("Invalid rocketchat key: %s\nExpected: %s", string(*conf.Receivers[0].RocketchatConfigs[0].Token), string(*defaultToken))
+	}
+	if overrideToken != *conf.Receivers[1].RocketchatConfigs[0].Token {
+		t.Errorf("Invalid rocketchat key: %s\nExpected: %s", string(*conf.Receivers[0].RocketchatConfigs[0].Token), string(overrideToken))
+	}
+}
+
+func TestRocketchatDefaultTokenID(t *testing.T) {
+	conf, err := LoadFile("testdata/conf.rocketchat-default-token.yml")
+	if err != nil {
+		t.Fatalf("Error parsing %s: %s", "testdata/conf.rocketchat-default-token.yml", err)
+	}
+
+	defaultTokenID := conf.Global.RocketchatTokenID
+	overrideTokenID := Secret("id456")
+	if defaultTokenID != conf.Receivers[0].RocketchatConfigs[0].TokenID {
+		t.Fatalf("Invalid rocketchat key: %s\nExpected: %s", string(*conf.Receivers[0].RocketchatConfigs[0].TokenID), string(*defaultTokenID))
+	}
+	if overrideTokenID != *conf.Receivers[1].RocketchatConfigs[0].TokenID {
+		t.Errorf("Invalid rocketchat key: %s\nExpected: %s", string(*conf.Receivers[0].RocketchatConfigs[0].TokenID), string(overrideTokenID))
+	}
+}
+
+func TestRocketchatDefaultTokenFile(t *testing.T) {
+	conf, err := LoadFile("testdata/conf.rocketchat-default-token-file.yml")
+	if err != nil {
+		t.Fatalf("Error parsing %s: %s", "testdata/conf.rocketchat-default-token-file.yml", err)
+	}
+
+	defaultTokenFile := conf.Global.RocketchatTokenFile
+	overrideTokenFile := "/override_file"
+	if defaultTokenFile != conf.Receivers[0].RocketchatConfigs[0].TokenFile {
+		t.Fatalf("Invalid Rocketchat key_file: %s\nExpected: %s", conf.Receivers[0].RocketchatConfigs[0].TokenFile, defaultTokenFile)
+	}
+	if overrideTokenFile != conf.Receivers[1].RocketchatConfigs[0].TokenFile {
+		t.Errorf("Invalid Rocketchat key_file: %s\nExpected: %s", conf.Receivers[0].RocketchatConfigs[0].TokenFile, overrideTokenFile)
+	}
+}
+
+func TestRocketchatDefaultIDTokenFile(t *testing.T) {
+	conf, err := LoadFile("testdata/conf.rocketchat-default-token-file.yml")
+	if err != nil {
+		t.Fatalf("Error parsing %s: %s", "testdata/conf.rocketchat-default-token-file.yml", err)
+	}
+
+	defaultTokenIDFile := conf.Global.RocketchatTokenIDFile
+	overrideTokenIDFile := "/override_file"
+	if defaultTokenIDFile != conf.Receivers[0].RocketchatConfigs[0].TokenIDFile {
+		t.Fatalf("Invalid Rocketchat key_file: %s\nExpected: %s", conf.Receivers[0].RocketchatConfigs[0].TokenIDFile, defaultTokenIDFile)
+	}
+	if overrideTokenIDFile != conf.Receivers[1].RocketchatConfigs[0].TokenIDFile {
+		t.Errorf("Invalid Rocketchat key_file: %s\nExpected: %s", conf.Receivers[0].RocketchatConfigs[0].TokenIDFile, overrideTokenIDFile)
+	}
+}
+
+func TestRocketchatBothTokenAndTokenFile(t *testing.T) {
+	_, err := LoadFile("testdata/conf.rocketchat-both-token-and-tokenfile.yml")
+	if err == nil {
+		t.Fatalf("Expected an error parsing %s: %s", "testdata/conf.rocketchat-both-token-and-tokenfile.yml", err)
+	}
+	if err.Error() != "at most one of rocketchat_token & rocketchat_token_file must be configured" {
+		t.Errorf("Expected: %s\nGot: %s", "at most one of rocketchat_token & rocketchat_token_file must be configured", err.Error())
+	}
+}
+
+func TestRocketchatBothTokenIDAndTokenIDFile(t *testing.T) {
+	_, err := LoadFile("testdata/conf.rocketchat-both-tokenid-and-tokenidfile.yml")
+	if err == nil {
+		t.Fatalf("Expected an error parsing %s: %s", "testdata/conf.rocketchat-both-tokenid-and-tokenidfile.yml", err)
+	}
+	if err.Error() != "at most one of rocketchat_token_id & rocketchat_token_id_file must be configured" {
+		t.Errorf("Expected: %s\nGot: %s", "at most one of rocketchat_token_id & rocketchat_token_id_file must be configured", err.Error())
+	}
+}
+
+func TestRocketchatNoToken(t *testing.T) {
+	_, err := LoadFile("testdata/conf.rocketchat-no-token.yml")
+	if err == nil {
+		t.Fatalf("Expected an error parsing %s: %s", "testdata/conf.rocketchat-no-token.yml", err)
+	}
+	if err.Error() != "no global Rocketchat Token set either inline or in a file" {
+		t.Errorf("Expected: %s\nGot: %s", "no global Rocketchat Token set either inline or in a file", err.Error())
+	}
+}
+
 func TestUnmarshalHostPort(t *testing.T) {
 	for _, tc := range []struct {
 		in string
diff --git a/config/testdata/conf.rocketchat-both-token-and-tokenfile.yml b/config/testdata/conf.rocketchat-both-token-and-tokenfile.yml
new file mode 100644
index 00000000..b51cebde
--- /dev/null
+++ b/config/testdata/conf.rocketchat-both-token-and-tokenfile.yml
@@ -0,0 +1,20 @@
+global:
+  rocketchat_token_file: /global_file
+  rocketchat_token: token123
+route:
+  group_by: ['alertname', 'cluster', 'service']
+  group_wait: 30s
+  group_interval: 5m
+  repeat_interval: 3h
+  receiver: team-Y-rocketchat
+  routes:
+    - match:
+        service: foo
+      receiver: team-X-rocketchat
+receivers:
+  - name: 'team-X-rocketchat'
+    rocketchat_configs:
+      - channel: '#team-X'
+  - name: 'team-Y-rocketchat'
+    rocketchat_configs:
+      - channel: '#team-Y'
diff --git a/config/testdata/conf.rocketchat-both-tokenid-and-tokenidfile.yml b/config/testdata/conf.rocketchat-both-tokenid-and-tokenidfile.yml
new file mode 100644
index 00000000..702e0522
--- /dev/null
+++ b/config/testdata/conf.rocketchat-both-tokenid-and-tokenidfile.yml
@@ -0,0 +1,20 @@
+global:
+  rocketchat_token_id_file: /global_file
+  rocketchat_token_id: id123
+route:
+  group_by: ['alertname', 'cluster', 'service']
+  group_wait: 30s
+  group_interval: 5m
+  repeat_interval: 3h
+  receiver: team-Y-rocketchat
+  routes:
+    - match:
+        service: foo
+      receiver: team-X-rocketchat
+receivers:
+  - name: 'team-X-rocketchat'
+    rocketchat_configs:
+      - channel: '#team-X'
+  - name: 'team-Y-rocketchat'
+    rocketchat_configs:
+      - channel: '#team-Y'
diff --git a/config/testdata/conf.rocketchat-default-token-file.yml b/config/testdata/conf.rocketchat-default-token-file.yml
new file mode 100644
index 00000000..c2984229
--- /dev/null
+++ b/config/testdata/conf.rocketchat-default-token-file.yml
@@ -0,0 +1,22 @@
+global:
+  rocketchat_token_file: /global_file
+  rocketchat_token_id_file: /etc/alertmanager/rocketchat_token_id
+route:
+  group_by: ['alertname', 'cluster', 'service']
+  group_wait: 30s
+  group_interval: 5m
+  repeat_interval: 3h
+  receiver: team-Y-rocketchat
+  routes:
+    - match:
+        service: foo
+      receiver: team-X-rocketchat
+receivers:
+  - name: 'team-X-rocketchat'
+    rocketchat_configs:
+      - channel: '#team-X'
+  - name: 'team-Y-rocketchat'
+    rocketchat_configs:
+      - channel: '#team-Y'
+        token_file: /override_file
+        token_id_file: /override_file
diff --git a/config/testdata/conf.rocketchat-default-token.yml b/config/testdata/conf.rocketchat-default-token.yml
new file mode 100644
index 00000000..01387472
--- /dev/null
+++ b/config/testdata/conf.rocketchat-default-token.yml
@@ -0,0 +1,22 @@
+global:
+  rocketchat_token: token123
+  rocketchat_token_id: id123
+route:
+  group_by: ['alertname', 'cluster', 'service']
+  group_wait: 30s
+  group_interval: 5m
+  repeat_interval: 3h
+  receiver: team-Y-rocketchat
+  routes:
+    - match:
+        service: foo
+      receiver: team-X-rocketchat
+receivers:
+  - name: 'team-X-rocketchat'
+    rocketchat_configs:
+      - channel: '#team-X'
+  - name: 'team-Y-rocketchat'
+    rocketchat_configs:
+      - channel: '#team-Y'
+        token: token456
+        token_id: id456
diff --git a/config/testdata/conf.rocketchat-no-token.yml b/config/testdata/conf.rocketchat-no-token.yml
new file mode 100644
index 00000000..ca443888
--- /dev/null
+++ b/config/testdata/conf.rocketchat-no-token.yml
@@ -0,0 +1,19 @@
+global:
+  rocketchat_token_id: id123
+route:
+  group_by: ['alertname', 'cluster', 'service']
+  group_wait: 30s
+  group_interval: 5m
+  repeat_interval: 3h
+  receiver: team-Y-rocketchat
+  routes:
+    - match:
+        service: foo
+      receiver: team-X-rocketchat
+receivers:
+  - name: 'team-X-rocketchat'
+    rocketchat_configs:
+      - channel: '#team-X'
+  - name: 'team-Y-rocketchat'
+    rocketchat_configs:
+      - channel: '#team-Y'
diff --git a/notify/rocketchat/rocketchat_test.go b/notify/rocketchat/rocketchat_test.go
new file mode 100644
index 00000000..35033d3d
--- /dev/null
+++ b/notify/rocketchat/rocketchat_test.go
@@ -0,0 +1,66 @@
+// Copyright 2019 Prometheus Team
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rocketchat
+
+import (
+	"fmt"
+	"net/url"
+	"os"
+	"testing"
+
+	"github.com/go-kit/log"
+	commoncfg "github.com/prometheus/common/config"
+	"github.com/stretchr/testify/require"
+
+	"github.com/prometheus/alertmanager/config"
+	"github.com/prometheus/alertmanager/notify/test"
+)
+
+func TestRocketchatRetry(t *testing.T) {
+	secret := config.Secret("xxxxx")
+	notifier, err := New(
+		&config.RocketchatConfig{
+			HTTPConfig: &commoncfg.HTTPClientConfig{},
+			Token:      &secret,
+			TokenID:    &secret,
+		},
+		test.CreateTmpl(t),
+		log.NewNopLogger(),
+	)
+	require.NoError(t, err)
+
+	for statusCode, expected := range test.RetryTests(test.DefaultRetryCodes()) {
+		actual, _ := notifier.retrier.Check(statusCode, nil)
+		require.Equal(t, expected, actual, fmt.Sprintf("error on status %d", statusCode))
+	}
+}
+
+func TestGettingRocketchatTokenFromFile(t *testing.T) {
+	f, err := os.CreateTemp("", "rocketchat_test")
+	require.NoError(t, err, "creating temp file failed")
+	_, err = f.WriteString("secret")
+	require.NoError(t, err, "writing to temp file failed")
+
+	_, err = New(
+		&config.RocketchatConfig{
+			TokenFile:   f.Name(),
+			TokenIDFile: f.Name(),
+			HTTPConfig:  &commoncfg.HTTPClientConfig{},
+			APIURL:      &config.URL{URL: &url.URL{Scheme: "http", Host: "example.com", Path: "/api/v1/"}},
+		},
+		test.CreateTmpl(t),
+		log.NewNopLogger(),
+	)
+	require.NoError(t, err)
+}