इसे छोड़कर कंटेंट पर जाएं
Invoke

Multi-User API Guide

यह कंटेंट अभी तक आपकी भाषा में उपलब्ध नहीं है।

This guide explains how to interact with InvokeAI’s API in both single-user and multi-user modes. The API behavior depends on the multiuser configuration setting.

Single-User Mode (multiuser: false or option absent):

  • No authentication required
  • All API endpoints accessible without tokens
  • Direct API access like previous InvokeAI versions
  • All content visible in unified view

Multi-User Mode (multiuser: true):

  • JWT token authentication required
  • User-scoped access to resources
  • Role-based authorization (admin vs regular user)
  • Data isolation between users

When multi-user mode is enabled, most API endpoints require authentication using JWT bearer tokens. The unauthenticated authentication endpoints are GET /api/v1/auth/status, POST /api/v1/auth/setup, and POST /api/v1/auth/login.

Authentication Process:

  1. Obtain Token: POST credentials to /api/v1/auth/login
  2. Store Token: Save the JWT token securely
  3. Use Token: Include token in Authorization header for all requests
  4. Refresh: Re-authenticate when token expires

Endpoint: POST /api/v1/auth/login

Request:

{
"email": "[email protected]",
"password": "SecurePassword123",
"remember_me": false
}

Response (Success):

{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"user_id": "abc123",
"email": "[email protected]",
"display_name": "John Doe",
"is_admin": false,
"is_active": true,
"created_at": "2024-01-15T10:00:00Z",
"updated_at": "2024-01-15T10:00:00Z",
"last_login_at": "2024-01-15T15:30:00Z"
},
"expires_in": 86400
}

Response (Error):

{
"detail": "Incorrect email or password"
}

Status Codes:

  • 200 OK — Authentication successful
  • 401 Unauthorized — Invalid credentials
  • 403 Forbidden — Account disabled
  • 422 Unprocessable Entity — Invalid request format

Include the JWT token in the Authorization header with the Bearer scheme:

HTTP Header:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Example HTTP Request:

GET /api/v1/boards HTTP/1.1
Host: localhost:9090
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json

Tokens have a limited lifetime:

  • Default: 24 hours (86400 seconds)
  • Remember Me: 7 days (604800 seconds)

Handling Expiration:

import requests
import time
def api_request(url, token, max_retries=1):
headers = {"Authorization": f"Bearer {token}"}
response = requests.get(url, headers=headers)
if response.status_code == 401: # Token expired
# Re-authenticate and retry
new_token = login()
headers = {"Authorization": f"Bearer {new_token}"}
response = requests.get(url, headers=headers)
return response

Endpoint: POST /api/v1/auth/logout

Request:

POST /api/v1/auth/logout HTTP/1.1
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Response:

{
"success": true
}

Note: With JWT tokens, logout is primarily client-side (delete token). Server-side session invalidation may be added in future releases.

Using requests library:

import requests
import json
class InvokeAIClient:
def __init__(self, base_url="http://localhost:9090"):
self.base_url = base_url
self.token = None
def login(self, email, password, remember_me=False):
"""Authenticate and store token."""
url = f"{self.base_url}/api/v1/auth/login"
payload = {
"email": email,
"password": password,
"remember_me": remember_me
}
response = requests.post(url, json=payload)
response.raise_for_status()
data = response.json()
self.token = data["token"]
return data["user"]
def _get_headers(self):
"""Get headers with authentication token."""
if not self.token:
raise Exception("Not authenticated. Call login() first.")
return {
"Authorization": f"Bearer {self.token}",
"Content-Type": "application/json"
}
def get_boards(self):
"""Get user's boards."""
url = f"{self.base_url}/api/v1/boards/"
response = requests.get(url, headers=self._get_headers())
response.raise_for_status()
return response.json()
def create_board(self, board_name):
"""Create a new board."""
url = f"{self.base_url}/api/v1/boards/"
response = requests.post(
url,
params={"board_name": board_name},
headers=self._get_headers()
)
response.raise_for_status()
return response.json()
def logout(self):
"""Logout and clear token."""
url = f"{self.base_url}/api/v1/auth/logout"
response = requests.post(url, headers=self._get_headers())
self.token = None
return response.json()
# Usage
client = InvokeAIClient()
user = client.login("[email protected]", "SecurePassword123")
print(f"Logged in as: {user['display_name']}")
boards = client.get_boards()
print(f"User has {len(boards['items'])} boards")
new_board = client.create_board("My New Board")
print(f"Created board: {new_board['board_name']}")
client.logout()

