Skip to content

Auth API

Base path: /api/v1/auth

Handles all user identity concerns: self-service registration, password and Google OAuth login, password reset, team invitations, short-lived access token refresh, logout with full token blocklisting, profile management, and organisation member administration.

See API Reference for auth, errors, and pagination.


POST /register

Register the first user for a brand-new organisation. Creates the organisation on the trial plan (14-day window) and the user as its owner with the admin role. Issues access + refresh tokens immediately — no email verification required.

Auth: None · Any plan · Rate limit: 10/min

Body

Field Type Required Constraints
email string Yes Valid email
password string Yes 8–128 chars, at least one uppercase letter, at least one digit
name string Yes 1–255 chars, non-blank
org_name string Yes 2–255 chars, non-blank
department string No Max 255 chars
curl -s -c cookies.txt -X POST https://api.knora.io/api/v1/auth/register \
  -H "Content-Type: application/json" \
  -d '{
    "email": "alice@example.com",
    "password": "Secure123",
    "name": "Alice Smith",
    "org_name": "Acme Corp"
  }'

201 Created

{
  "access_token": "<jwt>",
  "token_type": "bearer",
  "user": {
    "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "email": "alice@example.com",
    "name": "Alice Smith",
    "role": "admin",
    "org_id": "f0e1d2c3-b4a5-6789-abcd-ef0987654321",
    "is_org_owner": true,
    "department": "Engineering",
    "is_active": true,
    "created_at": "2026-05-31T10:00:00Z",
    "updated_at": "2026-05-31T10:00:00Z",
    "org": {
      "id": "f0e1d2c3-b4a5-6789-abcd-ef0987654321",
      "name": "Acme Corp",
      "slug": "acme-corp",
      "plan": "trial",
      "trial_ends_at": "2026-06-14T10:00:00Z",
      "is_active": true
    }
  },
  "org": {
    "id": "f0e1d2c3-b4a5-6789-abcd-ef0987654321",
    "name": "Acme Corp",
    "slug": "acme-corp",
    "plan": "trial",
    "trial_ends_at": "2026-06-14T10:00:00Z",
    "is_active": true
  }
}

Sets Set-Cookie: knora_refresh_token=<jwt>; HttpOnly; Secure; SameSite=Strict; Path=/.

Errors

Status Code Cause
409 REGISTRATION_FAILED Email address is already registered
422 VALIDATION_ERROR Missing or invalid fields
429 RATE_LIMITED Too many requests from this IP

POST /login

Authenticate with email and password credentials. Issues access + refresh tokens.

Auth: None · Any plan · Rate limit: 10/min

Body

Field Type Required
email string Yes
password string Yes
curl -s -c cookies.txt -X POST https://api.knora.io/api/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email": "alice@example.com", "password": "Secure123"}'

200 OK — same shape as POST /register.

Sets Set-Cookie: knora_refresh_token=....

Errors

Status Code Cause
401 INVALID_CREDENTIALS Wrong email/password, or account is inactive
422 VALIDATION_ERROR Missing or invalid fields
429 RATE_LIMITED Too many requests from this IP

POST /google

Google OAuth login and signup. The frontend completes the Google Sign-In flow to obtain a Google ID token, then passes it here. The backend verifies it with Google's servers.

Three flows: (1) existing Google-linked account — log in; (2) email already registered but no Google link — link the Google identity and log in; (3) brand-new user — create a new organisation and account (org_name required).

Auth: None · Any plan · Rate limit: 10/min

Body

Field Type Required Description
id_token string Yes Google ID token from the frontend OAuth flow
org_name string Conditional Required only when creating a brand-new account. Max 255 chars.
curl -s -c cookies.txt -X POST https://api.knora.io/api/v1/auth/google \
  -H "Content-Type: application/json" \
  -d '{"id_token": "<google-id-token>"}'

200 OK — same shape as POST /register.

Sets Set-Cookie: knora_refresh_token=....

Errors

Status Code Cause
400 GOOGLE_OAUTH_DISABLED GOOGLE_CLIENT_ID is not configured on the server
400 ORG_NAME_REQUIRED New Google user but org_name was not provided
401 INVALID_GOOGLE_TOKEN Token failed Google verification
401 EMAIL_NOT_VERIFIED The Google account's email is not verified
401 ACCOUNT_DEACTIVATED The matched Knora account has been deactivated
401 GOOGLE_ACCOUNT_CONFLICT Email already linked to a different Google identity
422 VALIDATION_ERROR Missing or invalid fields
429 RATE_LIMITED Too many requests from this IP

POST /forgot-password

Initiate a password reset for the given email address. Generates a signed reset token (1-hour TTL), stores it in Redis, and dispatches a reset email. Always returns 200 regardless of whether the account exists — prevents user enumeration.

Auth: None · Any plan · Rate limit: 5/min

