SLA credits are the financial mechanism for compensating customers when service level commitments aren't met. Their design — how they're triggered, calculated, applied, and communicated — determines whether they function as a trust-building mechanism or a source of customer frustration. Getting them right matters for both customer relationships and business sustainability.
How SLA Credits Work
At their core, SLA credits follow a simple structure:
- A service level commitment is defined (e.g., 99.9% monthly availability)
- Actual performance is measured against the commitment
- If performance falls below the commitment, a credit is calculated based on the credit schedule
- The credit is applied to the customer's next invoice or paid out directly
The complexity comes from the definitions — what counts as downtime, how credits are calculated, who initiates the claim, and how disputes are resolved.
Credit Schedule Design
Credit schedules should be progressive — more severe breaches result in larger credits:
## Example Credit Schedule Structure
### Tier 1: Minor Breach
- Threshold: 99.0% - 99.9% (below SLA target of 99.9%)
- Credit: 10% of monthly service fees
- Practical meaning: 43 minutes to 7 hours of downtime per month
### Tier 2: Significant Breach
- Threshold: 95.0% - 99.0%
- Credit: 25% of monthly service fees
- Practical meaning: 7 hours to 36 hours of downtime per month
### Tier 3: Severe Breach
- Threshold: Below 95.0%
- Credit: 50% of monthly service fees
- Practical meaning: More than 36 hours of downtime per month
### Maximums
- Maximum credit in any calendar month: 50% of monthly fee
- Credits do not accumulate across months
- Credits are the sole remedy for availability failures
(unless contract specifies additional remedies)
Calculating Credits Correctly
from dataclasses import dataclass
from typing import Optional
@dataclass
class CreditTier:
min_availability_pct: float
max_availability_pct: float # Exclusive upper bound (= SLA target)
credit_pct: float
@dataclass
class SLAContract:
customer_id: str
customer_name: str
sla_target_pct: float # e.g., 99.9
monthly_fee: float
credit_schedule: list # List of CreditTier
class SLACreditCalculator:
def calculate_monthly_credit(
self,
contract: SLAContract,
actual_availability_pct: float
) -> dict:
"""
Calculate SLA credit for a given month's actual availability.
"""
# No credit if SLA was met
if actual_availability_pct >= contract.sla_target_pct:
return {
"credit_amount": 0,
"credit_pct": 0,
"reason": "SLA met",
"actual_availability": actual_availability_pct,
"sla_target": contract.sla_target_pct
}
# Find applicable credit tier
applicable_tier = None
for tier in sorted(contract.credit_schedule, key=lambda t: t.credit_pct, reverse=True):
if actual_availability_pct >= tier.min_availability_pct:
applicable_tier = tier
break
if applicable_tier is None:
# Below all defined tiers — use highest credit
applicable_tier = max(contract.credit_schedule, key=lambda t: t.credit_pct)
credit_amount = contract.monthly_fee * (applicable_tier.credit_pct / 100)
# Apply maximum cap (typically 50%)
max_credit = contract.monthly_fee * 0.50
credit_amount = min(credit_amount, max_credit)
return {
"customer_id": contract.customer_id,
"customer_name": contract.customer_name,
"sla_target_pct": contract.sla_target_pct,
"actual_availability_pct": round(actual_availability_pct, 4),
"monthly_fee": contract.monthly_fee,
"credit_pct": applicable_tier.credit_pct,
"credit_amount": round(credit_amount, 2),
"credit_capped": credit_amount == max_credit,
"credit_tier": f"{applicable_tier.min_availability_pct}% - {contract.sla_target_pct}%"
}
def process_all_credits_for_month(self, year: int, month: int) -> list:
"""
Calculate credits for all enterprise customers for a given month.
Run this on the 1st of each month for the previous month.
"""
results = []
for contract in self.get_enterprise_contracts():
# Get actual availability from monitoring data
actual_availability = self.monitoring.get_monthly_availability(
customer_id=contract.customer_id,
year=year,
month=month
)
credit = self.calculate_monthly_credit(contract, actual_availability)
results.append(credit)
if credit["credit_amount"] > 0:
# Apply credit to billing system
self.billing.apply_credit(
customer_id=contract.customer_id,
amount=credit["credit_amount"],
description=f"SLA credit for {year}-{month:02d}: {actual_availability:.4f}% availability",
reference=f"sla-credit-{year}-{month:02d}"
)
# Notify customer
self.notify_customer_of_credit(contract, credit, year, month)
return results
Proactive vs Reactive Credit Application
The way credits are initiated has a significant impact on customer perception:
| Approach | Customer Experience | Trust Impact | |---|---|---| | Customer must request credits | Customers who don't know to ask don't get credits; creates adversarial relationship | Negative | | Provider applies credits when customer complains | Reactive; customers feel they had to fight for compensation | Neutral | | Provider automatically calculates and applies credits | Customer receives credits without asking; shows confidence in commitments | Positive | | Provider notifies customer proactively before next invoice | Best practice; customer sees credit before seeing invoice | Strongly positive |
Recommendation: Automate credit calculation and application. Notify customers proactively. This signals that you take your commitments seriously.
Credit Communication Template
When applying an SLA credit, communicate proactively:
Subject: SLA Credit Applied to Your Account — [Month]
[First Name],
Your service experienced a brief disruption on [date] that resulted in
[downtime duration] of unavailability. Your actual monthly availability
was [X]%, which fell below our committed [SLA target]%.
We've automatically applied a service credit of [credit amount] to
your account. This credit will appear on your [next month] invoice.
**What happened:**
[2-3 sentence plain-language explanation]
**What we've done to prevent recurrence:**
[Specific action items]
**Your account details:**
- Monthly service fee: $[amount]
- SLA commitment: [X]% availability
- Actual availability: [X]%
- Credit amount: $[amount] ([X]% of monthly fee)
For a detailed incident report, please visit [link].
We apologize for the disruption and appreciate your continued trust.
[Name]
Customer Success
Handling Disputed Credits
When customers dispute credit calculations:
class CreditDisputeHandler:
"""
Handle SLA credit disputes with a clear, documented process.
"""
def process_dispute(self, customer_id, disputed_month, customer_claim):
"""
Compare customer's claimed downtime vs provider's measured downtime.
"""
# Get provider's monitoring data
provider_data = self.monitoring.get_monthly_data(customer_id, disputed_month)
# Get customer's submitted evidence
customer_data = customer_claim.get("monitoring_data")
dispute_analysis = {
"provider_availability_pct": provider_data.availability_pct,
"customer_claimed_availability_pct": customer_claim.get("claimed_availability"),
"discrepancy_pct": abs(
provider_data.availability_pct -
(customer_claim.get("claimed_availability") or 0)
)
}
# Common reasons for discrepancy
potential_causes = []
if dispute_analysis["discrepancy_pct"] > 0.5:
potential_causes.append("Significant discrepancy — investigate measurement methodology")
if customer_data and customer_data.get("measurement_location") == "customer_network":
potential_causes.append("Customer measurement includes their own network latency/failures")
if customer_data and customer_data.get("includes_planned_maintenance"):
potential_causes.append("Customer measurement may include excluded maintenance windows")
return {
"dispute_analysis": dispute_analysis,
"potential_causes": potential_causes,
"resolution_path": self.determine_resolution_path(dispute_analysis),
"documentation": {
"provider_monitoring_logs": provider_data.raw_checks,
"contract_measurement_definition": self.get_contract_measurement_clause(customer_id),
"applicable_exclusions": self.get_applicable_exclusions(customer_id, disputed_month)
}
}
def determine_resolution_path(self, dispute_analysis):
"""
Determine the appropriate resolution for a credit dispute.
"""
discrepancy = dispute_analysis["discrepancy_pct"]
if discrepancy < 0.1:
return "accept_customer_claim" # Small discrepancy, customer benefit of doubt
elif discrepancy < 0.5:
return "negotiate_middle_ground" # Use average of both measurements
else:
return "detailed_reconciliation_required" # Need detailed log comparison
Credit Cap Considerations
Maximum credit caps protect business viability but should be set thoughtfully:
## Credit Cap Design Considerations
### Standard Practice: 50% Monthly Fee Cap
Rationale:
- Preserves business viability during severe outages
- Still provides meaningful compensation
- Customers can pursue other remedies (cancellation, legal) for catastrophic failures
### Enterprise Negotiation: Higher Caps
Some enterprise customers negotiate higher caps:
- 100% of monthly fee (common for critical business systems)
- Multiple months of credit (for sustained outages)
- Direct refund rather than credit (for financial impact)
### What Caps Don't Cover
Credits compensate for the fee paid — not for:
- Customer revenue lost during outage
- Cost of workarounds implemented
- Staff time spent on outage impact
These consequential damages are typically excluded by contract.
If a customer's business is significantly impacted, they may pursue
breach of warranty claims regardless of credit caps.
### Setting Appropriate Caps
The cap should be:
- High enough to be meaningful compensation
- Low enough to not create existential risk from a single customer's credits
- Consistent with the risk profile of the service
Tracking Credit Trends
Monitor credit issuance as a reliability health metric:
def analyze_credit_trends(credits, months=12):
"""
Analyze SLA credit issuance trends as a proxy for reliability health.
Increasing credits → decreasing reliability.
"""
monthly_totals = {}
for credit in credits:
month_key = credit["month"]
if month_key not in monthly_totals:
monthly_totals[month_key] = {
"total_credits_issued": 0,
"accounts_with_credits": 0,
"largest_single_credit": 0
}
monthly_totals[month_key]["total_credits_issued"] += credit["credit_amount"]
monthly_totals[month_key]["accounts_with_credits"] += 1
monthly_totals[month_key]["largest_single_credit"] = max(
monthly_totals[month_key]["largest_single_credit"],
credit["credit_amount"]
)
# Calculate trends
sorted_months = sorted(monthly_totals.keys())
recent = sorted_months[-3:] # Last 3 months
earlier = sorted_months[-6:-3] # 3 months before that
recent_avg = sum(monthly_totals[m]["total_credits_issued"] for m in recent) / len(recent)
earlier_avg = sum(monthly_totals[m]["total_credits_issued"] for m in earlier) / len(earlier) if earlier else 0
trend = "increasing" if recent_avg > earlier_avg * 1.1 else "stable" if recent_avg > earlier_avg * 0.9 else "improving"
return {
"monthly_data": monthly_totals,
"trend": trend,
"recent_3mo_avg_credits": round(recent_avg, 2),
"earlier_3mo_avg_credits": round(earlier_avg, 2),
"alert": trend == "increasing"
}
Conclusion
SLA credits work best as a trust mechanism, not just a financial liability. Providers who automate credit calculation, apply credits proactively, and communicate clearly about what happened and what's being fixed retain customers through SLA breaches far better than providers who make customers fight for compensation. The underlying requirement for all of this is accurate, independent monitoring data — you need reliable availability measurements to calculate credits correctly, defend against disputes, and build the credibility that makes proactive credit application feel genuine rather than strategic. AzMonitor provides the external monitoring record that serves as the objective availability baseline for these calculations.
3 monitors free forever · No credit card needed · Set up in 2 minutes
Start monitoring free →