Skip to content

Errors & authentication ​

This page covers structured API errors, correlation ids, and how customers authenticate to Exhale. Webhook ingest uses per-source verification — HMAC for PagerDuty and manual webhooks; URL/header tokens for most other sources. See the Sources index and guides linked below.

A public route catalog and language SDKs ship after launch. Tenant API keys can be issued in the app under Settings → Security & data → API keys.


Structured errors ​

Non-success responses use a consistent JSON shape:

json
{
  "error": {
    "code": "NOT_FOUND",
    "message": "Pulse … not found",
    "correlation_id": "550e8400-e29b-41d4-a716-446655440000"
  }
}
FieldDescription
codeMachine-readable error code (e.g. NOT_FOUND, VALIDATION_ERROR, UNAUTHORIZED)
messageHuman-readable detail
correlation_idRequest id for support and log lookup

Every response includes an X-Correlation-Id header. Clients may send their own value; otherwise the API generates a UUID. Match this id in support requests.

HTTPTypical codeWhen
400BAD_REQUESTMissing required webhook fields, invalid Content-Length
401UNAUTHORIZEDBad or missing signature / session. Login and related auth flows may set a more specific error.code: invalid_credentials (unknown email or wrong password — same response either way), invalid_mfa_code, mfa_challenge_expired, invalid_password, invalid_reset_token
403FORBIDDENViewer role, disabled tenant, expired trial. Product flows may set a more specific error.code: trial_expired, subscription_canceled, subscription_past_due, entitlement_limit / entitlement_*
404NOT_FOUNDUnknown pulse, unmapped paging account, or unknown webhook token
409CONFLICTConflicting state (e.g. retry-triage race, billing conflict)
413PAYLOAD_TOO_LARGEBody over size limit (default 1 MiB)
422VALIDATION_ERRORSchema validation failure
429RATE_LIMITEDPer-IP webhook or API rate limit (Retry-After). Per-email sign-in lockout uses auth_locked
503(varies)Redis fail-closed, maintenance, ambiguous account mapping
500INTERNAL_ERRORUnexpected server error

Customer authentication ​

Customer UI and /auth routes use the exhale_session HTTP-only cookie after login or signup.

Mutating /auth routes require:

  • Valid session
  • X-CSRF-Token header matching the CSRF cookie (double-submit pattern)
  • Origin or Referer from an allowed browser UI origin

MFA (TOTP): Paid tenant owners and admins may be required to enroll in TOTP before full session access when admin MFA is required. Trial signups and optional member/viewer MFA policies are configured under Settings → Account → Sign-in & MFA. Owners set the tenant MFA policy (PATCH /auth/settings key security).

Social login (identity OAuth): When Google and/or GitHub sign-in is enabled, Sign in and Sign up show Continue with Google / Continue with GitHub. Those flows authenticate the person only — they do not connect repositories or APM accounts. Repo and observability context still use Connections → Context. OAuth errors return as ?auth_error= on login/signup. If the IdP email matches an existing Exhale account that does not already have that identity, the callback redirects to /link-oauth so the owner can confirm with password, an emailed link, or an existing session. MFA after social login uses the same TOTP enrollment/verify path as password login.

Source and notification setup in the app uses the signed-in session and CSRF.

Tenant API keys (customer) ​

Tenant owners issue keys under Settings → Security & data → API keys. The secret is shown once (exh_… prefix); only a hash is stored. Keys do not authenticate inbound webhooks. A public route catalog for those keys ships with language SDKs after launch.


Webhook authentication ​

Webhooks do not use session cookies. Each source has its own verification:

Auth styleSourcesHow
HMAC signaturePagerDuty, Manual webhookHMAC-SHA256 of the raw body as v1=<hex> (X-PagerDuty-Signature or X-Exhale-Signature). Manual also requires X-Exhale-Webhook-Token (header route) or a path token.
URL / header tokenJSM Operations (Opsgenie), Splunk On-Call, incident.io, Grafana, Datadog, Alertmanager, New Relic, Sentry, Honeycomb, Elastic, Dynatrace, AWS CloudWatchOpaque tenant token via header X-Exhale-Webhook-Token (preferred) or a token in the webhook path. No vendor HMAC.

Health ​

On the hosted app, the only public health probe is:

GET https://exhaleoncall.com/health/live

It checks process liveness and does not require authentication. It does not report database, Redis, or queue depth. GET /health on that host returns 404.


Exhale by Kolstrom Systems LLC