---
title: Portal Auth API
path: reference/api/portal-auth
status: published
---

# Portal Auth API

Reference for the `Portal Auth` endpoint group — 6 endpoints.

Generated from the live OpenAPI spec. Re-run `_generate_api_reference.py` after backend changes.

## Authentication

All endpoints require a Bearer JWT in the `Authorization` header unless noted otherwise. See [Concepts → Tokens and scopes](/docs/scaikey/concepts/tokens-and-scopes) and [Reference → OAuth endpoints](/docs/scaikey/reference/oauth-endpoints) for how to obtain one.

## Endpoints

### **POST** `/api/v1/auth/authenticate`

_Authenticate User_

Step 2 of login: Verify password and issue tokens.

Requires a valid flow_token from the identify step.
If user belongs to multiple tenants, tenant_id must be specified.

**Request body:**

Required.

- `application/json` → [`AuthenticateRequest`](#schema-authenticaterequest)

**Responses:**

| Status | Body |
|---|---|
| `200` | `application/json` → [`AuthenticateResponse`](#schema-authenticateresponse) |
| `422` | `application/json` → [`HTTPValidationError`](#schema-httpvalidationerror) |

---

### **POST** `/api/v1/auth/identify`

_Identify User_

Step 1 of login: Identify user and return available tenants.

This endpoint is designed to be safe against enumeration attacks:
- Always returns a flow token (even for non-existent users)
- Response format is identical for existing and non-existing users
- Uses constant-time operations where possible

**Request body:**

Required.

- `application/json` → [`IdentifyRequest`](#schema-identifyrequest)

**Responses:**

| Status | Body |
|---|---|
| `200` | `application/json` → [`IdentifyResponse`](#schema-identifyresponse) |
| `422` | `application/json` → [`HTTPValidationError`](#schema-httpvalidationerror) |

---

### **POST** `/api/v1/auth/login`

_Portal Login_

Simple one-step login (backwards compatible).

If user exists in multiple tenants and tenant_id is not provided,
returns an error asking for tenant selection.

**Request body:**

Required.

- `application/json` → [`scaikey__api__v1__me__auth__LoginRequest`](#schema-scaikey-api-v1-me-auth-loginrequest)

**Responses:**

| Status | Body |
|---|---|
| `200` | `application/json` → [`scaikey__api__v1__me__auth__LoginResponse`](#schema-scaikey-api-v1-me-auth-loginresponse) |
| `422` | `application/json` → [`HTTPValidationError`](#schema-httpvalidationerror) |

---

### **POST** `/api/v1/auth/logout`

_Portal Logout_

Logout from the user portal.

**Parameters:**

| Name | In | Required | Type | Description |
|---|---|---|---|---|
| `authorization` | header | no | `string` \| `null` |  |

**Responses:**

| Status | Body |
|---|---|
| `200` | `application/json` → object |
| `422` | `application/json` → [`HTTPValidationError`](#schema-httpvalidationerror) |

---

### **POST** `/api/v1/auth/mfa/verify`

_Portal Mfa Verify_

Verify MFA code for the portal two-step login flow.

After /authenticate returns MFA_REQUIRED, the frontend calls this
endpoint with the mfa_token and the TOTP/email/backup code.

**Request body:**

Required.

- `application/json` → [`PortalMFAVerifyRequest`](#schema-portalmfaverifyrequest)

**Responses:**

| Status | Body |
|---|---|
| `200` | `application/json` → [`PortalMFAVerifyResponse`](#schema-portalmfaverifyresponse) |
| `422` | `application/json` → [`HTTPValidationError`](#schema-httpvalidationerror) |

---

### **POST** `/api/v1/auth/refresh`

_Portal Refresh_

Refresh access token.

**Request body:**

Required.

- `application/json` → [`scaikey__api__v1__me__auth__RefreshRequest`](#schema-scaikey-api-v1-me-auth-refreshrequest)

**Responses:**

| Status | Body |
|---|---|
| `200` | `application/json` → [`scaikey__api__v1__me__auth__RefreshResponse`](#schema-scaikey-api-v1-me-auth-refreshresponse) |
| `422` | `application/json` → [`HTTPValidationError`](#schema-httpvalidationerror) |

---

## Schemas

Definitions for every type referenced by the endpoints above. Schema-to-schema references on this page link within the page; cross-page references would require visiting the linked page.

### `AuthenticateRequest`

| Field | Type | Required | Description |
|---|---|---|---|
| `flow_token` | `string` | yes |  |
| `tenant_id` | `string` \| `null` | no |  |
| `password` | `string` | yes |  |

### `AuthenticateResponse`

| Field | Type | Required | Description |
|---|---|---|---|
| `status` | `string` | no | Default: `SUCCESS` |
| `access_token` | `string` \| `null` | no |  |
| `refresh_token` | `string` \| `null` | no |  |
| `user` | object \| `null` | no |  |
| `mfa_token` | `string` \| `null` | no |  |
| `user_id` | `string` \| `null` | no |  |
| `available_methods` | array of `string` \| `null` | no |  |

### `HTTPValidationError`

| Field | Type | Required | Description |
|---|---|---|---|
| `detail` | array of [`ValidationError`](#schema-validationerror) | no |  |

### `IdentifyRequest`

| Field | Type | Required | Description |
|---|---|---|---|
| `email` | `string` (`email`) | yes |  |

### `IdentifyResponse`

| Field | Type | Required | Description |
|---|---|---|---|
| `flow_token` | `string` | yes |  |
| `tenants` | array of object | yes |  |
| `requires_tenant_selection` | `boolean` | yes |  |

### `PortalMFAVerifyRequest`

| Field | Type | Required | Description |
|---|---|---|---|
| `mfa_token` | `string` | yes |  |
| `user_id` | `string` | yes |  |
| `method` | `string` | yes |  |
| `code` | `string` \| `null` | no |  |
| `trust_device` | `boolean` | no | Default: `False` |

### `PortalMFAVerifyResponse`

| Field | Type | Required | Description |
|---|---|---|---|
| `access_token` | `string` | yes |  |
| `refresh_token` | `string` | yes |  |
| `user` | object | yes |  |

### `ValidationError`

| Field | Type | Required | Description |
|---|---|---|---|
| `loc` | array of `string` \| `integer` | yes |  |
| `msg` | `string` | yes |  |
| `type` | `string` | yes |  |

### `scaikey__api__v1__me__auth__LoginRequest`

| Field | Type | Required | Description |
|---|---|---|---|
| `email` | `string` (`email`) | yes |  |
| `password` | `string` | yes |  |
| `tenant_id` | `string` \| `null` | no |  |

### `scaikey__api__v1__me__auth__LoginResponse`

| Field | Type | Required | Description |
|---|---|---|---|
| `access_token` | `string` | yes |  |
| `refresh_token` | `string` | yes |  |
| `user` | object | yes |  |

### `scaikey__api__v1__me__auth__RefreshRequest`

| Field | Type | Required | Description |
|---|---|---|---|
| `refresh_token` | `string` | yes |  |

### `scaikey__api__v1__me__auth__RefreshResponse`

| Field | Type | Required | Description |
|---|---|---|---|
| `access_token` | `string` | yes |  |
| `refresh_token` | `string` \| `null` | no |  |
