AWS alerts to your phone: CloudWatch alarms and EventBridge events
Updated October 17, 2026
There are two ways to get AWS alerts on your phone with NextMsg. For CloudWatch alarms (and anything else that publishes to SNS, such as AWS Budgets), subscribe a tiny Lambda function to an SNS topic. For EventBridge events (a failed pipeline, a stopped ECS task, an EC2 state change), use an API destination: no code at all. Both come as CloudFormation templates below.
First, install the NextMsg app and copy your send key from Settings.
CloudWatch alarms: SNS and Lambda
This template creates an SNS topic and a Lambda function that formats each message and sends it to NextMsg. Alarm messages become a short line such as "🚨 HighCPU is ALARM" with the reason; other SNS messages are sent with their subject.
AWSTemplateFormatVersion: "2010-09-09"
Description: Forward messages on an SNS topic (CloudWatch alarms and others) to NextMsg
Parameters:
NextMsgKey:
Type: String
NoEcho: true
Description: NextMsg send key (nextmsg_live_...)
Resources:
AlertsTopic:
Type: AWS::SNS::Topic
NotifyRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal: { Service: lambda.amazonaws.com }
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
NotifyFunction:
Type: AWS::Lambda::Function
Properties:
Runtime: nodejs22.x
Handler: index.handler
Role: !GetAtt NotifyRole.Arn
Timeout: 10
Environment:
Variables:
NEXTMSG_KEY: !Ref NextMsgKey
Code:
ZipFile: |
exports.handler = async (event) => {
for (const record of event.Records) {
const { Subject, Message } = record.Sns;
let text = Subject ? `${Subject}\n${Message}` : Message;
try {
const alarm = JSON.parse(Message);
if (alarm.AlarmName) {
const icon = { ALARM: "🚨", OK: "✅" }[alarm.NewStateValue] || "ℹ️";
text = `${icon} ${alarm.AlarmName} is ${alarm.NewStateValue}\n${alarm.NewStateReason}`;
}
} catch {}
const res = await fetch("https://api.nextmsg.app/v1/send", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.NEXTMSG_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ message: [...text].slice(0, 640).join(""), channel: "aws" }),
});
if (!res.ok) throw new Error(`NextMsg ${res.status}: ${await res.text()}`);
}
};
TopicSubscription:
Type: AWS::SNS::Subscription
Properties:
TopicArn: !Ref AlertsTopic
Protocol: lambda
Endpoint: !GetAtt NotifyFunction.Arn
InvokePermission:
Type: AWS::Lambda::Permission
Properties:
Action: lambda:InvokeFunction
FunctionName: !Ref NotifyFunction
Principal: sns.amazonaws.com
SourceArn: !Ref AlertsTopic
Outputs:
TopicArn:
Description: Use this topic as the action of any CloudWatch alarm
Value: !Ref AlertsTopicSave it as nextmsg-sns.yaml and deploy. Reading the key with read -s keeps it out of your shell history:
read -rs NEXTMSG_KEY
aws cloudformation deploy --template-file nextmsg-sns.yaml --stack-name nextmsg-alerts \
--parameter-overrides NextMsgKey="$NEXTMSG_KEY" --capabilities CAPABILITY_IAM
aws cloudformation describe-stacks --stack-name nextmsg-alerts \
--query "Stacks[0].Outputs[?OutputKey=='TopicArn'].OutputValue" --output textUse the topic ARN as the alarm action, in the console or with the CLI:
aws cloudwatch put-metric-alarm --alarm-name HighCPU \
--namespace AWS/EC2 --metric-name CPUUtilization --statistic Average \
--dimensions Name=InstanceId,Value=i-0123456789abcdef0 \
--period 300 --evaluation-periods 2 --threshold 90 --comparison-operator GreaterThanThreshold \
--alarm-actions TOPIC_ARN --ok-actions TOPIC_ARNTest the whole path without waiting for an alarm:
aws sns publish --topic-arn TOPIC_ARN --subject "Test" --message "Hello from AWS"EventBridge events: an API destination
EventBridge can call NextMsg directly. The connection stores the key as an Authorization: Bearer header (EventBridge keeps it in AWS Secrets Manager), and an input transformer turns the event into a NextMsg message. This example alerts on failed CodePipeline executions:
AWSTemplateFormatVersion: "2010-09-09"
Description: Send failed CodePipeline executions to NextMsg through an EventBridge API destination
Parameters:
NextMsgKey:
Type: String
NoEcho: true
Description: NextMsg send key (nextmsg_live_...)
Resources:
NextMsgConnection:
Type: AWS::Events::Connection
Properties:
AuthorizationType: API_KEY
AuthParameters:
ApiKeyAuthParameters:
ApiKeyName: Authorization
ApiKeyValue: !Sub "Bearer ${NextMsgKey}"
NextMsgDestination:
Type: AWS::Events::ApiDestination
Properties:
ConnectionArn: !GetAtt NextMsgConnection.Arn
InvocationEndpoint: https://api.nextmsg.app/v1/send
HttpMethod: POST
InvocationRateLimitPerSecond: 1
InvokeRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal: { Service: events.amazonaws.com }
Action: sts:AssumeRole
Policies:
- PolicyName: invoke-nextmsg
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action: events:InvokeApiDestination
Resource: !GetAtt NextMsgDestination.Arn
PipelineFailedRule:
Type: AWS::Events::Rule
Properties:
EventPattern:
source: [aws.codepipeline]
detail-type: [CodePipeline Pipeline Execution State Change]
detail:
state: [FAILED]
Targets:
- Id: nextmsg
Arn: !GetAtt NextMsgDestination.Arn
RoleArn: !GetAtt InvokeRole.Arn
RetryPolicy:
MaximumRetryAttempts: 3
InputTransformer:
InputPathsMap:
pipeline: $.detail.pipeline
region: $.region
InputTemplate: |
{"message": "❌ Pipeline <pipeline> failed in <region>", "channel": "aws"}Deploy it the same way (--template-file nextmsg-eventbridge.yaml --stack-name nextmsg-pipelines). To watch something else, change only the EventPattern and the message. For example, stopped ECS tasks:
EventPattern:
source: [aws.ecs]
detail-type: [ECS Task State Change]
detail:
lastStatus: [STOPPED]
# ...
InputPathsMap:
group: $.detail.group
reason: $.detail.stoppedReason
InputTemplate: |
{"message": "ECS task in stopped: ", "channel": "aws"} Notes
- Keep the key secret: the stack parameter is
NoEcho. In the Lambda version the key is a plain environment variable, visible to anyone who can read the function's configuration; for stricter setups, load it from AWS Secrets Manager or SSM Parameter Store instead. - Retries: EventBridge retries
429and5xxresponses, so the template limits it to 3 attempts. SNS retries a failed Lambda invocation on its own. - Regions: alarms publish to an SNS topic in their own Region, so deploy the SNS stack in each Region you have alarms in (AWS billing alarms live in us-east-1).
- Limits: messages are cut to 640 characters. The free plan includes 100 messages a month; a noisy alarm can use that quickly, so Pro (unlimited, fair use 5,000 a day) suits busy accounts.
More guides
- Get a phone notification when a GitHub Action fails
- Notify when a cron job or backup finishes or fails
- Server alerts to your phone without Telegram or Slack
- Make Claude Code, Cursor or any AI agent ping your phone
- Get a phone notification when Cursor's agent finishes
- Get a phone notification when OpenAI Codex finishes a task
- Phone notifications from Google Antigravity and Gemini agents