Template

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, and alertDuration.
  • 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):

{
  &quot;source&quot;: &quot;uptimerobot&quot;,
  &quot;monitor_id&quot;: &quot;[ID]&quot;,
  &quot;event&quot;: &quot;[UP|DOWN]&quot;,
  &quot;started_at&quot;: &quot;[ISO8601]&quot;,
  &quot;duration_secs&quot;: [N],
  &quot;region&quot;: &quot;[REGION]&quot;
}

Make Scenario Spec: Calculator → Ledger → Draft Incident

Modules and mapping you can recreate 1:1 in Make.

  1. HTTP webhook (Custom webhook)
  • Respond 200 OK quickly; push payload to queue/variable.
  1. Iterator/Array aggregator
  • Collect all log windows for [MONTH].
  1. 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) };
  1. Router: Apply Credit Ladder
  • Tools > Switch based on [VENDOR_CONFIG]
  • Set credit_percent per bracket or threshold.
  1. 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).
  1. 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
};
  1. 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:
{
  &quot;incident&quot;: {
    &quot;name&quot;: &quot;[SERVICE_NAME] disruption — [SHORT_CAUSE]&quot;,
    &quot;status&quot;: &quot;investigating&quot;,
    &quot;impact_override&quot;: &quot;[none|minor|major|critical]&quot;,
    &quot;body&quot;: &quot;[INCIDENT_MESSAGE_TEMPLATE_RENDERED]&quot;,
    &quot;deliver_notifications&quot;: false
  }
}
  1. 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:
{
  &quot;name&quot;: &quot;[SERVICE_NAME] disruption — [SHORT_CAUSE]&quot;,
  &quot;message&quot;: &quot;[INCIDENT_MESSAGE_TEMPLATE_RENDERED]&quot;,
  &quot;status&quot;: &quot;investigating&quot;,
  &quot;shouldPublish&quot;: false,
  &quot;notify&quot;: false,
  &quot;components&quot;: [{&quot;id&quot;: &quot;[COMPONENT_ID]&quot;, &quot;status&quot;: &quot;degraded&quot;}]
}
  1. Delay + Reminder
  • Schedule next-update reminder at [NEXT_UPDATE_AT] to Slack/Email: “Post update or flip to Resolved.”
  1. 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.

  1. Webhook (POST /sla-calc)
  • Respond 200 OK immediately; send payload to next.
  1. 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) };
  1. IF (Pick Ladder)
  • Conditions against monthly_uptime_percent using your [CREDIT_LADDER].
  • Set credit_percent accordingly.
  1. 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.
  1. 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.
  1. Notion (Create Page) or Google Sheets (Append Row)
  • Map ledger fields exactly as in the Ledger section.
  1. Wait Till (Next Update)
  • Use DateTime from template to schedule a reminder.
  1. Email/Slack (Monthly Summary)
  • Subject: [CLIENT] — [MONTH] SLA Credits &amp; 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

{
  &quot;incident&quot;: {
    &quot;name&quot;: &quot;[SERVICE_NAME] disruption — [SHORT_CAUSE]&quot;,
    &quot;status&quot;: &quot;investigating&quot;,
    &quot;impact_override&quot;: &quot;[none|minor|major|critical]&quot;,
    &quot;body&quot;: &quot;[RENDERED_MESSAGE]&quot;,
    &quot;component_ids&quot;: [&quot;[COMPONENT_ID]&quot;],
    &quot;deliver_notifications&quot;: false
  }
}

Statuspage — Update Incident

PATCH https://api.statuspage.io/v1/pages/[PAGE_ID]/incidents/[INCIDENT_ID]
Authorization: OAuth [API_KEY]
Content-Type: application/json

{
  &quot;incident&quot;: {
    &quot;status&quot;: &quot;[identified|monitoring|resolved]&quot;,
    &quot;body&quot;: &quot;[RENDERED_MESSAGE]&quot;,
    &quot;deliver_notifications&quot;: true
  }
}

Instatus — Create Incident (draft)

POST https://api.instatus.com/v1/[PAGE_ID]/incidents
Authorization: Bearer [API_KEY]
Content-Type: application/json

{
  &quot;name&quot;: &quot;[SERVICE_NAME] disruption — [SHORT_CAUSE]&quot;,
  &quot;message&quot;: &quot;[RENDERED_MESSAGE]&quot;,
  &quot;status&quot;: &quot;investigating&quot;,
  &quot;shouldPublish&quot;: false,
  &quot;notify&quot;: false,
  &quot;components&quot;: [{&quot;id&quot;: &quot;[COMPONENT_ID]&quot;, &quot;status&quot;: &quot;degraded&quot;}]
}

Instatus — Update Incident

PUT https://api.instatus.com/v1/[PAGE_ID]/incidents/[INCIDENT_ID]
Authorization: Bearer [API_KEY]
Content-Type: application/json

{
  &quot;status&quot;: &quot;[identified|monitoring|resolved]&quot;,
  &quot;message&quot;: &quot;[RENDERED_MESSAGE]&quot;,
  &quot;notify&quot;: true,
  &quot;shouldPublish&quot;: 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 %.