Skip to main content

Authentication API

Base path: /auth

All auth endpoints are public (no JWT required) unless noted otherwise.

POST /auth/register

Create a new tenant and admin user.

POST /auth/register
Content-Type: application/json

Request Body:

{
"email": "admin@acme.com",
"password": "SecurePass123!",
"companyName": "Acme Corporation",
"firstName": "John",
"lastName": "Doe"
}

Response (201):

{
"accessToken": "eyJhbGciOiJIUzI1NiIs...",
"refreshToken": "eyJhbGciOiJIUzI1NiIs...",
"user": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "admin@acme.com",
"firstName": "John",
"lastName": "Doe",
"role": "admin",
"roleLevel": 100
},
"tenant": {
"id": "tenant-uuid",
"slug": "acme-corporation",
"companyName": "Acme Corporation"
}
}

Errors:

CodeDescription
400Invalid input (weak password, missing fields)
409Email or company name already exists

POST /auth/login

Authenticate and receive JWT tokens.

POST /auth/login
Content-Type: application/json

Request Body:

{
"tenantSlug": "acme-corporation",
"email": "admin@acme.com",
"password": "SecurePass123!"
}

Response (200):

{
"accessToken": "eyJhbGciOiJIUzI1NiIs...",
"refreshToken": "eyJhbGciOiJIUzI1NiIs...",
"user": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "admin@acme.com",
"firstName": "John",
"lastName": "Doe",
"role": "admin",
"roleLevel": 100,
"avatar": "/uploads/avatars/uuid.jpg",
"departmentId": "dept-uuid",
"teamIds": ["team-uuid-1"]
}
}

Errors:

CodeDescription
401Invalid credentials
404Tenant not found

GET /auth/me

Get current authenticated user info.

note

Requires JWT authentication.

GET /auth/me
Authorization: Bearer <accessToken>

Response (200):

{
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "admin@acme.com",
"firstName": "John",
"lastName": "Doe",
"role": "admin",
"roleLevel": 100,
"avatar": "/uploads/avatars/uuid.jpg",
"departmentId": "dept-uuid",
"teamIds": ["team-uuid-1"],
"permissions": { "leads": { "view": true, "create": true, "edit": true, "delete": true } },
"tenant": {
"slug": "acme-corporation",
"companyName": "Acme Corporation"
}
}

POST /auth/refresh

Exchange a refresh token for new access and refresh tokens.

POST /auth/refresh
Content-Type: application/json

Request Body:

{
"refreshToken": "eyJhbGciOiJIUzI1NiIs..."
}

Response (200):

{
"accessToken": "eyJhbGciOiJIUzI1NiIs...",
"refreshToken": "eyJhbGciOiJIUzI1NiIs..."
}

Errors:

CodeDescription
401Invalid or expired refresh token

GET /auth/invite/validate

Validate an invitation token before accepting.

GET /auth/invite/validate?token=abc123def456

Response (200):

{
"valid": true,
"email": "newuser@acme.com",
"tenantName": "Acme Corporation",
"invitedBy": "John Doe",
"roleName": "Sales Representative"
}

Errors:

CodeDescription
400Token expired or already used
404Invalid token

POST /auth/invite/accept

Accept an invitation and create a user account.

POST /auth/invite/accept
Content-Type: application/json

Request Body:

{
"token": "abc123def456",
"password": "SecurePass123!",
"firstName": "Jane",
"lastName": "Smith"
}

Response (201):

{
"accessToken": "eyJhbGciOiJIUzI1NiIs...",
"refreshToken": "eyJhbGciOiJIUzI1NiIs...",
"user": {
"id": "new-user-uuid",
"email": "newuser@acme.com",
"firstName": "Jane",
"lastName": "Smith",
"role": "user",
"roleLevel": 10
}
}

POST /auth/forgot-password

Request a password reset email.

POST /auth/forgot-password
Content-Type: application/json

Request Body:

{
"email": "admin@acme.com",
"tenantSlug": "acme-corporation"
}

Response (200):

{
"message": "If the email exists, a reset link has been sent."
}
Security

This endpoint always returns success regardless of whether the email exists, to prevent email enumeration attacks.

GET /auth/reset-password/validate

Validate a password reset token.

GET /auth/reset-password/validate?token=reset-token-123

Response (200):

{
"valid": true,
"email": "admin@acme.com"
}

POST /auth/reset-password

Reset password using a valid token.

POST /auth/reset-password
Content-Type: application/json

Request Body:

{
"token": "reset-token-123",
"newPassword": "NewSecurePass456!"
}

Response (200):

{
"message": "Password reset successfully"
}

POST /auth/change-password

Change password for the authenticated user.

note

Requires JWT authentication.

POST /auth/change-password
Authorization: Bearer <accessToken>
Content-Type: application/json

Request Body:

{
"currentPassword": "OldPassword123!",
"newPassword": "NewPassword456!"
}

Response (200):

{
"message": "Password changed successfully"
}

Errors:

CodeDescription
400New password does not meet requirements
401Current password is incorrect