diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml
index 3a4091c0..7d8cfcdd 100644
--- a/.github/workflows/development.yml
+++ b/.github/workflows/development.yml
@@ -262,6 +262,10 @@ jobs:
           TEST_EMAIL: ${{ secrets.TEST_EMAIL }}
           GOTIFY_URL: ${{ secrets.GOTIFY_URL }}
           GOTIFY_TOKEN: ${{ secrets.GOTIFY_TOKEN }}
+          SNS_TOKEN: ${{ secrets.SNS_TOKEN }}
+          SNS_SECRET: ${{ secrets.SNS_SECRET }}
+          SNS_REGION: ${{ secrets.SNS_REGION }}
+          SNS_TOPIC: ${{ secrets.SNS_TOPIC }}
 
       - name: Coveralls Testing Coverage
         run: |
diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml
index 6cabf8c8..da7be637 100644
--- a/.github/workflows/master.yml
+++ b/.github/workflows/master.yml
@@ -262,6 +262,10 @@ jobs:
           TEST_EMAIL: ${{ secrets.TEST_EMAIL }}
           GOTIFY_URL: ${{ secrets.GOTIFY_URL }}
           GOTIFY_TOKEN: ${{ secrets.GOTIFY_TOKEN }}
+          SNS_TOKEN: ${{ secrets.SNS_TOKEN }}
+          SNS_SECRET: ${{ secrets.SNS_SECRET }}
+          SNS_REGION: ${{ secrets.SNS_REGION }}
+          SNS_TOPIC: ${{ secrets.SNS_TOPIC }}
 
       - name: Coveralls Testing Coverage
         run: |
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 79e15210..ae167032 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,7 @@
 - Modified Service Group failures on index page to show 90 days of failures
 - Modified Service view page, updated Latency and Ping charts, added failures below
 - Modified Service chart on index page to show ping data along with latency
+- Added AWS SNS Notifier
 
 # 0.90.63 (08-17-2020)
 - Modified build process to use xgo for all arch builds
diff --git a/frontend/src/API.js b/frontend/src/API.js
index b878eb08..89e212c2 100644
--- a/frontend/src/API.js
+++ b/frontend/src/API.js
@@ -4,12 +4,17 @@ const qs = require('querystring');
 axios.defaults.withCredentials = true
 
 const tokenKey = "statping_auth";
