Authentication

Cookie-based JWT sessions, refresh and logout.

Authentication is cookie based. Tokens are issued as HttpOnly cookies so client-side JavaScript never handles them.

How sessions work

FieldTypeDescription
accessstringHttpOnly cookie. 5 minute lifetime. Sent automatically with every request.
refreshstringHttpOnly cookie. 7 day lifetime. Exchanged for a new pair.
RotationstringRefresh tokens are rotated on use and the previous value is blacklisted.
SameSitestringLax, which blocks cross-site POST requests while allowing top-level navigation.

Sign up

POST/api/auth/signup/Public

Creates a user and immediately issues the session cookies.

Request
curl -X POST https://api.introzen.com/api/auth/signup/ \
  -H "Content-Type: application/json" \
  -c cookies.txt \
  -d '{"email": "owner@acme.com", "password": "<strong-password>"}'
201 Created
{
  "user": {
    "id": 12,
    "first_name": "",
    "last_name": "",
    "email": "owner@acme.com",
    "is_active": true,
    "is_staff": false,
    "date_joined": "2026-10-04T09:12:44.512Z"
  }
}

Sign in

POST/api/auth/login/Public

Validates credentials and sets a fresh pair of cookies.

Body

FieldTypeDescription
emailstringAccount email address.
passwordstringAccount password.

Refresh

POST/api/auth/token/refresh/Public

Reads the refresh cookie and issues a new access cookie. When rotation is enabled a new refresh cookie is issued as well.

Session status

GET/api/auth/session_status/Auth required

Tells the frontend where the user is in the onboarding funnel. This is what routes a new account to workspace setup instead of the dashboard.

status values

FieldTypeDescription
UNAUTHORIZEDstringNo valid session. Send the user to sign in. Answered as a 200, because it is one of this endpoint's answers rather than an error.
CREATE_TENANTstringAuthenticated but has no workspace yet.
SUBSCRIBE_PLANstringWorkspace exists but has no active plan.
ACCESS_GRANTEDstringFully onboarded. Load the dashboard.

Password recovery

Recovery is a two-step flow. The request step never reveals whether an address is registered, and the confirm step accepts a link only once.

POST/api/auth/password-reset/Public

Accepts an email address and always answers 200 with the same body. When the address belongs to an active account, a reset link is emailed to it.

Body

FieldTypeDescription
emailstringAddress that should receive the reset link.
POST/api/auth/password-reset/confirm/Public

Completes the reset. The new password is validated against the configured password policy, and every existing refresh token for the account is revoked.

Body

FieldTypeDescription
uidstringBase64 user identifier taken from the emailed link.
tokenstringSingle-use token taken from the emailed link.
passwordstringReplacement password. Minimum 8 characters, checked against the password validators.
Request a link, then use it
# 1. Ask for a reset link (always 200, even for unknown addresses)
curl -X POST https://api.introzen.com/api/auth/password-reset/ \
  -H "Content-Type: application/json" \
  -d '{"email": "owner@acme.com"}'

# 2. Exchange the uid and token from the email for a new password
curl -X POST https://api.introzen.com/api/auth/password-reset/confirm/ \
  -H "Content-Type: application/json" \
  -d '{"uid": "<uid>", "token": "<token>", "password": "<new-password>"}'

# 3. Sign in again: previous sessions and refresh tokens no longer work
curl -X POST https://api.introzen.com/api/auth/login/ \
  -H "Content-Type: application/json" \
  -c cookies.txt \
  -d '{"email": "owner@acme.com", "password": "<new-password>"}'

Sign out

POST/api/auth/logout/Auth required

Blacklists the refresh token and clears both cookies.

Last reviewed October 2026.