Body

Field Type Required
email string Yes
curl -s -X POST https://api.knora.io/api/v1/auth/forgot-password \
  -H "Content-Type: application/json" \
  -d '{"email": "alice@example.com"}'

200 OK

{
  "message": "If an account with that email exists, a reset link has been sent."
}

Errors

Status Code Cause
422 VALIDATION_ERROR Invalid email format
429 RATE_LIMITED Too many requests from this IP

POST /reset-password

Complete a password reset using the token from the reset email.

Auth: None · Any plan · Rate limit: 5/min

Body

Field Type Required Constraints
token string Yes Signed reset token from the email link
password string Yes 8–128 chars, at least one uppercase letter, at least one digit
curl -s -X POST https://api.knora.io/api/v1/auth/reset-password \
  -H "Content-Type: application/json" \
  -d '{"token": "<reset-token>", "password": "NewSecure456"}'

200 OK

{
  "message": "Your password has been reset successfully."
}

Errors

Status Code Cause
400 INVALID_RESET_TOKEN Token is invalid, expired, or already used
422 VALIDATION_ERROR Missing or invalid fields
429 RATE_LIMITED Too many requests from this IP

POST /accept-invite

Accept a team invitation by setting a password and activating the account. The invite token arrives via the email link sent by POST /invite and expires after 48 hours.

Auth: None · Any plan · Rate limit: 10/min

Body

Field Type Required Constraints
token string Yes Signed invite token from the email link
password string Yes 8–128 chars, at least one uppercase letter, at least one digit
curl -s -c cookies.txt -X POST https://api.knora.io/api/v1/auth/accept-invite \
  -H "Content-Type: application/json" \
  -d '{"token": "<invite-token>", "password": "NewSecure456"}'

200 OK — same shape as POST /register. Issues access + refresh tokens immediately.

Sets Set-Cookie: knora_refresh_token=....

Errors

Status Code Cause
400 INVALID_INVITE_TOKEN Token is invalid, expired, or already used
400 INVITE_ALREADY_ACCEPTED The invitation was previously accepted
422 VALIDATION_ERROR Missing or invalid fields
429 RATE_LIMITED Too many requests from this IP

POST /refresh

Issue a fresh access token using the knora_refresh_token httpOnly cookie. The browser sends the cookie automatically when credentials: 'include' is set. No body required.

Auth: Valid knora_refresh_token cookie (refresh JWT) · Rate limit: 20/min

curl -s -b cookies.txt -X POST https://api.knora.io/api/v1/auth/refresh

200 OK

{
  "access_token": "<new-jwt>",
  "token_type": "bearer"
}

Errors

Status Code Cause
401 AUTHENTICATION_FAILED Cookie absent, expired, or token blocklisted
401 ACCOUNT_DEACTIVATED User account was deactivated since token was issued
401 ORG_DEACTIVATED Organisation was deactivated since token was issued
401 ORG_NOT_FOUND Organisation was deleted since token was issued
429 RATE_LIMITED Too many requests from this IP

POST /logout

Logout the current user. Blocklists both the access token JTI and the refresh token JTI in Redis so neither can be reused before natural expiry. Clears the refresh cookie.

Auth: JWT · Any plan

curl -s -b cookies.txt -X POST https://api.knora.io/api/v1/auth/logout \
  -H "Authorization: Bearer <access_token>"

200 OK

{
  "message": "Successfully logged out."
}

Clears Set-Cookie: knora_refresh_token=; Max-Age=0; ....

Errors

Status Code Cause
401 AUTHENTICATION_FAILED Access token missing, expired, or already blocklisted

GET /me

Return the authenticated user's profile.

Auth: JWT · Any plan

curl -s -X GET https://api.knora.io/api/v1/auth/me \
  -H "Authorization: Bearer <access_token>"

200 OK

{
  "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "email": "alice@example.com",
  "name": "Alice Smith",
  "role": "admin",
  "org_id": "f0e1d2c3-b4a5-6789-abcd-ef0987654321",
  "is_org_owner": true,
  "department": "Engineering",
  "is_active": true,
  "created_at": "2026-05-31T10:00:00Z",
  "updated_at": "2026-05-31T10:00:00Z",
  "org": {
    "id": "f0e1d2c3-b4a5-6789-abcd-ef0987654321",
    "name": "Acme Corp",
    "slug": "acme-corp",
    "plan": "trial",
    "trial_ends_at": "2026-06-14T10:00:00Z",
    "is_active": true
  }
}

Errors

Status Code Cause
401 AUTHENTICATION_FAILED Token missing or invalid
401 ACCOUNT_DEACTIVATED Account was deactivated after token was issued
404 USER_NOT_FOUND User record deleted after token was issued

PUT /me

Update the authenticated user's profile. Only fields included in the body are modified. Send "department": null to clear it; omit to leave it unchanged. name cannot be cleared.

