Automating SLA Reports for Maintenance Subscriptions
How Many Hours Do You Spend on Monthly Reports?
If you're running maintenance subscriptions for more than ten clients, the end of each month probably means a familiar grind: opening each dashboard, copying numbers into a spreadsheet, formatting charts, and firing off emails. That process can easily eat several hours every month — hours that could go toward actual client work.
Automating SLA reports brings that effort close to zero while actually improving the quality of what clients receive. With Miterl's API, you can chain data collection, metric calculation, and email delivery into a single script that runs on a cron schedule. This guide walks you through the full setup.
Why Manual Reports Break Down
Volume kills the process
Five clients is manageable by hand. At twenty, a full reporting cycle can consume two full workdays. If an incident happens to fall at the end of the month, you're simultaneously fighting a fire and rushing through reports.
Transcription errors erode trust
Copy-pasting numbers from a dashboard introduces the risk of mistakes — wrong month, wrong monitor, off-by-one in a percentage. If a client questions a figure from last month's report, walking back the error costs far more time than the original mistake.
What automation gives you
| Item | Manual | After Automation |
|---|---|---|
| Time per client | 20–40 min | Under 1 min |
| Transcription errors | Possible | Zero |
| Delivery timing | Depends on staff | 1st of each month, automatic |
| Impact of doubling clients | Work doubles | Virtually unchanged |
Step 1: Get Your API Key and Pull Uptime Data
Generate an API key from your Miterl dashboard under Settings → API Keys. Then list your monitors to confirm IDs and current uptime figures.
# List all monitors with id, name, and 30-day uptime
curl -s "https://miterl.com/api/v1/monitors?per_page=100" \
-H "Authorization: Bearer YOUR_API_KEY" \
| jq -r '.data[] | "\(.id)\t\(.name)\t\(.uptime_30d)%"'
Sample output:
1 Client A — Corporate 99.97%
2 Client A — Careers 100.00%
3 Client B — E-commerce 99.83%
uptime_30d covers the rolling last 30 days. If you need calendar-month figures (e.g. May 1–31), calculate them from the incident history in the next step.
When deciding which SLA target to commit to, the "Uptime Percentage Calculator and Reference Guide" gives you pre-calculated monthly allowances for common thresholds — useful for client proposals and contract negotiations.
Step 2: Calculate Calendar-Month Downtime from Incident History
# Fetch resolved incidents for monitor 1 in April 2026 and sum downtime (minutes)
MONITOR_ID=1
YEAR=2026
MONTH=04
curl -s "https://miterl.com/api/v1/incidents?monitor_id=${MONITOR_ID}&status=resolved&per_page=100" \
-H "Authorization: Bearer YOUR_API_KEY" \
| jq --arg ym "${YEAR}-${MONTH}" '
[
.data[]
| select(.resolved_at | startswith($ym))
| .duration_seconds
]
| (add // 0) / 60
| "Total downtime: \(round) minutes"
'
Convert downtime minutes to an uptime percentage:
# Example: 22 minutes of downtime in April (30 days)
python3 -c "
downtime_min = 22
total_min = 30 * 24 * 60
uptime = (1 - downtime_min / total_min) * 100
print(f'Uptime: {uptime:.4f}%')
"
Step 3: Collect All Client Data with a Shell Script
Store your monitor IDs and client names in a mapping, then loop through them to produce a Markdown report file.
#!/bin/bash
# monthly_report_collect.sh
# Usage: MONTH=2026-04 ./monthly_report_collect.sh
API_KEY="YOUR_API_KEY"
BASE_URL="https://miterl.com/api/v1"
MONTH="${MONTH:-$(date +%Y-%m)}"
declare -A MONITORS=(
[1]="Client A — Corporate"
[2]="Client A — Careers"
[3]="Client B — E-commerce"
)
echo "# ${MONTH} Monthly SLA Report (auto-generated)"
echo ""
for MONITOR_ID in "${!MONITORS[@]}"; do
CLIENT_NAME="${MONITORS[$MONITOR_ID]}"
UPTIME=$(curl -s "${BASE_URL}/monitors/${MONITOR_ID}" \
-H "Authorization: Bearer ${API_KEY}" \
| jq -r '.data.uptime_30d // "N/A"')
INCIDENT_DATA=$(curl -s "${BASE_URL}/incidents?monitor_id=${MONITOR_ID}&status=resolved&per_page=100" \
-H "Authorization: Bearer ${API_KEY}" \
| jq --arg ym "${MONTH}" '
[.data[] | select(.resolved_at | startswith($ym))]
| {count: length, total_min: (map(.duration_seconds) | (add // 0) / 60 | round)}
')
INCIDENT_COUNT=$(echo "$INCIDENT_DATA" | jq -r '.count')
DOWNTIME_MIN=$(echo "$INCIDENT_DATA" | jq -r '.total_min')
echo "## ${CLIENT_NAME}"
echo "- Uptime (last 30 days): ${UPTIME}%"
echo "- Incidents this month: ${INCIDENT_COUNT}"
echo "- Total downtime: ${DOWNTIME_MIN} min"
echo ""
done
Schedule it with cron to run on the first of every month:
# Add to crontab — runs at 09:00 on the 1st of each month
0 9 1 * * /path/to/monthly_report_collect.sh > /reports/$(date +\%Y-\%m).md
Step 4: Email Reports Automatically
Convert the Markdown output to plain text (or HTML) and send it through your SMTP server or a transactional email API.
#!/usr/bin/env python3
# send_sla_report.py
import datetime
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
month = datetime.date.today().strftime("%Y-%m")
report_path = f"/reports/{month}.md"
with open(report_path) as f:
report_body = f.read()
CLIENTS = [
{"name": "Client A", "email": "[email protected]"},
{"name": "Client B", "email": "[email protected]"},
]
SMTP_HOST = "smtp.example.com"
SMTP_USER = "[email protected]"
SMTP_PASS = "YOUR_SMTP_PASSWORD"
def send_report(client_name: str, to_email: str, body: str) -> None:
msg = MIMEMultipart("alternative")
msg["Subject"] = f"[{month}] Monthly Site Monitoring Report — {client_name}"
msg["From"] = SMTP_USER
msg["To"] = to_email
msg.attach(MIMEText(body, "plain", "utf-8"))
with smtplib.SMTP(SMTP_HOST, 587) as server:
server.starttls()
server.login(SMTP_USER, SMTP_PASS)
server.sendmail(SMTP_USER, to_email, msg.as_string())
print(f"Sent to {to_email}")
for client in CLIENTS:
send_report(client["name"], client["email"], report_body)
If you prefer a transactional email service (SendGrid, Postmark, Mailgun), swap the SMTP block for their HTTP API — the structure remains the same.
Step 5: Include the Status Page Link in Every Report
Adding a live status page URL to each monthly report gives clients a self-service reference point and reduces inbound questions.
# Fetch status page summary to append to the report
SLUG="client-a-status"
curl -s "https://miterl.com/api/v1/status-pages/${SLUG}" \
| jq -r '"Overall status: \(.data.overall_status) | URL: https://status.client-a.example.com"'
On the Standard plan and above, you can serve status pages on a custom domain (e.g. status.client-a.com) so clients see your agency's branding rather than a Miterl subdomain.
Step 6: Exclude Planned Maintenance from SLA Calculations
Scheduled deployments and server maintenance should not count as downtime in your SLA figures. Register a maintenance window before each deployment using the Webhook API, and Miterl will exclude that window from incident tracking automatically.
# Start a maintenance window before deploying (auto-closes after 1 hour)
curl -X POST https://miterl.com/api/v1/webhooks/maintenance/${MITERL_TOKEN}/start \
-H "Content-Type: application/json" \
-d '{"duration_hours": 1, "name": "Monthly maintenance — 2026-05"}'
One line in your deploy script is all it takes to keep planned outages out of client reports.
What Automation Looks Like in Practice
Once the pipeline is in place, your end-of-month workflow becomes:
- Cron triggers the collection script, writes a Markdown file
- Python script emails each client their report
- Your team checks sent mail and handles any replies
Hours of manual work collapse into minutes, freeing your team to focus on actual maintenance and proactive recommendations rather than copy-pasting numbers into spreadsheets.
Summary
Automating SLA reports is one of the highest-leverage improvements a web agency can make to its maintenance subscription operation. Miterl's API provides the raw data; a shell script and cron handle the collection; a small Python script handles delivery. Client count can double without the reporting burden growing at all.
For a full API reference, see the documentation. The SLA fundamentals and manual report template are covered in "Uptime SLA Report Guide." For status page setup, see "Using Client-Facing Status Pages." For the full Miterl setup walkthrough for agencies, read "Miterl Setup Guide for Web Agencies." Ready to start? Sign up for free or browse use cases to see how other agencies run their monitoring.