Authentication
All Mantis API endpoints require authentication. This guide covers the supported authentication methods and flows.
Authentication Methods
Section titled “Authentication Methods”Mantis supports two authentication methods:
- JWT Bearer Tokens (recommended for user sessions)
- API Keys (recommended for automation and CI/CD)
JWT Bearer Token Authentication
Section titled “JWT Bearer Token Authentication”Login Flow
Section titled “Login Flow”Exchange your email and password for an access token:
POST /api/v1/auth/loginContent-Type: application/json
{ "email": "user@example.com", "password": "your-password"}Response:
{ "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImN1cnJlbnQifQ...", "token_type": "Bearer", "expires_in": 3600, "user": { "id": "550e8400-e29b-41d4-a716-446655440000", "email": "user@example.com", "username": "johndoe", "display_name": "John Doe", "status": "active", "roles": ["operator", "user"], "permissions": ["deployments:create", "deployments:read"], "created_at": "2024-01-15T10:30:00Z" }}Using the Access Token
Section titled “Using the Access Token”Include the token in the Authorization header with the Bearer prefix:
curl https://your-mantis-server/api/v1/deployments \ -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImN1cnJlbnQifQ..."Token Refresh
Section titled “Token Refresh”Before your access token expires, refresh it to get a new one:
POST /api/v1/auth/refreshCookie: refresh_token=<automatic>Response:
{ "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImN1cnJlbnQifQ...", "token_type": "Bearer", "expires_in": 3600, "user": { ... }}A new refresh token is also set as a cookie (refresh token rotation).
Logout
Section titled “Logout”Revoke all tokens for the current user:
POST /api/v1/auth/logoutAuthorization: Bearer <token>Response:
{ "message": "Successfully logged out"}Two-Factor Authentication (2FA)
Section titled “Two-Factor Authentication (2FA)”If 2FA is enabled for your account, the login flow has an additional step.
Step 1: Initial Login
Section titled “Step 1: Initial Login”POST /api/v1/auth/loginContent-Type: application/json
{ "email": "user@example.com", "password": "your-password"}Response (2FA Required):
{ "requires_2fa": true, "pending_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImN1cnJlbnQifQ..."}Step 2: Verify TOTP Code
Section titled “Step 2: Verify TOTP Code”POST /api/v1/auth/2fa/verifyContent-Type: application/json
{ "code": "123456", "pending_token": "<pending_token>"}Response (Success):
{ "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImN1cnJlbnQifQ...", "token_type": "Bearer", "expires_in": 3600, "user": { ... }}API Key Authentication
Section titled “API Key Authentication”API keys provide long-lived authentication for automation and CI/CD pipelines.
Creating an API Key
Section titled “Creating an API Key”POST /api/v1/auth/api-keysAuthorization: Bearer <jwt-token>Content-Type: application/json
{ "name": "CI/CD Pipeline", "description": "Automation token for GitHub Actions", "expires_in_days": 90, "scopes": ["deployments:create", "deployments:read"]}Response:
{ "api_key": { "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "name": "CI/CD Pipeline", "description": "Automation token for GitHub Actions", "key_prefix": "mnt_a1b2c3d4...", "scopes": ["deployments:create", "deployments:read"], "last_used_at": null, "last_used_ip": null, "created_at": "2024-01-15T10:30:00Z", "expires_at": "2024-04-15T10:30:00Z" }, "full_key": "mnt_a1b2c3d4e5f67890abcdef1234567890abcdef1234567890", "warning": "Store this key securely — it will not be shown again."}Using an API Key
Section titled “Using an API Key”Use the API key as a Bearer token (same as JWT):
curl https://your-mantis-server/api/v1/deployments \ -H "Authorization: Bearer mnt_a1b2c3d4e5f67890abcdef1234567890abcdef1234567890"API keys:
- Start with the prefix
mnt_ - Don’t expire unless you set
expires_in_days - Are scoped to the tenant of the user who created them
- Automatically track
last_used_atandlast_used_ip
Listing Your API Keys
Section titled “Listing Your API Keys”GET /api/v1/auth/api-keysAuthorization: Bearer <jwt-token>Response:
{ "api_keys": [ { "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "name": "CI/CD Pipeline", "description": "Automation token for GitHub Actions", "key_prefix": "mnt_a1b2c3d4...", "scopes": ["deployments:create", "deployments:read"], "last_used_at": "2024-01-20T15:45:00Z", "last_used_ip": "203.0.113.42", "created_at": "2024-01-15T10:30:00Z", "expires_at": "2024-04-15T10:30:00Z" } ]}Revoking an API Key
Section titled “Revoking an API Key”DELETE /api/v1/auth/api-keys/{id}Authorization: Bearer <jwt-token>Response:
{ "message": "API key revoked successfully"}SSE Tokens
Section titled “SSE Tokens”Server-Sent Events (SSE) endpoints — such as GET /api/v1/sse/{resource_type}
and GET /api/v1/deployments/{id}/logs — do not accept your regular JWT in
an Authorization header. Because EventSource cannot set request headers, SSE
connections authenticate with a short-lived, single-use token passed in the
?sse_token= query parameter.
Obtaining an SSE Token
Section titled “Obtaining an SSE Token”Exchange a valid JWT (or API key) for an SSE token:
POST /api/v1/auth/sse-tokenAuthorization: Bearer <jwt-token>Content-Type: application/json
{}Response:
{ "sse_token": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCI...", "expires_in": 60, "session_id": "b3c1...", "refresh_token": "..."}Using an SSE Token
Section titled “Using an SSE Token”Pass the token in the ?sse_token= query parameter when opening the connection:
const { sse_token } = await fetch('/api/v1/auth/sse-token', { method: 'POST', headers: { Authorization: `Bearer ${accessToken}`, 'Content-Type': 'application/json', }, body: '{}',}).then((r) => r.json());
const eventSource = new EventSource( `/api/v1/sse/deployments?sse_token=${sse_token}`);Refreshing an SSE Session
Section titled “Refreshing an SSE Session”To renew an SSE session without a full re-login, use the session_id and
refresh_token from the original response:
POST /api/v1/auth/sse-token/refreshContent-Type: application/json
{ "session_id": "b3c1...", "refresh_token": "..."}Current User Information
Section titled “Current User Information”Get information about the currently authenticated user:
GET /api/v1/auth/meAuthorization: Bearer <token>Response:
{ "id": "550e8400-e29b-41d4-a716-446655440000", "email": "user@example.com", "username": "johndoe", "display_name": "John Doe", "status": "active", "email_verified": true, "roles": ["operator", "user"], "permissions": ["deployments:create", "deployments:read", "targets:read"], "last_login_at": "2024-01-20T08:30:00Z", "created_at": "2023-06-01T12:00:00Z"}Security Best Practices
Section titled “Security Best Practices”Token Storage
Section titled “Token Storage”Web Applications:
- Store access tokens in memory (JavaScript variable)
- Never store tokens in
localStorageorsessionStorage - Refresh tokens are handled automatically via HTTP-only cookies
Mobile/Desktop Apps:
- Use secure platform-specific storage (Keychain on iOS, Keystore on Android)
- Never log token values
CI/CD Pipelines:
- Use API keys instead of JWT tokens
- Store API keys in secret management systems (GitHub Secrets, HashiCorp Vault, etc.)
- Rotate API keys regularly (set
expires_in_days)
Token Security
Section titled “Token Security”API Key Scopes
Section titled “API Key Scopes”When creating API keys, use the minimum required scopes:
{ "name": "Read-Only Monitoring", "scopes": ["deployments:read", "targets:read"]}Available scopes match the permission system. See the Permissions guide for the full list.
Troubleshooting
Section titled “Troubleshooting”401 Unauthorized
Section titled “401 Unauthorized”Error:
{ "error": { "code": "UNAUTHORIZED", "message": "Authentication required" }}Causes:
- Missing
Authorizationheader - Token not prefixed with
Bearer - Malformed token
TOKEN_EXPIRED
Section titled “TOKEN_EXPIRED”Error:
{ "error": { "code": "TOKEN_EXPIRED", "message": "Token expired" }}Solution: Call /auth/refresh to get a new access token.
INVALID_CREDENTIALS
Section titled “INVALID_CREDENTIALS”Error:
{ "error": { "code": "INVALID_CREDENTIALS", "message": "Invalid credentials" }}Causes:
- Wrong email or password
- Account is suspended or inactive
- API key is expired or revoked
TWO_FACTOR_REQUIRED
Section titled “TWO_FACTOR_REQUIRED”Error:
{ "error": { "code": "TWO_FACTOR_REQUIRED", "message": "Two-factor authentication required" }}Cause: You’re using a pending_token on a regular endpoint. Complete the 2FA flow first by calling /auth/2fa/verify.
Code Examples
Section titled “Code Examples”JavaScript/TypeScript
Section titled “JavaScript/TypeScript”class MantisClient { private accessToken: string | null = null; private baseUrl: string;
constructor(baseUrl: string) { this.baseUrl = baseUrl; }
async login(email: string, password: string): Promise<void> { const response = await fetch(`${this.baseUrl}/api/v1/auth/login`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email, password }), credentials: 'include', // Include cookies for refresh token });
if (!response.ok) { throw new Error('Login failed'); }
const data = await response.json(); this.accessToken = data.access_token; }
async listDeployments(): Promise<any> { const response = await fetch(`${this.baseUrl}/api/v1/deployments`, { headers: { Authorization: `Bearer ${this.accessToken}`, }, });
return response.json(); }}
// Usageconst client = new MantisClient('https://mantis.example.com');await client.login('user@example.com', 'password');const deployments = await client.listDeployments();Python
Section titled “Python”import requestsfrom typing import Optional
class MantisClient: def __init__(self, base_url: str): self.base_url = base_url self.access_token: Optional[str] = None self.session = requests.Session()
def login(self, email: str, password: str) -> None: response = self.session.post( f"{self.base_url}/api/v1/auth/login", json={"email": email, "password": password} ) response.raise_for_status() data = response.json() self.access_token = data["access_token"]
def list_deployments(self) -> list: response = self.session.get( f"{self.base_url}/api/v1/deployments", headers={"Authorization": f"Bearer {self.access_token}"} ) response.raise_for_status() return response.json()
# Usageclient = MantisClient("https://mantis.example.com")client.login("user@example.com", "password")deployments = client.list_deployments()cURL (API Key)
Section titled “cURL (API Key)”#!/bin/bashset -e
MANTIS_URL="https://mantis.example.com"API_KEY="mnt_your_api_key_here"
# Create deploymentcurl -X POST "$MANTIS_URL/api/v1/deployments" \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "solution_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890" }'Next Steps
Section titled “Next Steps”- Error Handling - Understand API error responses
- Pagination - Work with paginated results
- Deployments API - Create and manage deployments
