Skip to main content

Authentication

Most Haltless API endpoints authenticate with a bearer JWT access token. Telemetry ingestion authenticates with an API key instead — see Sensor Data and the API Keys guide.

Send the access token on every authenticated request:

Authorization: Bearer <jwt>

Access tokens are short-lived. A long-lived refresh token is returned as a secure, httpOnly cookie by `/api/v1/auth/login` and is used by `/api/v1/auth/refresh` to mint a new access token.

Endpoints

MethodPathDescription
POST/api/v1/auth/loginExchange email and password for an access token
POST/api/v1/auth/refreshGet a fresh access token from the refresh cookie
GET/api/v1/auth/meReturn the authenticated user's profile
POST/api/v1/auth/logoutRevoke the current session
POST/api/v1/auth/mfa/verifyComplete an MFA challenge for a full token
POST/api/v1/auth/forgot-passwordEmail a password-reset link
POST/api/v1/auth/reset-passwordSet a new password from a reset token
GET/api/v1/auth/sso/checkCheck whether an email domain uses SSO
POST/api/v1/auth/sso/exchangeExchange an SSO code for tokens

Log in

POST /api/v1/auth/login

Request body

{
"email": "user@example.com",
"password": "<password>"
}

Response200 OK

{
"access_token": "<jwt>",
"token_type": "bearer",
"mfa_required": false,
"password_expired": false
}

When the account has multi-factor authentication enabled, the response instead returns a short-lived pending token with mfa_required set to true. Complete the challenge at `/api/v1/auth/mfa/verify` to receive a full access token.

Refresh an access token

POST /api/v1/auth/refresh

Sends the httpOnly refresh cookie set at login; no request body is required.

Response200 OK

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

Complete an MFA challenge

POST /api/v1/auth/mfa/verify

Request body

{
"mfa_token": "<pending-token>",
"code": "123456"
}

The code accepts a six-digit time-based one-time code or a single-use recovery code.

Response200 OK

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

Get the current user

GET /api/v1/auth/me

Requires a bearer JWT. Returns the authenticated user's profile.

Response200 OK

{
"id": "3f2504e0-4f89-41d3-9a0c-0305e82c3301",
"email": "user@example.com",
"full_name": "Jordan Rivera",
"role": "operator",
"mfa_enabled": true,
"timezone": "UTC",
"locale": "en",
"last_login_at": "2026-08-07T09:12:00Z"
}

Log out

POST /api/v1/auth/logout

Requires a bearer JWT. Revokes the current access and refresh tokens and clears the refresh cookie. Returns 204 No Content.

Password reset

POST /api/v1/auth/forgot-password emails a time-limited reset link to the address if it matches a user. It always returns 204 No Content, so it never reveals whether an email is registered.

{
"email": "user@example.com"
}

POST /api/v1/auth/reset-password consumes the emailed token and sets a new password:

{
"token": "<reset-token>",
"new_password": "<new-password>"
}

Returns 204 No Content on success.

Single sign-on

If your organization uses SSO, check a login email's domain before showing a password field:

GET /api/v1/auth/sso/check?email=user@example.com

Response200 OK

{
"has_sso": true,
"provider_name": "Example IdP",
"redirect_url": "https://idp.example.com/authorize?..."
}

Redirect the user to redirect_url. After the identity provider authenticates the user, your login page receives a short-lived code that you exchange for tokens:

POST /api/v1/auth/sso/exchange

{
"code": "<sso-code>"
}

Response200 OK

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

See the Authentication getting-started guide for a full walkthrough.