Auth: JWT · Any plan

Body (all fields optional)

Field Type Constraints
name string 1–255 chars, non-blank
department string | null Max 255 chars; null clears the field
curl -s -X PUT https://api.knora.io/api/v1/auth/me \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{"name": "Alice J. Smith"}'

200 OK — updated user object, same shape as GET /me.

Errors

Status Code Cause
401 AUTHENTICATION_FAILED Token missing or invalid
401 ACCOUNT_DEACTIVATED Account was deactivated after token was issued
422 VALIDATION_ERROR Field value violates constraints

POST /invite

Invite a new user into the caller's organisation. Creates an inactive account, generates a signed invite token (48-hour TTL) stored in Redis, and dispatches an invite email. The invitee completes signup via POST /accept-invite.

Auth: JWT · admin or manager · Any plan · Rate limit: 20/min

Body

Field Type Required Constraints
email string Yes Valid email
name string Yes 1–255 chars, non-blank
role string No admin, manager, or member (default: member)
department string No Max 255 chars
curl -s -X POST https://api.knora.io/api/v1/auth/invite \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{"email": "bob@example.com", "name": "Bob Jones", "role": "member"}'

201 CreatedUserResponse for the newly created (inactive) user. No tokens.

{
  "id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
  "email": "bob@example.com",
  "name": "Bob Jones",
  "role": "member",
  "org_id": "f0e1d2c3-b4a5-6789-abcd-ef0987654321",
  "is_org_owner": false,
  "department": null,
  "is_active": false,
  "created_at": "2026-06-01T09:00:00Z",
  "updated_at": "2026-06-01T09:00:00Z",
  "org": null
}

Errors

Status Code Cause
400 USER_LIMIT_EXCEEDED Organisation has reached its plan's user seat limit
401 AUTHENTICATION_FAILED Token missing or invalid
403 INSUFFICIENT_ROLE Caller does not have admin or manager role
409 EMAIL_ALREADY_EXISTS Email is already registered
422 VALIDATION_ERROR Invalid field value (e.g. unknown role)
429 RATE_LIMITED Too many requests

GET /members

List all active members of the caller's organisation.

Auth: JWT · Any plan

curl -s -X GET https://api.knora.io/api/v1/auth/members \
  -H "Authorization: Bearer <access_token>"

200 OK

{
  "members": [
    {
      "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "email": "alice@example.com",
      "name": "Alice Smith",
      "role": "admin",
      "org_id": "f0e1d2c3-b4a5-6789-abcd-ef0987654321",
      "is_org_owner": true,
      "department": "Engineering",
      "is_active": true,
      "created_at": "2026-05-31T10:00:00Z",
      "updated_at": "2026-05-31T10:00:00Z",
      "org": null
    }
  ],
  "total": 1
}

Returns active members only. No pagination — all members returned in a single response.

Errors

Status Code Cause
401 AUTHENTICATION_FAILED Token missing or invalid
401 ACCOUNT_DEACTIVATED Caller's account was deactivated

PATCH /members/:user_id

Change the role of a member within the caller's organisation. The caller cannot change their own role. The org owner's role cannot be changed.

Auth: JWT · admin · Any plan

Path parameter: user_id — UUID of the target member.

Body

Field Type Required Constraints
role string Yes admin, manager, or member
curl -s -X PATCH https://api.knora.io/api/v1/auth/members/b2c3d4e5-f6a7-8901-bcde-f12345678901 \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{"role": "manager"}'

200 OK — updated UserResponse for the target member.

Errors

Status Code Cause
400 CANNOT_CHANGE_OWN_ROLE Caller tried to change their own role
400 CANNOT_CHANGE_OWNER_ROLE Target is the organisation owner
401 AUTHENTICATION_FAILED Token missing or invalid
403 INSUFFICIENT_ROLE Caller does not have admin role
404 USER_NOT_FOUND Target user not found or belongs to a different org
422 VALIDATION_ERROR role value is not one of the allowed values

DELETE /members/:user_id

Deactivate (soft-delete) a member from the caller's organisation. The user record is retained but marked is_active = false, excluding them from all member listings and blocking authentication. The caller cannot remove themselves.

Auth: JWT · admin · Any plan

Path parameter: user_id — UUID of the member to remove.

curl -s -X DELETE https://api.knora.io/api/v1/auth/members/b2c3d4e5-f6a7-8901-bcde-f12345678901 \
  -H "Authorization: Bearer <access_token>"

200 OK

{
  "message": "Member removed successfully."
}

Errors

Status Code Cause
400 CANNOT_REMOVE_SELF Caller attempted to remove themselves
401 AUTHENTICATION_FAILED Token missing or invalid
403 INSUFFICIENT_ROLE Caller does not have admin role
404 USER_NOT_FOUND Target user not found or belongs to a different org