跳到主要内容

Errors

Four error shapes are in use, and one group of endpoints reports failure inside a 200 response. A caller must handle all five.

Shapes

ShapeBodyUsed by
General{"error":"<message>"}Sessions, people, notifications, calendar, status
Qualified{"error":"<message>","reason":"<value>"}Approval and account status refusals
OAuth{"error":"<code>"}, sometimes with error_descriptionThe /auth/oidc endpoints
HTML pageA rendered page, not JSON/auth/bridge errors, and the OIDC callback refusal
Success-shaped200 with a false flag in the bodyPOST /auth/verify, /location, /calendar

The error message is intended for a developer. It is not stable and must not be matched on. Branch on the status code, and on reason where one is present.

Status codes

StatusMeaningRetry
200Handled. Check the body before assuming successNot applicable
204Preflight answeredNot applicable
302Redirect in a browser flowFollow it
400Request is malformed, or a required field is missingOnly after correcting the request
401Credential absent, invalid or expiredAfter obtaining a new credential
403Credential is valid, the account may not do thisNo. Escalate to an administrator
404No such record, or no such providerNo
409The change conflicts with an existing linkNo. Resolve the conflict first
429Rate limit reached on an email code endpointAfter the interval stated in the message
500The request failed inside the service or an upstreamOnce, then escalate
502An upstream returned an unusable answerOnce, then escalate
503A dependency is unavailable, or a provider is disabledYes, with backoff

Distinguishing 401 from 403

StatusInterpretationCorrect handling
401The service does not know who is callingSign in again
403The service knows, and refusesDo not retry. Do not sign the person out
503 with Authorization unavailableThe account could not be looked upRetry. Do not sign the person out

A caller that treats every refusal as a sign-in failure loops a person through sign-in indefinitely when their account is pending.

Approval refusals

POST /auth/verify answers 200 when the token is valid and the account is not permitted.

reasonMeaning
pendingThe account exists and has not been reviewed
rejectedThe account has been refused
no_project_accessApproved, but without a grant for the requested project
phone_not_linkedReturned with 403. The number is on no account

Gated endpoints answer 403 with the same vocabulary in reason.

OAuth error codes

CodeCause
invalid_clientThe client is not registered, or the secret does not match
invalid_requestA required parameter is missing, or the redirect URI is not registered
unsupported_response_typeAnything other than code
unsupported_grant_typeAnything other than authorization_code
invalid_grantThe code is unknown, expired, already used, bound to another client, or the redirect URI or PKCE verifier does not match
invalid_tokenThe access token is unknown or expired
server_errorToken signing failed

An invalid authorise request is answered to the caller. It is never delivered to the requested redirect URI.

Non-JSON errors

The bridge endpoints and the OIDC callback refusal render HTML. A client that sets Accept: application/json still receives HTML. Check the content type before parsing, or branch on the status code alone.

Silent failures

EndpointFailure presentation
GET /calendar/events{"events":[]} with 200, whether the week is empty or the calendar is unreachable
GET /calendar/upcoming-locations{"locations":[]} with 200
Every /location endpoint200 with a fallback body. See Location
PATCH /auth/people/:personId200 with failures listed in firebaseSyncErrors
POST /auth/otp/send for an unknown address{"ok":true} with 200, and no mail sent
POST /auth/zitadel/claims when the lookup fails200 with the urn:akn: claims absent

None of these raise a status code a caller can trap. Inspect the body.

Cross-origin failures

A browser request from an origin outside the allowlist receives no allow-origin header, so the browser rejects the response before the caller sees it. This presents as a network error, not as a 401 or 403. The request itself reached the service and may have taken effect.