Error Handling:

import requests
from requests.exceptions import HTTPError
def safe_api_call(client, method, *args, **kwargs):
"""Make API call with error handling."""
try:
func = getattr(client, method)
return func(*args, **kwargs)
except HTTPError as e:
if e.response.status_code == 401:
print("Authentication failed or token expired")
# Re-authenticate
client.login(email, password)
# Retry
return func(*args, **kwargs)
elif e.response.status_code == 403:
print("Permission denied")
elif e.response.status_code == 404:
print("Resource not found")
else:
print(f"API error: {e.response.status_code}")
print(e.response.text)
raise
# Usage
try:
boards = safe_api_call(client, "get_boards")
except Exception as e:
print(f"Failed to get boards: {e}")

Using fetch API:

class InvokeAIClient {
constructor(baseUrl = 'http://localhost:9090') {
this.baseUrl = baseUrl;
this.token = null;
}
async login(email, password, rememberMe = false) {
const response = await fetch(`${this.baseUrl}/api/v1/auth/login`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
email,
password,
remember_me: rememberMe,
}),
});
if (!response.ok) {
throw new Error(`Login failed: ${response.statusText}`);
}
const data = await response.json();
this.token = data.token;
// Store token in localStorage
localStorage.setItem('invokeai_token', data.token);
return data.user;
}
getHeaders() {
if (!this.token) {
throw new Error('Not authenticated. Call login() first.');
}
return {
'Authorization': `Bearer ${this.token}`,
'Content-Type': 'application/json',
};
}
async getBoards() {
const response = await fetch(`${this.baseUrl}/api/v1/boards/`, {
headers: this.getHeaders(),
});
if (!response.ok) {
throw new Error(`Failed to get boards: ${response.statusText}`);
}
return response.json();
}
async createBoard(boardName) {
const url = new URL(`${this.baseUrl}/api/v1/boards/`);
url.searchParams.set('board_name', boardName);
const response = await fetch(url, {
method: 'POST',
headers: this.getHeaders(),
});
if (!response.ok) {
throw new Error(`Failed to create board: ${response.statusText}`);
}
return response.json();
}
async logout() {
const response = await fetch(`${this.baseUrl}/api/v1/auth/logout`, {
method: 'POST',
headers: this.getHeaders(),
});
this.token = null;
localStorage.removeItem('invokeai_token');
return response.json();
}
}
// Usage
(async () => {
const client = new InvokeAIClient();
try {
const user = await client.login('[email protected]', 'SecurePassword123');
console.log(`Logged in as: ${user.display_name}`);
const boards = await client.getBoards();
console.log(`User has ${boards.items.length} boards`);
const newBoard = await client.createBoard('My New Board');
console.log(`Created board: ${newBoard.board_name}`);
await client.logout();
} catch (error) {
console.error('Error:', error.message);
}
})();

TypeScript with Types:

