Skip to content

Error Contract

This document is FINAL and ENFORCEABLE for all services.


This document standardizes error responses across:

  • Auth
  • Core
  • Base / Module backends
  • Frontend expectations

401 → authentication problem
403 → authorization problem
503 → system failure

This must be applied consistently across all services.


Used when:

  • missing token
  • invalid token
  • expired token
  • wrong issuer / audience
  • token revoked
{
"success": false,
"error": {
"code": "unauthorized",
"message": "invalid or expired token"
}
}
  • attempt token refresh
  • if fails → logout

Used when:

  • user authenticated
  • BUT:
    • no membership
    • module not granted
    • permission missing
    • delegation denied
{
"success": false,
"error": {
"code": "forbidden",
"message": "missing permission: finance.expense.create"
}
}
  • show access denied
  • optionally refresh access state

Used when:

  • missing x-org
  • invalid x-org
  • malformed request
{
"success": false,
"error": {
"code": "validation_error",
"message": "missing x-org header"
}
}

Used when:

  • Auth unavailable
  • Core unavailable (via Auth)
  • access resolution fails
  • dependency timeout
{
"success": false,
"error": {
"code": "service_unavailable",
"message": "unable to resolve access"
}
}
  • show retry UI
  • DO NOT assume access

❌ DO NOT return 403 for invalid token
❌ DO NOT return 401 for missing permission
❌ DO NOT return 200 with hidden failure

If access cannot be determined:

→ return 503

❌ DO NOT allow request if Auth fails
❌ DO NOT allow request using stale cache

ScenarioStatus
Missing token401
Invalid token401
Expired token401
Missing x-org400
Invalid x-org400
No membership403
Module not granted403
Permission missing403
Delegation denied403
Auth down503
Core down (via Auth)503
Cache invalid and cannot rebuild503

Every error must log:

  • userId (if available)
  • companyId (if available)
  • endpoint
  • error code
  • reason
  • timestamp

Errors must be predictable, consistent, and never ambiguous.

401 = who are you?
403 = you cannot do this
503 = system cannot decide