Skip to main content
Source: equa-server/modules/auth/src/responses.ts (authErrorKeys, lines 32-45)

Error Codes

The Equa API uses standard HTTP status codes to indicate the outcome of requests. Error responses include a JSON body with a human-readable message.

Error Response Format

{
  "error": "Description of the error"
}
Some validation errors from vineyard-lawn may include additional detail:
{
  "error": "Bad Request",
  "message": "Invalid value for field 'email'"
}

Status Codes

200 OK

The request succeeded. The response body contains the requested data.

201 Created

A new resource was successfully created (used on some POST endpoints).

400 Bad Request

The request body is malformed, missing required fields, or contains invalid values. Check the request schema and ensure all required parameters are provided. Common causes:
  • Missing required fields in the request body
  • Invalid UUID format for path parameters
  • Invalid email format
  • Validation errors from vineyard-lawn request schemas

401 Unauthorized

The request lacks a valid session. This occurs when:
  • No session cookie is present
  • The session has expired (sessions last ~42 minutes with rolling renewal, configurable via API_SESSION_MAX_AGE)
  • The session was invalidated (e.g., after logout)
Resolution: Re-authenticate via one of the login endpoints.

403 Forbidden

The authenticated user does not have permission to perform this action. The Equa API uses role-based access control (RBAC) through vineyard-lawn’s requires declarations. Common causes:
  • Attempting to edit a cap table without canEditCapTable permission
  • Accessing organization data without being a member
  • Attempting admin operations without site-level admin privileges
  • Performing billing operations without canEditOrganizationBilling permission
Resolution: Verify the user has the required role/permission within the organization. Organization admins can adjust member permissions.

404 Not Found

The requested resource does not exist or the URL path is incorrect. Common causes:
  • Invalid resource UUID
  • Accessing a deleted resource
  • Incorrect API path

409 Conflict

The request conflicts with the current state of the resource. Common causes:
  • Creating a resource that already exists (e.g., duplicate email address)
  • Attempting a state transition that is not allowed
  • Concurrent modification conflicts

500 Internal Server Error

An unexpected error occurred on the server. These errors are logged server-side. Resolution: Retry the request. If the error persists, contact support with the request details and timestamp.

Application Error Keys

In addition to HTTP status codes, authentication-related errors include an application-specific error key in the response body. These keys are defined in modules/auth/src/responses.ts as authErrorKeys and thrown via BadRequest(message, key).
{
  "error": "Bad Request",
  "key": "invalidCredentials"
}
KeyWire ValueHTTP StatusDescriptionTriggering Endpoint(s)Client Handling
emailUnavailableemailUnavailable400Email address is already registeredPOST /v1/user (registration)Show “email already in use” message
emailNotVerifiedemailNotVerified400Login attempted with an unverified emailPOST /v1/user/loginPrompt user to verify their email
existingActiveTempPasswordexistingActiveTempPassword400A temporary password is already activePOST /v1/user/password/resetInform user a reset email was already sent
invalidCredentialsinvalidCredentials400Wrong email or passwordPOST /v1/user/loginShow generic “invalid credentials” message
invalidEmailVerificationCodeinvalidEmailVerificationCode400Verification code is invalid or expiredPOST /v1/user/email/verifyPrompt user to request a new code
invalidTwoFactorTokeninvalidTwoFactorToken400Wrong 2FA TOTP tokenPOST /v1/user/2fa/verify, POST /v1/user/2faPrompt user to re-enter code
UnauthorizedUnauthorized400Generic authentication failureVarious auth endpointsRedirect to login
recentEmailVerificationrecentEmailVerification400Verification email sent too recently (cooldown)POST /v1/user/email/verify/sendShow cooldown timer (default 1800s)
userNotFounduserNotFound400No user found for the given identifierPOST /v1/user/password/resetShow generic “if account exists” message
emailBlacklistedEmailBlacklisted400Email address is on the blocklistPOST /v1/user (registration)Show “email not allowed” message
domainBlacklistedDomainBlacklisted400Email domain is on the blocklistPOST /v1/user (registration)Show “email domain not allowed” message
ipLimitipLimitReached400Too many registrations from this IP addressPOST /v1/user (registration)Show rate limit message, suggest trying later
The key field in the error key name (left column) is the object property name in TypeScript. The Wire Value column shows the actual string sent in the API response key field. For most keys they match, but emailBlacklisted sends "EmailBlacklisted", domainBlacklisted sends "DomainBlacklisted", and ipLimit sends "ipLimitReached".

Server Error Types (from Confluence KnowledgeBase)

Source: Confluence KnowledgeBase — Server Error Types (by Christopher Johnson)
Beyond HTTP status codes and auth error keys, the server defines these application-level error types:
Error KeyDescription
databaseMissingDatabase connection lost or database does not exist
resourceNotFoundAttempting to access a resource that does not exist
unauthorizedUser lacks authentication for the requested resource
invalidFieldsRequired field missing in the request
unsupportedFileExtensionUploaded file type is not supported
missingFileArgumentFile parameter is required but was not provided
insufficientSharesOperation requires more shares than are available

Handling Errors

const response = await fetch('https://api.equa.cc/v1/organization', {
  credentials: 'include',
})

if (!response.ok) {
  const error = await response.json()

  switch (response.status) {
    case 401:
      // Redirect to login
      window.location.href = '/login'
      break
    case 403:
      console.error('Permission denied:', error.error)
      break
    case 400:
      console.error('Validation error:', error.error)
      break
    default:
      console.error('API error:', response.status, error.error)
  }
}