Skip to content

Error catalogue

Every error response has the same shape:

json
{
  "error": {
    "code": "FORBIDDEN_CAPABILITY",
    "message": "Metric 'paygap.gender_median_gap' requires capability 'people.view_paygap'",
    "detail": { "capability": "people.view_paygap" }
  }
}

detail is optional. Codes are stable — once published, never repurposed.

CodeStatusWhen it fires
UNAUTHENTICATED401No session cookie and no Authorization header, OR the API key is unknown / revoked / malformed
METRIC_NOT_FOUND404The metric id does not exist in the registry. Check spelling and the reference
FORBIDDEN_CAPABILITY403Authenticated, but the caller lacks the required capability. detail.capability names the capability — request it on the API key, or use a session with the appropriate role
PERIOD_KIND_MISMATCH400Snapshot period (today) sent to a range metric, or a range period sent to a snapshot metric. Check periodKind on the metric metadata
INVALID_PERIOD400?period= couldn't be parsed. Misspelled key, malformed range, or empty string
INVALID_AS_OF400?asOf= is not a valid ISO date, or it's in the future
INVALID_REQUEST400Generic validation failure — malformed JSON body in a batch, missing required fields, oversize batch (>50 queries), duplicate keys
INTERNAL_ERROR500The resolver threw an unexpected error. File a bug — these should never happen in production

Recovery

CodeWhat to do
UNAUTHENTICATEDRe-mint the key or sign in again
METRIC_NOT_FOUNDLook it up in /api/v1/metrics
FORBIDDEN_CAPABILITYTick the capability box when minting a new key, or sign in as an admin/owner
PERIOD_KIND_MISMATCHRead the metric's periodKind field and use the right kind of period
INVALID_PERIOD / INVALID_AS_OFThe error message includes the offending input — fix the format
INVALID_REQUESTRead the message — it names the specific field at fault
INTERNAL_ERRORRetry once with backoff. If it persists, file a bug

Batch

In POST /api/v1/metrics/batch, errors come in two layers:

  • Top-level errors — malformed body, oversize batch, future asOf, etc. Returned as a normal envelope with INVALID_REQUEST / INVALID_AS_OF and an HTTP 400.
  • Per-query errors — a query references an unknown metric or hits a capability that the caller doesn't have. The whole batch still returns 200 OK, with that query's slot containing { ok: false, error: { code, message } }. Sibling queries are unaffected.

This is intentional: a dashboard with one denied panel should still render the other nine.

OneBusiness · Built for businesses that work.