SLA Credit + Comms Kit: Copyable Configs for Make/n8n, Notion Ledger, and Status Page APIs
A copyable template bundle to calculate Monthly Uptime %, apply vendor-style credit ladders, write a per‑client ledger in Notion/CSV, and open draft incidents via Statuspage/Instatus APIs with templated comms. Built for solo operators automating SLA math and updates without hiring a comms lead.
How to use this template:
- Duplicate the sections you need into your own docs/automation notes.
- Replace every [BRACKETED] placeholder with your details.
- Choose either Make or n8n specs (both included). Start with a sandbox/status-page test page.
- Ship in 3 passes: (1) ingest logs + compute uptime, (2) write to ledger + email monthly summary, (3) gated incident drafts via API.
- Keep auto-publish OFF until you’re confident; default to draft + human approve.
Config: Service + Measurement Defaults
Define one or more services you’ll measure and credit.
service_id: [SERVICE_SLUG]
service_name: [SERVICE NAME]
currency: [USD|EUR|...]
monthly_billable_amount: [AMOUNT]
measurement_window_minutes: [5] # use 5m windows unless your SLA defines differently
exclusions:
- scheduled_maintenance: [true|false]
- outages_under_minutes: [5] # ignore continuous outages below this duration if allowed by SLA
- third_party_dependencies: [LIST OR false]
proof_sources:
- [LOG_SOURCE_1: e.g., CloudWatch metrics path]
- [LOG_SOURCE_2: e.g., Postgres audit table]
- [Monitoring: e.g., UptimeRobot monitor ID]
Template: SLA Credit Ladders (ready-to-copy examples)
Copy one as a starting point, then adapt to your own SLA. Keep a version log of changes.
Twilio APIs (example based on 2026 SLA):
credit_ladder:
vendor: Twilio APIs
threshold_kind: availability_threshold
monthly_threshold_percent: 99.95 # 99.99% for certain enterprise editions
brackets:
- when_actual_below_threshold: true
credit_percent: 10
notes:
- Exclude outages <5 continuous minutes and scheduled/emergency maintenance if applicable.
- Claim window: within [30 DAYS] after end of month. Confirm current SLA.
Amazon S3 (example based on 2023 SLA):
credit_ladder:
vendor: Amazon S3 (Standard & most classes)
threshold_kind: uptime_brackets
brackets:
- range: "<99.9% and ≥99.0%"
credit_percent: 10
- range: "<99.0% and ≥95.0%"
credit_percent: 25
- range: "<95.0%"
credit_percent: 100
notes:
- Monthly Uptime Percentage typically computed from 5‑minute intervals.
- Claim window: by end of the [SECOND BILLING CYCLE] following the incident month. Confirm current SLA.
Atlassian Cloud (example based on 2024 SLA):
credit_ladder:
vendor: Atlassian Cloud
threshold_kind: uptime_brackets
brackets:
- range: "<99.9% and ≥99.0%"
credit_percent: 10
- range: "<99.0% and ≥95.0%"
credit_percent: 25
- range: "<95.0%"
credit_percent: 50
notes:
- Applies to eligible products; confirm scope.
- Claim submission + timing requirements apply. Confirm current SLA.
Add your own:
credit_ladder:
vendor: [YOUR SLA NAME]
threshold_kind: [availability_threshold|uptime_brackets]
monthly_threshold_percent: [OPTIONAL]
brackets:
- range: ["<X% and ≥Y%" | when_actual_below_threshold: true]
credit_percent: [N]
Template: SLA Credit Ledger (Notion/CSV schema)
Use this schema for either a Notion database or a CSV you append monthly. Suggested fields:
- Client (text): [CLIENT_NAME]
- Service (select): [SERVICE_NAME]
- Month (date, any day in month): [YYYY‑MM‑01]
- Uptime % (number, 2 dp): [CALCULATED]
- Credit % (number): [FROM LADDER]
- Billable Amount (number): [MONTHLY_BILL]
- Credit Amount (formula/number): Billable Amount × Credit % ÷ 100
- Claim By (date):
- Twilio: [MONTH_END + 30 days]
- AWS S3: [MONTH_END + 2 billing cycles]
- Atlassian: [PER SLA]
- Claim Channel (select): [PORTAL|TICKET|EMAIL]
- Status (select): [To Review|Eligible|Filed|Approved|Denied]
- Evidence URLs (multi-text): links to dashboards/log exports
- Notes (rich text)
Notion formula hints (optional):
- Credit Amount:
round(prop("Billable Amount") * prop("Credit %") / 100, 2) - Static Claim By dates: set via your automation (recommended) rather than complex Notion formulas.
Template: Uptime Input → Calculator Payload
What your webhook or aggregator should output before applying the credit ladder.
Expected payload (per client-service-month):
{
"client_id": "[CLIENT_ID]",
"service_id": "[SERVICE_SLUG]",
"month": "[YYYY-MM]",
"window_minutes": [5],
"total_windows": [N],
"error_windows": [M],
"excluded_windows": [K],
"calculation_notes": ["Excluded scheduled maintenance on 2026-05-12 02:00–02:30 UTC"],
"billable_amount": [AMOUNT],
"evidence": ["[URL_TO_DASHBOARD]", "[URL_TO_LOG_EXPORT]"]
}
Monthly Uptime % formula:
monthly_uptime_percent = 100 * (total_windows - error_windows - excluded_windows) / (total_windows - excluded_windows)
Template: Monitoring → Webhook (UptimeRobot or similar)
Wire monitors to your webhook, but gate publication.
- Create/attach a Webhook alert contact to your monitor(s). Point to:
[YOUR_PUBLIC_WEBHOOK_URL]. - Parse payload fields such as
monitorID,monitorURL,alertType,alertDetails, andalertDuration. - Multi-signal guard (recommended): only create a draft incident when at least 2 of 3 are true for ≥[5] minutes:
- External monitor down
- Error-rate spike in first‑party logs
- Synthetic healthcheck failing
- Auto-snooze rule: if recovery <[8] minutes, keep as draft and auto-resolve silently.
Example webhook intake normalization (pseudo):
{
"source": "uptimerobot",
"monitor_id": "[ID]",
"event": "[UP|DOWN]",
"started_at": "[ISO8601]",
"duration_secs": [N],
"region": "[REGION]"
}
Make Scenario Spec: Calculator → Ledger → Draft Incident
Modules and mapping you can recreate 1:1 in Make.
- HTTP webhook (Custom webhook)
- Respond 200 OK quickly; push payload to queue/variable.
- Iterator/Array aggregator
- Collect all log windows for [MONTH].
- Tools > Function (JS)
const total = inputs.total_windows;
const errors = inputs.error_windows;
const excl = inputs.excluded_windows || 0;
const uptime = 100 * (total - errors - excl) / (total - excl);
return { monthly_uptime_percent: +uptime.toFixed(4) };
- Router: Apply Credit Ladder
- Tools > Switch based on [VENDOR_CONFIG]
- Set
credit_percentper bracket or threshold.
- Notion > Create Database Item (or CSV > Add row)
- Map: Client, Service, Month, Uptime %, Credit %, Billable Amount, Credit Amount (pre-calc), Evidence URLs, Status=“To Review”, Claim By (see next step).
- Tools > Function (JS) — Claim Deadlines
const monthEnd = new Date(Date.UTC(Y, M+1, 0));
return {
twilio_claim_by: new Date(monthEnd.getTime() + 30*24*3600*1000).toISOString(),
aws_claim_by: new Date(Date.UTC(Y, M+3, 1)).toISOString() // approx: end of 2nd billing cycle; refine per your billing cycle
};
- HTTP > Make a request — Statuspage (Draft Incident)
- Method: POST
- URL:
https://api.statuspage.io/v1/pages/[PAGE_ID]/incidents - Headers:
Authorization: OAuth [API_KEY],Content-Type: application/json - Body:
{
"incident": {
"name": "[SERVICE_NAME] disruption — [SHORT_CAUSE]",
"status": "investigating",
"impact_override": "[none|minor|major|critical]",
"body": "[INCIDENT_MESSAGE_TEMPLATE_RENDERED]",
"deliver_notifications": false
}
}
- HTTP > Make a request — Instatus (Draft Incident)
- Method: POST
- URL:
https://api.instatus.com/v1/[PAGE_ID]/incidents - Headers:
Authorization: Bearer [API_KEY],Content-Type: application/json - Body:
{
"name": "[SERVICE_NAME] disruption — [SHORT_CAUSE]",
"message": "[INCIDENT_MESSAGE_TEMPLATE_RENDERED]",
"status": "investigating",
"shouldPublish": false,
"notify": false,
"components": [{"id": "[COMPONENT_ID]", "status": "degraded"}]
}
- Delay + Reminder
- Schedule next-update reminder at
[NEXT_UPDATE_AT]to Slack/Email: “Post update or flip to Resolved.”
- Monthly Summary Email
- Aggregate ledger rows; email yourself: credits due + claim-by dates + evidence links.
n8n Workflow Spec: Nodes + Code Snippets
Nodes and minimal code you can paste.
- Webhook (POST /sla-calc)
- Respond
200 OKimmediately; send payload to next.
- Function (Compute Uptime)
const t = $json.total_windows;
const e = $json.error_windows;
const x = $json.excluded_windows || 0;
const pct = 100 * (t - e - x) / (t - x);
return { monthly_uptime_percent: +pct.toFixed(4) };
- IF (Pick Ladder)
- Conditions against
monthly_uptime_percentusing your [CREDIT_LADDER]. - Set
credit_percentaccordingly.
- HTTP Request (Statuspage draft) [optional]
- POST
https://api.statuspage.io/v1/pages/[PAGE_ID]/incidents - Headers:
Authorization: OAuth [API_KEY],Content-Type: application/json - JSON body: see Statuspage payload in Make section.
- HTTP Request (Instatus draft) [optional]
- POST
https://api.instatus.com/v1/[PAGE_ID]/incidents - Headers:
Authorization: Bearer [API_KEY] - JSON body: see Instatus payload in Make section.
- Notion (Create Page) or Google Sheets (Append Row)
- Map ledger fields exactly as in the Ledger section.
- Wait Till (Next Update)
- Use
DateTimefrom template to schedule a reminder.
- Email/Slack (Monthly Summary)
- Subject:
[CLIENT] — [MONTH] SLA Credits & Deadlines - Body: dynamic list of credits + claim-by dates.
Incident Comms: Templated Messages + Approval Gate
Default to drafts. Always include a promised next update.
Investigating (public status page update template)
[START TIME, TZ]: We’re investigating elevated errors affecting [SCOPE/PRODUCT].
Impact: [X% of requests|[N] customers] may see [SYMPTOM].
Current mitigation: [WHAT YOU’RE DOING].
Next update by: [NEXT_UPDATE_AT, TZ].
Reference: [STATUS_PAGE_URL or TICKET #].
Identified (public status page update template)
[CURRENT TIME, TZ]: We’ve identified a cause impacting [SCOPE/PRODUCT].
Mitigation: [ROLLBACK|SCALE UP|DISABLE FEATURE FLAG].
Customer guidance: [WORKAROUND if any].
Next update by: [NEXT_UPDATE_AT, TZ] or “Resolved” if metrics return to baseline.
Resolved (public status page resolution template)
[RESOLVE TIME, TZ]: Service has recovered. Root cause: [CAUSE].
Impact window: [START–END, TZ].
Follow-ups: [POSTMORTEM LINK if applicable].
Internal approval checklist (pre-publish)
- Draft states key fields: scope, impact, mitigation, next update
- Links to evidence dashboards are included
- Impact classification matches components
Template: Monthly Credit Summary Email
Send this to yourself monthly (or auto-generate in automation) so you never miss filing windows.
Subject: [CLIENT] — [YYYY‑MM] SLA Credits, Evidence, and Claim Deadlines
Body:
- Service: [SERVICE_NAME]
- Uptime %: [XX.XXXX%]
- Credit %: [N%]
- Credit Amount: [CURRENCY][AMOUNT]
- Claim by: [DATE]
- Claim channel: [PORTAL/TICKET/EMAIL LINK]
- Evidence: [DASHBOARD URL], [LOG EXPORT URL]
- Notes: [MEASUREMENT WINDOWS, EXCLUSIONS APPLIED]
Footer reminders:
- Twilio: claim within [30 DAYS] of month end (confirm current SLA).
- AWS S3: claim by end of [SECOND BILLING CYCLE] after the incident month (confirm current SLA).
- Atlassian: follow product-specific claim timing (confirm current SLA).
API Payload Templates: Statuspage & Instatus (Draft-first)
Use these to render incident drafts. Replace placeholders and sanitize content before publishing.
Statuspage — Create Incident (draft)
POST https://api.statuspage.io/v1/pages/[PAGE_ID]/incidents
Authorization: OAuth [API_KEY]
Content-Type: application/json
{
"incident": {
"name": "[SERVICE_NAME] disruption — [SHORT_CAUSE]",
"status": "investigating",
"impact_override": "[none|minor|major|critical]",
"body": "[RENDERED_MESSAGE]",
"component_ids": ["[COMPONENT_ID]"],
"deliver_notifications": false
}
}
Statuspage — Update Incident
PATCH https://api.statuspage.io/v1/pages/[PAGE_ID]/incidents/[INCIDENT_ID]
Authorization: OAuth [API_KEY]
Content-Type: application/json
{
"incident": {
"status": "[identified|monitoring|resolved]",
"body": "[RENDERED_MESSAGE]",
"deliver_notifications": true
}
}
Instatus — Create Incident (draft)
POST https://api.instatus.com/v1/[PAGE_ID]/incidents
Authorization: Bearer [API_KEY]
Content-Type: application/json
{
"name": "[SERVICE_NAME] disruption — [SHORT_CAUSE]",
"message": "[RENDERED_MESSAGE]",
"status": "investigating",
"shouldPublish": false,
"notify": false,
"components": [{"id": "[COMPONENT_ID]", "status": "degraded"}]
}
Instatus — Update Incident
PUT https://api.instatus.com/v1/[PAGE_ID]/incidents/[INCIDENT_ID]
Authorization: Bearer [API_KEY]
Content-Type: application/json
{
"status": "[identified|monitoring|resolved]",
"message": "[RENDERED_MESSAGE]",
"notify": true,
"shouldPublish": true
}
Guardrails: Noise, Timing, and Over‑Crediting Controls
- Gate to draft until both are true: (a) outage persists ≥[5] minutes; (b) at least [2] corroborating signals agree.
- Auto-resolve if metrics return to baseline for <[8] minutes after start (don’t publish).
- Cap incident update cadence at every [30] minutes; always set “Next update by”.
- Never compute credits from raw ping downtime alone. Use first‑party error rates + exclusions to avoid over-crediting.
- Log every decision: inputs, formula used, ladder version, exclusions applied, and final credit %.