interface LoginRequest {
email: string;
password: string;
remember_me?: boolean;
}
interface User {
user_id: string;
email: string;
display_name: string;
is_admin: boolean;
is_active: boolean;
created_at: string;
}
interface LoginResponse {
token: string;
user: User;
expires_in: number;
}
interface Board {
board_id: string;
board_name: string;
created_at: string;
updated_at: string;
deleted_at?: string;
cover_image_name?: string;
}
class InvokeAIClient {
private baseUrl: string;
private token: string | null = null;
constructor(baseUrl: string = 'http://localhost:9090') {
this.baseUrl = baseUrl;
}
async login(
email: string,
password: string,
rememberMe: boolean = false
): Promise<User> {
const response = await fetch(`${this.baseUrl}/api/v1/auth/login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password, remember_me: rememberMe }),
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.detail || 'Login failed');
}
const data: LoginResponse = await response.json();
this.token = data.token;
return data.user;
}
private getHeaders(): HeadersInit {
if (!this.token) {
throw new Error('Not authenticated');
}
return {
'Authorization': `Bearer ${this.token}`,
'Content-Type': 'application/json',
};
}
async getBoards(): Promise<{ items: Board[] }> {
const response = await fetch(`${this.baseUrl}/api/v1/boards/`, {
headers: this.getHeaders(),
});
if (!response.ok) {
throw new Error('Failed to get boards');
}
return response.json();
}
}

Login:

Terminal window
# Login and extract token
TOKEN=$(curl -X POST http://localhost:9090/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{
"email": "[email protected]",
"password": "SecurePassword123",
"remember_me": false
}' | jq -r '.token')
echo "Token: $TOKEN"

Get Boards:

Terminal window
curl -X GET http://localhost:9090/api/v1/boards/ \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json"

Create Board:

Terminal window
curl -X POST "http://localhost:9090/api/v1/boards/?board_name=My%20API%20Board" \
-H "Authorization: Bearer $TOKEN"

All endpoints now require authentication except:

  • GET /api/v1/auth/status — Check whether multi-user setup is required
  • POST /api/v1/auth/setup — Initial admin setup
  • POST /api/v1/auth/login — User login
  • GET /api/v1/images/i/{image_name}/full — Full-resolution image file
  • GET /api/v1/images/i/{image_name}/thumbnail — Image thumbnail
  • GET /api/v1/workflows/i/{workflow_id}/thumbnail — Workflow thumbnail

The image and thumbnail endpoints are intentionally unauthenticated because browsers load these resources via <img src> tags, which cannot send Authorization headers. Security relies on the fact that image and workflow IDs are UUIDs and therefore unguessable.

Resources are now filtered by the authenticated user:

Boards:

# Before (single-user)
GET /api/v1/boards/?all=true # Returns all boards
# After (multi-user)
GET /api/v1/boards/?all=true # Returns boards the current user can access, including their own boards, boards they are a member of and boards with general access; admins can see all boards

Images:

# Images are filtered by board ownership
GET /api/v1/images/ # Only shows images on user's boards

Workflows:

# Returns user's workflows + public workflows
GET /api/v1/workflows/

Queue:

# Regular users see their own queue items in full and may see redacted details for other users' items on queue-status endpoints
GET /api/v1/queue/... # Queue data, sanitized for non-admin viewers
# With origin_prefix, item_ids, current, next and status cover only the caller's own items
GET /api/v1/queue/{queue_id}/status?origin_prefix=webv2:p:<project_id>:q:
# Administrators see all queue items in full
GET /api/v1/queue/... # Full queue data

Some endpoints require administrator privileges:

User Management:

GET /api/v1/auth/users # List users (admin only)
POST /api/v1/auth/users # Create user (admin only)
GET /api/v1/auth/users/{id} # Get user (admin only)
PATCH /api/v1/auth/users/{id} # Update user (admin only)
DELETE /api/v1/auth/users/{id} # Delete user (admin only)

Model Management (Write Operations):

POST /api/v2/models/install # Install model (admin only)
DELETE /api/v2/models/i/{key} # Delete model (admin only)
PATCH /api/v2/models/i/{key} # Update model (admin only)
PUT /api/v2/models/convert/{key} # Convert model (admin only)

Model Management (Read Operations):

GET /api/v2/models/ # List models (all users)
GET /api/v2/models/i/{key} # Get model details (all users)

401 Unauthorized:

{
"detail": "Invalid authentication credentials"
}

Occurs when:

  • Token is missing
  • Token is invalid
  • Token is expired
  • Token signature is invalid

403 Forbidden:

{
"detail": "Admin privileges required"
}

Occurs when:

  • User attempts admin-only operation
  • Account is disabled
  • Insufficient permissions

404 Not Found:

{
"detail": "Resource not found"
}

Occurs when:

  • Resource doesn’t exist
  • User doesn’t have access to resource

Check if initial administrator setup is required

Section titled “Check if initial administrator setup is required”

Endpoint: GET /api/v1/auth/status

Description: Returns a SetupStatusResponse indicating whether setup is needed and multiuser mode status.

Request: No parameters

Response (initial setup not yet complete):

{
"setup_required": true,
"multiuser_enabled": true,
"strict_password_checking": true,
"admin_email": "[email protected]"
}

Response (setup already complete, or multiuser disabled):

{
"setup_required": false,
"multiuser_enabled": true,
"strict_password_checking": true,
"admin_email": null
}

Endpoint: POST /api/v1/auth/setup

Description: Create initial administrator account (only works if no admin exists)

Request:

{
"email": "[email protected]",
"display_name": "Administrator",
"password": "SecureAdminPass123"
}

Response:

{
"success": true,
"user": {
"user_id": "abc123",
"email": "[email protected]",
"display_name": "Administrator",
"is_admin": true,
"is_active": true
}
}

Endpoint: GET /api/v1/auth/me

Description: Get currently authenticated user’s information

Request:

GET /api/v1/auth/me
Authorization: Bearer <token>

Response:

{
"user_id": "abc123",
"email": "[email protected]",
"display_name": "John Doe",
"is_admin": false,
"is_active": true,
"created_at": "2024-01-15T10:00:00Z",
"updated_at": "2024-01-15T10:00:00Z",
"last_login_at": "2024-01-15T15:30:00Z"
}

Endpoint: GET /api/v1/auth/users

Request:

GET /api/v1/auth/users
Authorization: Bearer <admin_token>

Response:

[
{
"user_id": "abc123",
"email": "[email protected]",
"display_name": "John Doe",
"is_admin": false,
"is_active": true,
"created_at": "2024-01-15T10:00:00Z",
"updated_at": "2024-04-25T17:23:00Z",
"last_login_at": "2024-01-15T15:30:00Z"
}
]

Endpoint: POST /api/v1/auth/users

Request:

{
"email": "[email protected]",
"display_name": "New User",
"password": "TempPassword123",
"is_admin": false
}

Response:

{
"user_id": "xyz789",
"email": "[email protected]",
"display_name": "New User",
"is_admin": false,
"is_active": true,
"created_at": "2024-01-15T16:00:00Z"
}

Endpoint: PATCH /api/v1/auth/users/{user_id}

Request:

{
"display_name": "Updated Name",
"is_active": true,
"is_admin": false
}

Response:

{
"user_id": "xyz789",
"email": "[email protected]",
"display_name": "Updated Name",
"is_admin": false,
"is_active": true
}

Endpoint: DELETE /api/v1/auth/users/{user_id}

Response:

Returns 204 No Content on success.

On an error, it returns 422 Unprocessable Content and the following JSON:

{
"detail": [
{
"loc": [
"string",
0
],
"msg": "string",
"type": "string"
}
]
}

Boards are available to every authenticated user; what each caller may do to one is decided by their role on it.

Endpoint: GET /api/v1/boards/

Response:

{
"limit": 0,
"offset": 0,
"total": 0,
"items": [
{
"board_id": "8b31a33d-0acb-46fe-8612-83601481cf2c",
"board_name": "Testing Board",
"user_id": "string",
"created_at": "2026-05-07T03:04:00.738Z",
"updated_at": "2026-05-07T03:04:00.738Z",
"deleted_at": "2026-05-07T03:04:00.738Z",
"cover_image_name": "string",
"archived": false,
"general_access": "restricted",
"board_visibility": "private",
"my_role": "owner",
"allowed_actions": ["read", "add_media", "manage_items", "rename", "set_cover", "archive", "set_general_access", "move", "delete"],
"admin_override": false,
"image_count": 0,
"asset_count": 0,
"owner_username": "string"
}
]
}

This returns a paged response. See the swagger page (http://localhost:9090/docs#/boards/list_boards) for details. Each board describes the caller’s access to it:

  • general_access — the role every other user gets: restricted (none), viewer or contributor. Only the owner or an administrator may change it, with PATCH /api/v1/boards/{board_id}.
  • my_role — the caller’s own role: viewer, contributor, editor or owner; null when only administrator rights give access.
  • allowed_actions — the board actions the caller may take. Clients should decide what to offer from this list rather than from my_role.
  • admin_override — true when administrator rights give the caller more than my_role does.
  • owner_username — the owner’s display name. Administrators see the email of an owner without one; other users see Unknown user.
  • project_id — shown only to the board’s owner and administrators. An image or video DTO’s project_id likewise names the project it was made in only to that project’s owner and to administrators; everyone else reads null.

Only a board that does not exist answers 404. A board the caller cannot open answers 403 with the detail {"code": "board_unreadable", "message": "Not authorized to access this board"}, and an action the caller’s role does not allow answers 403 with the code board_action_denied and the message Not authorized to change this board: a refusal can be reversed by the board’s owner, so clients must not treat it as the board being gone, and the code tells lost access from a stale role without matching the message. A project’s inbox refuses renaming, archiving, moving, deleting and a change of general access with 409; those follow its project.

Endpoint: GET /api/v1/boards/{board_id}

Response:

{
"board_id": "8b31a33d-0acb-46fe-8612-83601481cf2c",
"board_name": "Testing Board",
"user_id": "3c59a0ba-f4c7-4275-b96f-82179e8aaff8",
"created_at": "2026-03-09 16:10:47.095",
"updated_at": "2026-03-09 16:10:55",
"deleted_at": null,
"cover_image_name": "08689e4b-f084-4c49-83a8-4fc1edb167c4.png",
"archived": false,
"general_access": "viewer",
"board_visibility": "shared",
"my_role": "viewer",
"allowed_actions": ["read"],
"admin_override": false,
"image_count": 55,
"asset_count": 0,
"owner_username": "Sam"
}

Projects are private to the account that created them. The full save protocol, including the content and workspace halves and the schema floors, is described in Project Files (.invk).

Endpoint: POST /api/v1/projects

Creates the project and its inbox board and answers 201 with the record and workspace. The optional workspace field is the caller’s first workspace in the project, the workspace document itself (a create’s workspace is always the first row, so it carries no expected_revision), stored in the same transaction as the content, so no project is created without its creator’s workspace. It is checked as on PUT /api/v1/projects/{project_id}/workspace, except that the create’s max_canvas_schema_version covers it, and it needs minimum_document_schema_version at 4 or above: beside a whole document it is refused with 400 and the code workspace_needs_split_document, and nothing is created. The response’s workspace is the stored workspace (data, revision 1, updated_at), or null when the request carried none.

Endpoint: PUT /api/v1/projects/{project_id}

Saves the project’s content and answers with the record and workspace. The optional workspace field ({data, expected_revision}) stores the caller’s workspace in the same transaction and is for the save that splits a whole project (document schema 3 or older) into content and workspace: the raised document floor, the content and the first workspace row then land together or not at all. Its revision is checked as on PUT /api/v1/projects/{project_id}/workspace, and a mismatch refuses the whole save with 409 and the workspace’s current_revision. Omit it on every other save: a workspace sent with a save that leaves the document floor below 4 is refused with 400 and the code workspace_needs_split_document. The split cannot go without it either: a save that raises the floor from below 4 to 4 or above without workspace is refused with 400 and the code split_needs_workspace, so the split of a whole project never lands without the caller’s workspace.

Workspace saves declare max_canvas_schema_version (default 2) as well as max_document_schema_version; a client below the project’s canvas floor gets 412 with the code canvas_schema_unsupported.

Do:

  • Store tokens securely (keychain, secure storage)
  • Use HTTPS to transmit tokens
  • Clear tokens on logout
  • Handle token expiration gracefully

Don’t:

  • Store tokens in URL parameters
  • Log tokens in plain text
  • Share tokens between users
  • Store tokens in version control

Always handle authentication errors:

def make_request(client, func, *args, **kwargs):
max_retries = 3
retry_count = 0
while retry_count < max_retries:
try:
return func(*args, **kwargs)
except AuthenticationError:
if retry_count >= max_retries - 1:
raise
# Re-authenticate
client.login(email, password)
retry_count += 1
except Exception as e:
logger.error(f"Request failed: {e}")
raise

Be mindful of API rate limits:

  • Implement exponential backoff for retries
  • Cache frequently accessed data
  • Batch requests when possible
  • Don’t hammer the login endpoint
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
def create_session():
"""Create session with retry logic."""
session = requests.Session()
retry = Retry(
total=3,
backoff_factor=0.3,
status_forcelist=[500, 502, 503, 504],
)
adapter = HTTPAdapter(max_retries=retry)
session.mount('http://', adapter)
session.mount('https://', adapter)
return session

Before (single-user mode):

import requests
def get_boards():
response = requests.get("http://localhost:9090/api/v1/boards/")
return response.json()

After (multi-user mode):

import requests
class APIClient:
def __init__(self):
self.token = None
def login(self, email, password):
response = requests.post(
"http://localhost:9090/api/v1/auth/login",
json={"email": email, "password": password}
)
self.token = response.json()["token"]
def get_boards(self):
headers = {"Authorization": f"Bearer {self.token}"}
response = requests.get(
"http://localhost:9090/api/v1/boards/",
headers=headers
)
return response.json()
# Usage
client = APIClient()
client.login("[email protected]", "password")
boards = client.get_boards()

InvokeAI supports both single-user and multi-user modes via the multiuser configuration option.

Configuration:

invokeai.yaml
# Single-user mode (no authentication)
multiuser: false # or omit the option entirely
# Multi-user mode (authentication required)
multiuser: true

Checking Mode Programmatically:

def is_multiuser_enabled(base_url):
response = requests.get(f"{base_url}/api/v1/auth/status")
response.raise_for_status()
return response.json()["multiuser_enabled"]
# Example usage
base_url = "http://localhost:9090"
if is_multiuser_enabled(base_url):
print("Multi-user mode: authentication required")
# Use authenticated API calls
else:
print("Single-user mode: no authentication needed")
# Use direct API calls

Adaptive Client:

class AdaptiveInvokeAIClient:
def __init__(self, base_url="http://localhost:9090"):
self.base_url = base_url
self.token = None
self.multiuser_mode = self._check_multiuser_mode()
def _check_multiuser_mode(self):
"""Detect if multi-user mode is enabled."""
try:
response = requests.get(f"{self.base_url}/api/v1/boards/")
return response.status_code == 401
except:
return False
def login(self, email, password):
"""Login (only needed in multi-user mode)."""
if not self.multiuser_mode:
print("Single-user mode: login not required")
return
response = requests.post(
f"{self.base_url}/api/v1/auth/login",
json={"email": email, "password": password}
)
self.token = response.json()["token"]
def _get_headers(self):
"""Get headers (with auth token if in multi-user mode)."""
if self.multiuser_mode and self.token:
return {"Authorization": f"Bearer {self.token}"}
return {}
def get_boards(self):
"""Get boards (works in both modes)."""
response = requests.get(
f"{self.base_url}/api/v1/boards/",
headers=self._get_headers()
)
return response.json()

If you are building an external client against the HTTP API, see the dedicated Workflow Execution guide.

In multi-user mode, remember to include your Authorization: Bearer <token> header on queue and workflow requests.

InvokeAI provides OpenAPI documentation for all endpoints.

Access Swagger UI:

http://localhost:9090/docs

Download OpenAPI Schema:

Terminal window
curl http://localhost:9090/openapi.json > invokeai_openapi.json

Generate Client Code:

Use tools like openapi-generator to generate client libraries:

Terminal window
# Generate Python client
openapi-generator generate \
-i http://localhost:9090/openapi.json \
-g python \
-o ./invokeai-client
# Generate TypeScript client
openapi-generator generate \
-i http://localhost:9090/openapi.json \
-g typescript-fetch \
-o ./invokeai-client-ts

Always use HTTPS in production:

# Development
client = InvokeAIClient("http://localhost:9090")
# Production
client = InvokeAIClient("https://invoke.example.com")

Protect JWT tokens:

# Never log tokens
logger.info(f"User logged in") # Good
logger.info(f"Token: {token}") # Bad!
# Use environment variables for credentials
import os
email = os.environ.get("INVOKEAI_EMAIL")
password = os.environ.get("INVOKEAI_PASSWORD")

Always validate user input:

import re
def validate_email(email):
pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
return re.match(pattern, email) is not None
def validate_password(password):
"""Check password meets requirements."""
if len(password) < 8:
return False, "Password must be at least 8 characters"
if not any(c.isupper() for c in password):
return False, "Password must contain uppercase letters"
if not any(c.islower() for c in password):
return False, "Password must contain lowercase letters"
if not any(c.isdigit() for c in password):
return False, "Password must contain numbers"
return True, ""

Issue: “Invalid authentication credentials”

  • Token expired — re-authenticate
  • Token malformed — check token string
  • Token signature invalid — check secret key hasn’t changed

Issue: “Admin privileges required”

  • User is not an administrator
  • Use admin account for this operation

Issue: Token not being sent

  • Check Authorization header is present
  • Verify Bearer prefix is included
  • Check token isn’t truncated

Issue: CORS errors

Configure CORS in InvokeAI:

invokeai.yaml
allow_origins:
- "http://localhost:3000"
- "https://myapp.example.com"
allow_credentials: true
allow_methods:
- "*"
allow_headers:
- "*"

Questions? Visit the InvokeAI Discord or check the FAQ.