Skip to main content
Archie Auth exposes its full surface as REST endpoints on the platform gateway. Use them when you’d rather call HTTP directly than go through GraphQL — anywhere a curl or a thin HTTP client is the right tool. For the equivalent GraphQL operations, see the GraphQL API reference.

Required headers

HeaderRequiredDefaultDescription
X-Project-IdYesYour project identifier.
environmentNomasterTarget environment name.
Content-TypeYesapplication/json.
AuthorizationProtected endpoints onlyBearer <accessToken>.
Base URL: https://your-gateway.example.com

Public endpoints

These don’t require authentication — they’re the way unauthenticated visitors enter the system.

POST /auth/signup

Register a new user account. Request:
{
  "email": "user@example.com",
  "password": "SecureP@ss1",
  "firstName": "John",
  "lastName": "Doe",
  "roleId": "optional-role-id"
}
Responses:
StatusBodyWhen
201{ "userId": "uuid", "message": "Verification email sent" }Created.
400{ "error": "VALIDATION_ERROR", "violations": [...] }Invalid input or weak password.
409{ "error": "AUTH_EMAIL_EXISTS" }Email already registered.
404{ "error": "AUTH_NOT_CONFIGURED" }Auth not enabled for this project + environment.
429{ "error": "RATE_LIMIT_EXCEEDED" }Too many signups (10/min per IP).
If email verification is disabled, the user is auto-verified and can log in immediately.

POST /auth/login

Authenticate and receive an access/refresh token pair. Request:
{
  "email": "user@example.com",
  "password": "SecureP@ss1"
}
Successful response (200):
{
  "accessToken": "eyJhbGciOiJSUzI1NiIs...",
  "refreshToken": "base64-encoded-token...",
  "user": {
    "id": "a1b2c3d4-e5f6-...",
    "email": "user@example.com",
    "firstName": "John",
    "lastName": "Doe",
    "roles": ["Member"]
  }
}
Error responses:
StatusBodyWhen
401{ "error": "AUTH_INVALID_CREDENTIALS" }Wrong email or password.
403{ "error": "AUTH_EMAIL_NOT_VERIFIED" }Email not yet confirmed.
423{ "error": "AUTH_ACCOUNT_LOCKED", "lockedUntil": "..." }Locked from failed attempts.
429{ "error": "RATE_LIMIT_EXCEEDED" }Too many logins (20/min per IP).
When JWE encryption is enabled, the accessToken is a 5-part JWE string instead of a 3-part JWS. Both formats are accepted on inbound requests.

POST /auth/confirm-signup

Confirm an email with the 6-digit code. Request:
{
  "email": "user@example.com",
  "code": "123456"
}
Responses:
StatusBodyWhen
200{ "accessToken": "...", "refreshToken": "...", "user": {...} }Confirmed and logged in.
400{ "error": "AUTH_INVALID_CODE" }Code wrong, expired, or attempt limit hit.
Codes expire after 1 hour. Up to 5 attempts are allowed before the code is invalidated.

POST /auth/recover-password

Request a password recovery email. Always returns 200, regardless of whether the email exists, to prevent enumeration. Request:
{
  "email": "user@example.com"
}
Response (always 200):
{
  "message": "If account exists, recovery email sent"
}
Rate-limited to 5 / minute per email.

POST /auth/reset-password

Reset a password using the recovery code. Request:
{
  "email": "user@example.com",
  "newPassword": "NewSecureP@ss2",
  "code": "654321"
}
Responses:
StatusBodyWhen
200{ "message": "Password reset successfully" }Updated.
400{ "error": "AUTH_INVALID_CODE" }Code wrong or expired.
A successful reset clears any active account lockout.

POST /auth/refresh-token

Exchange a refresh token for a new access/refresh pair. Request:
{
  "refreshToken": "base64-encoded-token..."
}
Responses:
StatusBodyWhen
200{ "accessToken": "...", "refreshToken": "..." }New token pair.
401{ "error": "AUTH_TOKEN_INVALID" }Invalid or already-used refresh token.
Refresh tokens rotate on every use. The previous refresh token is invalidated; store the new one from the response. Reusing an old refresh token returns AUTH_TOKEN_INVALID and triggers a session-wide invalidation as a token-theft signal.

GET /auth/.well-known/jwks.json

Public JWKS endpoint for external services validating Archie-issued tokens. No authentication required. Response (200):
{
  "keys": [
    {
      "kty": "RSA",
      "kid": "abc12345",
      "use": "sig",
      "alg": "RS256",
      "n": "...",
      "e": "AQAB"
    }
  ]
}
When JWE encryption is enabled, two keys are returned — one for signing and one for encryption:
{
  "keys": [
    {
      "kty": "RSA", "kid": "abc12345", "use": "sig", "alg": "RS256", "n": "...", "e": "AQAB"
    },
    {
      "kty": "RSA", "kid": "enc-def67890", "use": "enc", "alg": "RSA-OAEP-256", "n": "...", "e": "AQAB"
    }
  ]
}
The response is cached in-memory for 5 minutes. External services should refresh on kid mismatch.

Protected endpoints

Require Authorization: Bearer <accessToken>.

POST /auth/logout

Revoke the current access token and invalidate the associated refresh token.
curl -X POST https://your-gateway.example.com/auth/logout \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..." \
  -H "X-Project-Id: your-project-id" \
  -H "environment: master"
Responses:
StatusBodyWhen
200{ "message": "Logged out successfully" }Token revoked.
401{ "error": "AUTH_TOKEN_INVALID" }Missing or invalid token.
Revoked tokens are tracked in a distributed blacklist until their natural expiry, so re-presenting the access token after logout fails with 401.

Error codes reference

CodeHTTPMeaning
AUTH_EMAIL_EXISTS409Email already registered.
AUTH_INVALID_CREDENTIALS401Wrong email or password.
AUTH_EMAIL_NOT_VERIFIED403Email not yet confirmed.
AUTH_ACCOUNT_LOCKED423Too many failed attempts; account temporarily locked.
AUTH_INVALID_CODE400Verification or recovery code wrong, expired, or out of attempts.
AUTH_TOKEN_EXPIRED401Access token has expired.
AUTH_TOKEN_INVALID401Token malformed, revoked, or tampered.
AUTH_NOT_CONFIGURED404Auth not enabled for this project + environment.
RATE_LIMIT_EXCEEDED429Too many requests; honor Retry-After.
VALIDATION_ERROR400Request body validation failed (see violations).

Rate limits

EndpointLimitScope
POST /auth/signup10 / minutePer IP
POST /auth/login20 / minutePer IP
POST /auth/recover-password5 / minutePer email
429 responses include a Retry-After header indicating seconds to wait.

FAQ

To prevent email enumeration. If the response distinguished “exists” from “doesn’t exist”, an attacker could probe a list of emails and learn which are registered. Returning 200 either way removes that signal.
Fetch the JWKS from /auth/.well-known/jwks.json and validate signatures locally. Cache the JWKS on your edge service; refresh on kid mismatch (which signals key rotation). Don’t call Archie on every request to validate.
Most likely a revocation. Archie checks a distributed token blacklist on every request, so logged-out and force-logged-out tokens fail even if the signature is valid. The local-only validation can’t see the blacklist.
Surface a clear “account temporarily locked” message and either show the countdown from lockedUntil or offer a password reset link — a successful reset clears the lockout.
Put a Custom API in front of /auth/login with a tighter rate-limit policy. The auth endpoint defaults are platform-wide; per-route Custom API config lets you go stricter.