# Multi-User API Guide

## Overview

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 vs Multi-User Mode

**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

## Authentication (Multi-User Mode Only)

### Authentication Flow

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

### Login Endpoint

**Endpoint:**`POST /api/v1/auth/login`

**Request:**

```
{
  "email": "user@example.com",
  "password": "SecurePassword123",
  "remember_me": false
}
```

**Response (Success):**

```
{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "user": {
    "user_id": "abc123",
    "email": "user@example.com",
    "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

### Using the Token

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
```

### Token Expiration

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
```

### Logout Endpoint

**Endpoint:**`POST /api/v1/auth/logout`

**Request:**

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

**Response:**

```
{
  "success": true
}
```

## Code Examples

### Python

**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("user@example.com", "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()
```

### JavaScript/TypeScript

**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;
    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('user@example.com', '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);
  }
})();
```

### cURL

**Login:**

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

**Get Boards:**

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

**Create Board:**

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

## Best Practices

### Token Storage

**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

### Error Handling

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
```

### Rate Limiting

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

## Troubleshooting

### Common Issues

**Issue: “Invalid authentication credentials”**

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

**Issue: Token not being sent**

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

## Additional Resources

- [User Guide](/content/features/multi-user-mode/api-guide/user-guide/index.html) — For end users
- [Administrator Guide](/content/features/multi-user-mode/api-guide/admin-guide/index.html) — For administrators
- [GitHub Repository](https://github.com/invoke-ai/InvokeAI) — Source code

### Questions?
Visit the [InvokeAI Discord](https://discord.gg/ZmtBAhwWhy) or check the [FAQ](/content/troubleshooting/faq/index.html).