+const version = "0.90.64";
+const commit = "8b54ceb16f0e2ca6c4f5b8f0fe3b5cc2598dc594";
 
 class Api {
   constructor() {
 
   }
 
+  version = () => version
+  commit = () => commit
+
   async oauth() {
     const oauth = axios.get('api/oauth').then(response => (response.data))
     return oauth
diff --git a/frontend/src/pages/Help.vue b/frontend/src/pages/Help.vue
index f2b4f75e..af66fff0 100755
--- a/frontend/src/pages/Help.vue
+++ b/frontend/src/pages/Help.vue
@@ -2275,7 +2275,7 @@ OluFxewsEO0QNDrfFb+0gnjYlnGqOFcZjUMXbDdY5oLSPtXohynuTK1qyQ==
 </div>
 
 <div class="text-center small text-dim" v-pre>
-Automatically generated from Statping's Wiki on 2020-08-19 02:31:01.555206 &#43;0000 UTC
+Automatically generated from Statping's Wiki on 2020-08-20 04:46:47.972956 &#43;0000 UTC
 </div>
 
 </div>
diff --git a/go.mod b/go.mod
index 0a8bf024..c4366a12 100644
--- a/go.mod
+++ b/go.mod
@@ -5,6 +5,7 @@ go 1.14
 
 require (
 	github.com/GeertJohan/go.rice v1.0.0
+	github.com/aws/aws-sdk-go v1.30.20
 	github.com/dgrijalva/jwt-go v3.2.0+incompatible
 	github.com/fatih/structs v1.1.0
 	github.com/foomo/simplecert v1.7.5
diff --git a/notifiers/amazon_sns.go b/notifiers/amazon_sns.go
new file mode 100644
index 00000000..fb21f1b3
--- /dev/null
+++ b/notifiers/amazon_sns.go
@@ -0,0 +1,150 @@
+package notifiers
+
+import (
+	"fmt"
+	"github.com/aws/aws-sdk-go/aws"
+	"github.com/aws/aws-sdk-go/aws/credentials"
+	"github.com/aws/aws-sdk-go/aws/session"
+	"github.com/aws/aws-sdk-go/service/sns"
+	"github.com/statping/statping/types/null"
+	"time"
+
+	"github.com/statping/statping/types/failures"
+	"github.com/statping/statping/types/notifications"
+	"github.com/statping/statping/types/notifier"
+	"github.com/statping/statping/types/services"
+)
+
+var _ notifier.Notifier = (*amazonSNS)(nil)
+
+type amazonSNS struct {
+	*notifications.Notification
+}
+
+func (g *amazonSNS) Select() *notifications.Notification {
+	return g.Notification
+}
+
+func (g *amazonSNS) Valid(values notifications.Values) error {
+	return nil
+}
+
+var AmazonSNS = &amazonSNS{&notifications.Notification{
+	Method:      "amazon_sns",
+	Title:       "Amazon SNS",
+	Description: "Use amazonSNS to receive push notifications. Add your amazonSNS URL and App Token to receive notifications.",
+	Author:      "Hunter Long",
+	AuthorUrl:   "https://github.com/hunterlong",
+	Icon:        "amazon",
+	Delay:       5 * time.Second,
+	Limits:      60,
+	SuccessData: null.NewNullString(`{{.Service.Name}} is back online and was down for {{.Service.Downtime.Human}}`),
+	FailureData: null.NewNullString(`{{.Service.Name}} is offline and has been down for {{.Service.Downtime.Human}}`),
+	DataType:    "html",
+	Form: []notifications.NotificationForm{{
+		Type:        "text",
+		Title:       "AWS Access Token",
+		DbField:     "api_key",
+		Placeholder: "AKPMED5XUXSEU3O5AB6M",
+		Required:    true,
+	}, {
+		Type:        "text",
+		Title:       "AWS Secret Key",
+		DbField:     "api_secret",
+		Placeholder: "39eAZODxEosHRgzLx173ttX9sCtJVOE8rzElRE9B",
+		Required:    true,
+	}, {
+		Type:        "text",
+		Title:       "Region",
+		SmallText:   "Amazon Region for SNS",
+		DbField:     "var1",
+		Placeholder: "us-west-2",
+		Required:    true,
+	}, {
+		Type:        "text",
+		Title:       "SNS Topic ARN",
+		SmallText:   "The ARN of the Topic",
+		DbField:     "host",
+		Placeholder: "arn:aws:sns:us-west-2:123456789012:YourTopic",
+		Required:    true,
+	}}},
+}
+
+func valToAttr(val interface{}) *sns.MessageAttributeValue {
+	dataType := "String"
+	switch val.(type) {
+	case string, bool:
+		dataType = "String"
+	case int, int64, uint, uint64, uint32:
+		dataType = "Number"
+	}
+	return &sns.MessageAttributeValue{
+		DataType:    aws.String(dataType),
+		StringValue: aws.String(fmt.Sprintf("%v", val)),
+	}
+}
+
+func messageAttributesSNS(s services.Service, f failures.Failure) map[string]*sns.MessageAttributeValue {
+	attr := make(map[string]*sns.MessageAttributeValue)
+	attr["service_id"] = valToAttr(s.Id)
+	attr["online"] = valToAttr(s.Online)
+	attr["downtime_milliseconds"] = valToAttr(s.Downtime().Milliseconds())
+	if f.Id != 0 {
+		attr["failure_issue"] = valToAttr(f.Issue)
+		attr["failure_reason"] = valToAttr(f.Reason)
+		attr["failure_status_code"] = valToAttr(f.ErrorCode)
+		attr["failure_ping"] = valToAttr(f.PingTime)
+	}
+	return attr
+}
+
+// Send will send a HTTP Post to the amazonSNS API. It accepts type: string
+func (g *amazonSNS) sendMessage(msg string, s services.Service, f failures.Failure) (string, error) {
+	creds := credentials.NewStaticCredentials(g.ApiKey.String, g.ApiSecret.String, "")
+	c := aws.NewConfig()
+	c.Credentials = creds
+	c.Region = aws.String(g.Var1.String)
+	sess, err := session.NewSession(c)
+	if err != nil {
+		return "", err
+	}
+
+	client := sns.New(sess)
+	input := &sns.PublishInput{
+		Message:           aws.String(msg),
+		TopicArn:          aws.String(g.Host.String),
+		MessageAttributes: messageAttributesSNS(s, f),
+	}
+
+	result, err := client.Publish(input)
+	if err != nil {
+		return "", err
+	}
+
+	return result.String(), nil
+}
+
+// OnFailure will trigger failing service
+func (g *amazonSNS) OnFailure(s services.Service, f failures.Failure) (string, error) {
+	msg := ReplaceVars(g.FailureData.String, s, f)
+	return g.sendMessage(msg, s, f)
+}
+
+// OnSuccess will trigger successful service
+func (g *amazonSNS) OnSuccess(s services.Service) (string, error) {
+	msg := ReplaceVars(g.SuccessData.String, s, failures.Failure{})
+	return g.sendMessage(msg, s, failures.Failure{})
+}
+
+// OnTest will test the amazonSNS notifier
+func (g *amazonSNS) OnTest() (string, error) {
+	s := services.Example(true)
+	f := failures.Example()
+	msg := ReplaceVars(`This is a test SNS notification from Statping. Service: {{.Service.Name}} - Downtime: {{.Service.Downtime.Human}}`, s, f)
+	return g.sendMessage(msg, s, f)
+}
+
+// OnSave will trigger when this notifier is saved
+func (g *amazonSNS) OnSave() (string, error) {
+	return "", nil
+}
diff --git a/notifiers/amazon_sns_test.go b/notifiers/amazon_sns_test.go
new file mode 100644
index 00000000..c9988c3e
--- /dev/null
+++ b/notifiers/amazon_sns_test.go
@@ -0,0 +1,76 @@
+package notifiers
+
+import (
+	"testing"
+	"time"
+
+	"github.com/statping/statping/database"
+	"github.com/statping/statping/types/core"
+	"github.com/statping/statping/types/failures"
+	"github.com/statping/statping/types/notifications"
+	"github.com/statping/statping/types/null"
+	"github.com/statping/statping/types/services"
+	"github.com/statping/statping/utils"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+)
+
+func TestAmazonSNSNotifier(t *testing.T) {
+	err := utils.InitLogs()
+	require.Nil(t, err)
+	snsToken := utils.Params.GetString("SNS_TOKEN")
+	snsSecret := utils.Params.GetString("SNS_SECRET")
+	snsRegion := utils.Params.GetString("SNS_REGION")
+	snsTopic := utils.Params.GetString("SNS_TOPIC")
+
+	db, err := database.OpenTester()
+	require.Nil(t, err)
+	db.AutoMigrate(&notifications.Notification{})
+	notifications.SetDB(db)
+	core.Example()
+
+	if snsToken == "" || snsSecret == "" || snsRegion == "" || snsTopic == "" {
+		t.Log("SNS notifier testing skipped, missing SNS_TOKEN, SNS_SECRET, SNS_REGION, SNS_TOPIC environment variables")
+		t.SkipNow()
+	}
+
+	t.Run("Load SNS", func(t *testing.T) {
+		AmazonSNS.ApiKey = null.NewNullString(snsToken)
+		AmazonSNS.ApiSecret = null.NewNullString(snsSecret)
+		AmazonSNS.Var1 = null.NewNullString(snsRegion)
+		AmazonSNS.Host = null.NewNullString(snsTopic)
+		AmazonSNS.Delay = 15 * time.Second
+		AmazonSNS.Enabled = null.NewNullBool(true)
+
+		Add(AmazonSNS)
+
+		assert.Equal(t, "Hunter Long", AmazonSNS.Author)
+		assert.Equal(t, snsToken, AmazonSNS.ApiKey.String)
+		assert.Equal(t, snsSecret, AmazonSNS.ApiSecret.String)
+	})
+
+	t.Run("SNS Notifier Tester", func(t *testing.T) {
+		assert.True(t, AmazonSNS.CanSend())
+	})
+
+	t.Run("SNS Notifier Tester OnSave", func(t *testing.T) {
+		_, err := AmazonSNS.OnSave()
+		assert.Nil(t, err)
+	})
+
+	t.Run("SNS OnFailure", func(t *testing.T) {
+		_, err := AmazonSNS.OnFailure(services.Example(false), failures.Example())
+		assert.Nil(t, err)
+	})
+
+	t.Run("SNS OnSuccess", func(t *testing.T) {
+		_, err := AmazonSNS.OnSuccess(services.Example(true))
+		assert.Nil(t, err)
+	})
+
+	t.Run("SNS Test", func(t *testing.T) {
+		_, err := AmazonSNS.OnTest()
+		assert.Nil(t, err)
+	})
+
+}
diff --git a/notifiers/mobile_test.go b/notifiers/mobile_test.go
index 14029178..b53ee849 100644
--- a/notifiers/mobile_test.go
+++ b/notifiers/mobile_test.go
@@ -47,7 +47,7 @@ func TestMobileNotifier(t *testing.T) {
 		Add(Mobile)
 
 		assert.Equal(t, "Hunter Long", Mobile.Author)
-		assert.Equal(t, mobileToken, Mobile.Var1)
+		assert.Equal(t, mobileToken, Mobile.Var1.String)
 	})
 
 	t.Run("Mobile Notifier Tester", func(t *testing.T) {
diff --git a/notifiers/notifiers.go b/notifiers/notifiers.go
index d2cc6680..4bed95f4 100644
--- a/notifiers/notifiers.go
+++ b/notifiers/notifiers.go
@@ -35,6 +35,7 @@ func InitNotifiers() {
 		Pushover,
 		statpingMailer,
 		Gotify,
+		AmazonSNS,
 	)
 }
 
diff --git a/notifiers/slack.go b/notifiers/slack.go
index f476da66..bc552b9f 100644
--- a/notifiers/slack.go
+++ b/notifiers/slack.go
@@ -9,6 +9,7 @@ import (
 	"github.com/statping/statping/types/null"
 	"github.com/statping/statping/types/services"
 	"github.com/statping/statping/utils"
+	"regexp"
 	"strings"
 	"time"
 )
@@ -93,5 +94,10 @@ func (s *slack) OnSave() (string, error) {
 }
 
 func (s *slack) Valid(values notifications.Values) error {
+	regex := `https\:\/\/hooks\.slack\.com/services/[A-Z0-9]{9}/[A-Z0-9]{10}/[a-zA-Z0-9]{22}`
+	r := regexp.MustCompile(regex)
+	if !r.MatchString(values.Host) {
+		return errors.New("slack webhook does not match with expected regex " + regex)
+	}
 	return nil
 }
diff --git a/source/generate_help.go b/source/generate_help.go
index 99ee9150..6bccd7cf 100644
--- a/source/generate_help.go
+++ b/source/generate_help.go
@@ -100,6 +100,7 @@ type Render struct {
 }
 
 func main() {
+	fmt.Println("RUNNING: ./source/generate_help.go")
 	fmt.Println("\n\nGenerating Help.vue from Statping's Wiki")
 	fmt.Println("Cloning ", wikiUrl)
 	cmd := exec.Command("git", "clone", wikiUrl)
diff --git a/source/generate_version.go b/source/generate_version.go
new file mode 100644
index 00000000..db35378a
--- /dev/null
+++ b/source/generate_version.go
@@ -0,0 +1,41 @@
+// +build ignore
+
+package main
+
+import (
+	"bytes"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"os/exec"
+	"regexp"
+	"strings"
+)
+
+const replace = `const version = "[0-9]\.[0-9]{2}\.[0-9]{2}";`
+const replaceCommit = `const commit = \"[a-z0-9]{40}\"\;`
+
+func main() {
+	fmt.Println("RUNNING: ./source/generate_version.go")
+	version, _ := ioutil.ReadFile("../version.txt")
+	apiJsFile, _ := ioutil.ReadFile("../frontend/src/API.js")
+
+	w := bytes.NewBuffer(nil)
+	cmd := exec.Command("git", "rev-parse", "HEAD")
+	cmd.Stdout = w
+	cmd.Run()
+	gitCommit := strings.TrimSpace(w.String())
+
+	fmt.Println("git commit: ", gitCommit)
+
+	replaceWith := `const version = "` + strings.TrimSpace(string(version)) + `";`
+	replaceCommitWith := `const commit = "` + gitCommit + `";`
+
+	vRex := regexp.MustCompile(replace)
+	newApiFile := vRex.ReplaceAllString(string(apiJsFile), replaceWith)
+	cRex := regexp.MustCompile(replaceCommit)
+	newApiFile = cRex.ReplaceAllString(newApiFile, replaceCommitWith)
+
+	fmt.Printf("Setting version %s to frontend/src/API.js\n", string(version))
+	ioutil.WriteFile("../frontend/src/API.js", []byte(newApiFile), os.FileMode(0755))
+}
diff --git a/source/source.go b/source/source.go
index 40607612..15d54e7d 100644
--- a/source/source.go
+++ b/source/source.go
@@ -1,6 +1,7 @@
 package source
 
 //go:generate go run generate_help.go
+//go:generate go run generate_version.go
 
 import (
 	"fmt"