Errors

The complete, closed set of error codes Selyf's API can return, what each one means, and what to do about it.

Response shape

Every error response from every route in the API Reference has this exact shape: { error: { code, message, requestId, details? } }. code is always one of the values below, message is a fixed, generic public message (never internal detail: no stack traces, no query fragments, no upstream provider text), requestId can be included when reporting a persistent error, and details only appears for VALIDATION_FAILED and only ever names which fields failed, never their values.

AUTH_REQUIRED

HTTP 401

Caller is not authenticated.

When it occurs
A request to an endpoint that requires a session was made with no valid session cookie.
What to do
Send the user through GET /auth/google to sign in, then retry.

FORBIDDEN

HTTP 403

Authenticated, but not allowed to touch this resource.

When it occurs
For example, a missing or invalid status-admin token on POST/PATCH /api/status/incidents.
What to do
Verify the caller has the correct credential for that specific route -- this is not a session problem, retrying with the same credential will not help.

CSRF_FAILED

HTTP 403

A state-changing request was missing or failed CSRF validation.

When it occurs
The X-CSRF-Token header is missing or does not match the session, or the Origin header is not the configured web origin -- on disconnect, account deletion, sync, analyze, or draft.
What to do
Fetch a fresh token from GET /auth/session and retry with it in the X-CSRF-Token header.

OAUTH_FAILED

HTTP 400

The OAuth authorization flow failed or was tampered with.

When it occurs
Google's token exchange or userinfo request failed, or the callback's state parameter did not match the one issued.
What to do
Restart sign-in from GET /auth/google -- this is not retryable with the same authorization code.

TOKEN_EXPIRED

HTTP 401

Stored OAuth credentials are expired or were revoked upstream.

When it occurs
Google has invalidated the stored refresh token outside of this application (e.g. the user revoked access from their Google Account settings).
What to do
Prompt the user to reconnect their Google account.

VALIDATION_FAILED

HTTP 400

Request payload, query, or path parameters failed schema validation.

When it occurs
For example, limit/offset out of range on GET /api/mail/threads, a malformed thread id, or an account-deletion body that is not exactly { "confirm": true }.
What to do
Read the response’s details.issues field for which fields failed, fix them, and resubmit.

NOT_FOUND

HTTP 404

The resource does not exist, or the caller may not know that it exists.

When it occurs
A thread id that does not belong to the authenticated account, or a route that does not exist.
What to do
Verify the id or path. Retrying the same request will not succeed.

RATE_LIMITED

HTTP 429

Caller exceeded this application's own rate limits.

When it occurs
The global or a per-route rate limit was tripped, or a sync was already in progress for the account.
What to do
Wait the number of seconds in the Retry-After response header before retrying.

GMAIL_RATE_LIMITED

HTTP 429

The Gmail API itself refused the request due to quota or rate limits.

When it occurs
A sync call hit Gmail’s own rate limit, not this application’s.
What to do
Wait and retry the sync later.

AI_RATE_LIMITED

HTTP 429

The configured AI provider refused the request due to quota or rate limits.

When it occurs
The AI provider itself (Anthropic or Gemini) rate-limited an analyze or draft call.
What to do
Wait and retry later.

AI_BUDGET_EXCEEDED

HTTP 429

The account's configured AI token budget is exhausted.

When it occurs
This account's daily or monthly AI_DAILY_TOKEN_BUDGET/AI_MONTHLY_TOKEN_BUDGET has been reached.
What to do
Wait for the daily or monthly budget window to reset -- an immediate retry will not succeed.

AI_REQUEST_FAILED

HTTP 502

The AI provider request failed for a reason other than rate limiting.

When it occurs
The configured AI provider returned an error or an unreadable response.
What to do
Retry later. If it persists, treat it as a temporary AI-provider outage.

SYNC_FAILED

HTTP 502

Mailbox synchronization failed.

When it occurs
The Gmail API request during POST /api/mail/sync failed for a reason other than Gmail's own rate limiting.
What to do
Retry the sync later.

SERVICE_UNAVAILABLE

HTTP 503

A dependency (database, AI provider, Google OAuth) is unavailable or unconfigured.

When it occurs
The database is unreachable or not configured, no AI provider is configured, or Google OAuth is not configured, for the route being called.
What to do
Retry later. This reflects deployment/configuration state, not a mistake in the request.

INTERNAL_ERROR

HTTP 500

Catch-all for an unexpected server error. Never carries internal detail to the client.

When it occurs
Anything not already one of the codes above.
What to do
Retry later. If it persists, note the response’s requestId when reporting it.