How to Automate Monthly Uptime Reports for Clients
Monthly Reports Are Your Maintenance Contract's Report Card
Even with a signed maintenance subscription in place, clients still ask: "What exactly are you doing each month?" Development work and WordPress updates are visible. Uptime monitoring and incident response are not — unless you show the numbers.
A monthly uptime report is the most direct way to make invisible work visible. "This month we held 99.97% uptime. One incident occurred, but we detected it before you did and restored service in 15 minutes" — that kind of statement justifies your retainer fee, reinforces trust, and raises the bar for cancellation.
The problem is scale. Building reports by hand is fine for five clients. Beyond that, it becomes a recurring drag at month-end. This guide shows how to pull uptime and incident data from Miterl's API and generate automated uptime reports for every client on your roster, so you spend minutes, not hours, on this task each month.
Why Manual Reports Don't Scale
Client count and labor grow together
At 20–30 minutes per client, ten clients cost you three to five hours each month. Twenty clients eat a full day. When a late-month incident coincides with reporting week, you're fighting a fire and racing a deadline at the same time.
Manual transcription introduces errors
Copying numbers from a dashboard by hand means there's always a risk of a wrong month, a wrong monitor, or an off-by-one in a percentage. If a client questions a figure from last month, walking it back takes more time and credibility than the original error cost.
What automation changes
| Item | Manual | After Automation |
|---|---|---|
| Time per client | 20–30 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 |
What Data Miterl Provides
Miterl stores uptime percentages and incident records for every monitor you configure. You can retrieve this data through the API and feed it directly into your report generation script — no dashboard tab-switching required.
The three data points most useful for client reports are:
- Rolling 30-day uptime (
uptime_30d): Percentage of time the monitor was up over the last 30 days - Incident list (
/api/v1/incidents): Start time, resolution time, and duration in seconds for each outage - Status page URL: A public page clients can check themselves at any time
Miterl also has a native Client Reports page (Dashboard → Client Reports, Standard plan and above): group monitors under a client, turn on auto-delivery, and Miterl generates a PDF report and emails it to the addresses you configure automatically on the 1st of every month. The API-based workflow in this guide is the right fit if you're on the Free plan, want full control over report format and branding, or need to merge uptime data into a report layout of your own rather than the built-in PDF.
Step 1: Get Your API Key and List Your Monitors
Generate an API key from the Miterl dashboard under Settings → API Keys. Then confirm your monitor 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%
Step 2: Pull Calendar-Month Incident Data
uptime_30d covers a rolling 30-day window. For a calendar-month view (e.g., June 1–30), filter the incident list by resolution date.
# Fetch resolved incidents for monitor 1 in June 2026 and sum downtime
MONITOR_ID=1
YEAR=2026
MONTH=06
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))
]
| {
count: length,
total_downtime_min: (map(.duration_seconds) | (add // 0) / 60 | round)
}
'
Sample output:
{
"count": 1,
"total_downtime_min": 9
}
To convert downtime minutes to an uptime percentage for a 30-day month:
# Example: 9 minutes of downtime in a 30-day month
downtime_min = 9
total_min = 30 * 24 * 60 # 43200 minutes
uptime_pct = (1 - downtime_min / total_min) * 100
print(f"Uptime: {uptime_pct:.4f}%")
# → Uptime: 99.9792%
For a quick reference on what common SLA thresholds (99.9%, 99.95%, 99.99%) allow in monthly downtime minutes, the "Uptime Percentage Calculator and Reference Guide" has the full conversion table.
Step 3: Merge Uptime and Incident Data Into One Client Report
Store your monitor IDs and client names in a mapping, then loop through them to generate a combined report file — the last step before you have a fully automated uptime report ready to send.
#!/bin/bash
# monthly_report_collect.sh
# Usage: MONTH=2026-06 ./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 Uptime Report (auto-generated)"
echo ""
for MONITOR_ID in "${!MONITORS[@]}"; do
CLIENT_NAME="${MONITORS[$MONITOR_ID]}"
# Fetch 30-day rolling uptime
UPTIME=$(curl -s "${BASE_URL}/monitors/${MONITOR_ID}" \
-H "Authorization: Bearer ${API_KEY}" \
| jq -r '.data.uptime_30d // "N/A"')
# Fetch current-month incident summary
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 "- Rolling 30-day uptime: ${UPTIME}%"
echo "- Incidents this month: ${INCIDENT_COUNT}"
echo "- Total downtime: ${DOWNTIME_MIN} min"
echo ""
done
Run this script via cron on the first of each month, pipe the output to a Markdown or HTML template, and send it via your mail service of choice (SendGrid, Postmark, SES, etc.). That completes the automation loop.
Pairing Reports with a Status Page
A monthly email report is valuable on its own, but pairing it with a client-facing status page compounds the effect. When clients want to know if their site is up right now, they can check the status page instead of emailing you. That reduces reactive support traffic and positions you as the kind of agency that provides transparent, professional tooling.
Miterl's status pages are linked to your monitors and publicly accessible. Share the URL with your client at onboarding, and ask them to bookmark it.
For a full breakdown of what status pages cost to run and how to price them into your retainer, see "Status Page Cost and ROI for Agencies: A Pricing Breakdown". The monthly report covered in this article is one layer of a larger client portal that also includes a live status page and incident notices — the full three-layer design is covered in "Client Portal for Monitoring Data: Agency Reporting Guide".
Using Reports to Justify and Grow Retainer Fees
The strategic purpose of a monthly uptime report is to remind clients what they are paying for. In months with no incidents, "we maintained 99.99% uptime" is not a non-event — it is proof that the monitoring and maintenance infrastructure worked. Without a report, that month looks like nothing happened. With a report, it looks like a quiet, stable operation delivered on schedule.
Clients who cancel maintenance contracts most often cite a vague sense of not seeing value. A report converts that vague unease into a concrete record. Once a client has twelve months of uptime data in their inbox, your retainer becomes something they understand, not something they question.
For the broader business case for including monitoring in every maintenance contract, "Why Agencies Should Include Monitoring in Contracts" covers positioning, pricing, and common objections.
If you want to formalize uptime commitments with SLA targets in your contracts, "Automating SLA Reports for Maintenance Subscriptions" extends the approach above with calendar-month accuracy calculations and automated delivery. Once automation is in place, the natural next question is which numbers to actually put in front of clients. "5 Monitoring KPIs Web Agencies Should Report to Clients" answers that directly — it covers the five metrics (uptime, response time, SSL expiry, MTTR, and mail auth status) that translate monitoring work into language clients understand and value. If your client roster is heavily WordPress-based, it is worth pairing uptime data with response time trends in your reports — WordPress-specific slowdowns from WP-Cron accumulation or plugin updates often go undetected by uptime checks alone. "WordPress Speed Monitoring for Web Agencies" explains how to set up that layer and present the findings to clients.
What to Include in a Client Uptime Report
A minimal but effective report structure:
| Field | Example |
|---|---|
| Reporting period | June 1–30, 2026 |
| Monitored URL | https://example-client.com |
| Uptime (30-day rolling) | 99.97% |
| Incidents this month | 1 |
| Total downtime | 9 min |
| Status page URL | https://status.example-client.com |
| Upcoming maintenance | WordPress core update scheduled |
Keep it simple. The goal is not to impress clients with complexity — it is to give them a number they can feel good about. Charts and detailed logs add polish, but the core value is the uptime percentage and incident count.
Frequently Asked Questions
What's the difference between this and "Automating SLA Reports"?
This guide covers automated uptime reports for general client transparency — a monthly "here's what we did" email that any maintenance retainer can use, regardless of whether the contract has a formal SLA. "Automating SLA Reports for Maintenance Subscriptions" builds on the same API calls but adds the extra step needed when a contract has a numeric SLA target: registering planned maintenance windows via the Webhook API so scheduled deployments are excluded from the downtime calculation, then checking the result against common thresholds (99.9%, 99.95%, 99.99%) with the uptime percentage calculator. If your contracts don't reference a specific uptime percentage, start here; if they do, use the SLA-focused version instead.
Can I automate delivery, or do I have to send reports manually?
If you're on the Standard plan or above, yes: create a client group under Dashboard → Client Reports, add the monitors and recipient emails, and enable auto-delivery — Miterl generates and emails the PDF automatically on the 1st of each month, no script required. The API-based approach in this guide is for cases the built-in PDF doesn't cover: the Free plan, custom formatting or branding, or folding uptime data into a report layout you control. In that case, schedule the shell script above with cron and pipe its output to your own email delivery step (SendGrid, Postmark, SES, or similar) — the script already automates data collection and formatting, leaving only the mail send step to wire up.
Summary
Automating monthly uptime reports delivers three compounding benefits for agencies:
- Time savings: Eliminate the month-end data collection grind
- Trust building: Deliver a consistent, numbers-backed proof of work each month
- Revenue protection: Make the value of your retainer undeniable, reducing cancellations and supporting fee increases
Start with one or two clients. Pull their monitor data using the script above, format it into a simple email, and send it manually first to see how clients respond. Once the format feels right, automate the delivery and roll it out across your entire roster.