API Authentication
Authenticate requests to the MaxRMM REST API using JWT access and refresh tokens.
Base URL
https://api.maxrmm.com All API endpoints are served over HTTPS. HTTP requests are rejected.
Authentication Flow
MaxRMM uses a JWT (JSON Web Token) authentication flow with short-lived access tokens and long-lived refresh tokens.
1. Login
POST /api/auth/login
Content-Type: application/json
{
"email": "admin@company.com",
"password": "your-password"
} Response (no MFA):
{
"accessToken": "eyJhbGciOiJIUzI1NiIs...",
"refreshToken": "eyJhbGciOiJIUzI1NiIs...",
"mfaRequired": false
} Response (MFA enabled):
{
"mfaRequired": true,
"mfaToken": "eyJhbGciOiJIUzI1NiIs..."
} 2. MFA Verification (if required)
If the login response includes mfaRequired: true, send the TOTP code:
POST /api/auth/mfa-verify
Content-Type: application/json
{
"mfaToken": "eyJhbGciOiJIUzI1NiIs...",
"code": "123456"
} Response is the same as a successful login (accessToken + refreshToken).
Note: The mfaToken expires after 5 minutes. If it expires, the user must log in again.
3. Using the Access Token
Include the access token in the Authorization header for all API requests:
GET /api/agents
Authorization: Bearer eyJhbGciOiJIUzI1NiIs... 4. Refreshing Tokens
Access tokens expire after a short period. Use the refresh token to get a new pair without re-entering credentials:
POST /api/auth/refresh
Content-Type: application/json
{
"refreshToken": "eyJhbGciOiJIUzI1NiIs..."
} Response:
{
"accessToken": "eyJhbGciOiJIUzI1NiIs...(new)",
"refreshToken": "eyJhbGciOiJIUzI1NiIs...(new)",
"mfaRequired": false
} Important: Each refresh token can only be used once. After refreshing, the old refresh token is invalidated and a new one is returned. Store the new refresh token for future use.
Token Lifecycle
| Token | Lifetime | Storage Recommendation |
|---|---|---|
| Access Token | 15 minutes | In-memory only. Do not persist to disk or localStorage in browsers. |
| Refresh Token | 7 days | Secure HTTP-only cookie or encrypted storage. Never expose in client-side code. |
| MFA Token | 5 minutes | In-memory only. Discard after MFA verification. |
Access Token Payload
The JWT access token contains these claims:
{
"technicianId": "cl8f2k9x0001abcdef012345",
"companyId": "cl8f2k9x0002abcdef012345",
"role": "admin",
"iat": 1711234567,
"exp": 1711235467
} | Claim | Description |
|---|---|
technicianId | Unique identifier of the authenticated technician |
companyId | Tenant/company the technician belongs to |
role | Permission level: admin, technician, or readonly |
iat | Issued-at timestamp (Unix epoch) |
exp | Expiration timestamp (Unix epoch) |
Error Responses
| Status | Error | Meaning |
|---|---|---|
400 | Email and password required | Missing required fields in login request |
401 | Invalid email or password | Credentials do not match |
401 | Invalid or expired MFA token | MFA token has expired (5-minute window) |
401 | Invalid TOTP code | The 6-digit MFA code is wrong |
401 | Invalid or expired refresh token | Refresh token was already used or expired |
Example: Full Auth Flow in cURL
# 1. Login
ACCESS=$(curl -s -X POST https://api.maxrmm.com/api/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"admin@company.com","password":"your-password"}' \
| jq -r '.accessToken')
# 2. Use the token
curl -s https://api.maxrmm.com/api/agents \
-H "Authorization: Bearer $ACCESS" \
| jq '.[].hostname'