The First Question Nobody Can Answer Fast Enough
A CloudWatch alarm fires at 2:14 AM. Error rate on your payments service has crossed 5%. You page the on-call engineer, they open the incident, and the first thing they ask is: "Did anyone deploy recently?"
Now someone has to check CodePipeline. Someone else checks the Slack deploy channel. A third person scrolls through GitHub commits. Meanwhile the incident is live and your customers are hitting errors. This coordination overhead — before anyone has even started debugging — is one of the most avoidable parts of incident response.
The fix is not a better runbook. It is logging every deployment as a structured change event so that when an alarm fires, the incident context already includes what changed and when.
What a Change Log Entry Actually Needs
A deployment event that is useful during an incident needs four things: a timestamp, the affected service or component, who or what triggered it, and optionally a link back to the pipeline run or commit.
Alert24's change log API accepts exactly this structure. Each change event you POST shows up as a marker on the incident timeline for any alert that fires on a related monitor. When your on-call engineer opens the incident, they see "Payments service deployed 8 minutes ago" without having to ask anyone.
Here is the shape of a change event:
{
"component": "payments-service",
"description": "Deploy v2.14.1 — feat: retry logic for failed charges",
"triggered_by": "codepipeline/payments-prod",
"metadata": {
"commit": "a3f9c12",
"pipeline_execution_id": "abc-123",
"environment": "production"
}
}
The component field is how Alert24 correlates this change to monitors — any monitor you have tagged with payments-service will show this event in its incident context when it alarms.
Posting Deployments from CodePipeline
The cleanest pattern for CodePipeline is a post-deploy Lambda invoked at the end of your Deploy stage. You add a new action to your pipeline's final stage with ActionTypeId of AWS::Lambda::Invoke, and that Lambda calls Alert24's changes endpoint.
Here is a minimal Lambda that handles the CodePipeline invocation:
import json
import boto3
import urllib.request
import os
codepipeline = boto3.client('codepipeline')
ALERT24_API_KEY = os.environ['ALERT24_API_KEY']
ALERT24_CHANGES_URL = 'https://api.alert24.com/v1/changes'
def handler(event, context):
job_id = event['CodePipeline.job']['id']
job_data = event['CodePipeline.job']['data']
pipeline_name = job_data['actionConfiguration']['configuration'].get(
'UserParameters', 'unknown-pipeline'
)
payload = json.dumps({
"component": pipeline_name,
"description": f"Deployment completed via {pipeline_name}",
"triggered_by": f"codepipeline/{pipeline_name}",
"metadata": {
"job_id": job_id,
"environment": "production"
}
}).encode('utf-8')
req = urllib.request.Request(
ALERT24_CHANGES_URL,
data=payload,
headers={
'Content-Type': 'application/json',
'Authorization': f'Bearer {ALERT24_API_KEY}'
},
method='POST'
)
try:
urllib.request.urlopen(req, timeout=5)
codepipeline.put_job_success_result(jobId=job_id)
except Exception as e:
# Do not fail the pipeline if the change log call fails
print(f"Warning: failed to post change event: {e}")
codepipeline.put_job_success_result(jobId=job_id)
A few things worth noting about this implementation. First, the exception handler calls put_job_success_result regardless of whether the Alert24 call succeeded. You do not want a monitoring integration to break your deploy pipeline. Log the failure, alert on it separately if it matters to you, but never let it block a deployment. Second, the Lambda needs only the codepipeline:PutJobSuccessResult and codepipeline:PutJobFailureResult permissions plus whatever is needed to read the API key from Secrets Manager if you store it there.
Wiring It Up in CloudFormation or Terraform
If you are managing your pipeline as infrastructure-as-code, here is the Terraform snippet that adds the notification action to an existing stage:
action {
name = "NotifyChangelog"
category = "Invoke"
owner = "AWS"
provider = "Lambda"
version = "1"
run_order = 99
configuration = {
FunctionName = aws_lambda_function.alert24_deploy_notifier.function_name
UserParameters = var.service_name
}
}
Set run_order to a number higher than your actual deploy action so it only fires after the deploy succeeds. The UserParameters value becomes your component name in Alert24, so use the same identifier you have on your monitors.
GitHub Actions Alternative
If your deployments go through GitHub Actions rather than CodePipeline, a curl step at the end of your workflow handles the same job:
- name: Log deployment to Alert24
if: success()
run: |
curl -s -X POST https://api.alert24.com/v1/changes \
-H "Authorization: Bearer ${{ secrets.ALERT24_API_KEY }}" \
-H "Content-Type: application/json" \
-d '{
"component": "${{ env.SERVICE_NAME }}",
"description": "Deploy ${{ github.ref_name }} — ${{ github.event.head_commit.message }}",
"triggered_by": "github-actions/${{ github.workflow }}",
"metadata": {
"commit": "${{ github.sha }}",
"run_id": "${{ github.run_id }}",
"actor": "${{ github.actor }}"
}
}'
The if: success() condition means this step only runs when the deploy steps before it passed. You get an accurate record of completed deployments, not attempted ones.
How the Correlation Works in Practice
Once you are posting deployment events, the incident timeline becomes self-explanatory. Here is what the context looks like when a CloudWatch alarm fires shortly after a deploy:
| Time | Event | Type |
|---|---|---|
| 02:06 AM | payments-service deployed v2.14.1 | Change |
| 02:11 AM | payments-service error rate > 5% | Alert triggered |
| 02:14 AM | Incident created, on-call paged | Incident |
Your on-call engineer opens the incident and the deployment is already there, timestamped eight minutes before the alarm. The question "what changed?" has an answer before anyone has to ask it. That is the difference between a 20-minute investigation and a 3-minute one.
Component Tagging Is the Load-Bearing Piece
The correlation only works if your monitors and your change events use the same component identifiers. Before you wire up the Lambda or GitHub Actions step, check how your CloudWatch monitors are tagged in Alert24. If your monitor for the payments service error rate is tagged payments-service, your deploy events need "component": "payments-service" — not payments, not payments-prod, not the pipeline ARN.
A simple convention that works well: use the same name as your ECS service, Lambda function name, or Kubernetes deployment. Whatever you use in one place, use it everywhere. Consistency here is what makes the correlation automatic rather than manual.
Next Steps
Start with one service — your most frequently deployed, or the one that has caused the most incident confusion. Add the post-deploy Lambda or GitHub Actions step, tag your monitors consistently, and watch the next incident response to see how different it feels when the deployment event is already there.
After that, extend it to Terraform runs using a null_resource with a local-exec provisioner calling the same API, and to any manual hotfixes using the Alert24 web UI or API directly. The goal is that every change that could affect production appears in the change log before it has a chance to cause an incident you cannot explain.
The alarm firing is not the problem. Not knowing what changed when it fires is the problem.