Appearance
Authentication
Public endpoints under /auth/*. No token is needed to call them - this is how you get one.
Once you have a token, send it on every /api/* request:
http
Authorization: Bearer <token>The token is either a login JWT (this page) or an API key for non-interactive use (see API keys).
Register
http
POST /auth/registerjson
{
"username": "alice",
"firstName": "Alice",
"lastName": "Smith",
"email": "alice@example.com",
"password": "Sup3rSecret",
"passwordConfirm": "Sup3rSecret",
"acceptTerms": true
}| Field | Rule |
|---|---|
username | 2-30 characters, lowercase letters, digits and hyphens; a hyphen may not start or end it. Stored hyphens become underscores, so my-app registers as my_app. |
firstName, lastName | 2-15 letters, nothing else. Title-cased for you. |
email | Must be a real address; you will need to click the link we send. |
password | 8+ characters with an uppercase letter, a lowercase letter, and a digit. |
passwordConfirm | Must match password. |
acceptTerms | Must be true. The server enforces it, not just the console. |
Returns 201 with the newly created user. The first person to register creates the account and becomes its Owner:
json
{
"id": "6a4d5b83b7006ccae6391810",
"username": "alice",
"firstName": "Alice",
"lastName": "Smith",
"email": "alice@example.com",
"lastLogin": null,
"isActive": true,
"isVerified": false,
"role": "user",
"accountRole": "owner",
"accountId": "6a4d5b83b7006ccae6391800",
"acceptedTermsVersion": "2026-07-22",
"acceptedTermsAt": "2026-07-16T09:41:02Z",
"createdAt": "2026-07-16T09:41:02Z",
"updatedAt": null
}We send a verification email straight away. You can log in before verifying, but /api/* calls are blocked until your email is verified (see below). A username or email that's already taken returns 409. Several validation failures come back as one 400 with the messages joined by ; .
Login
http
POST /auth/loginjson
{
"username": "alice",
"password": "Sup3rSecret"
}username accepts either the username you registered with or your account email address.
Response - just a token, nothing else:
json
{ "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50SWQiOiI2YTRkNWI4M2I3MDA2Y2NhZTYzOTE4MDAifQ.7dQm1s8lVQK0kQ5pQmDQ8P2t0nJfYhZg-example" }That token is a JWT carrying your account and user identity. Send it as Authorization: Bearer <token> on your API calls.
No refresh, no logout
The token expires on a timer (an hour by default). There is no refresh endpoint and no logout endpoint - when it expires, just log in again. To "log out", discard the token on your side. Stateless and simple.
Some things do end a session early, because the token carries a version stamp that the server checks on every request. Changing your password, resetting it, having your role changed, or being disabled or removed all bump that stamp, and every token issued before the bump starts returning 401:
json
{ "message": "Session expired, please sign in again" }Too many wrong guesses
Eight failed logins for the same username within 15 minutes returns 429 Too many requests. A correct password clears the counter. Wait out the window, or reset your password.
Verify account
http
POST /auth/verify-account/:token:token is the verification token from the signup email; it is good for 24 hours. Until you verify, /api/* calls return 403 with "User is not verified". A used or expired token returns 401.
json
{ "message": "Account verified successfully" }Resend verification email
http
POST /auth/resend-verification-emailjson
{ "email": "alice@example.com" }Request a password reset
http
POST /auth/reset-password-requestjson
{ "email": "alice@example.com" }Always returns 200, whether or not that email has an account - we are not going to tell a stranger which addresses are registered. If it exists, a reset link lands in the inbox and is valid for 24 hours.
Complete a password reset
http
POST /auth/reset-passwordjson
{
"token": "3f5c1d9a-77b2-4c1e-9f0a-2bd6c4e18a55",
"password": "N3wSecret",
"passwordConfirm": "N3wSecret"
}token comes from the reset email; password and passwordConfirm must match and the new password must pass the same rules as at registration. Resetting your password signs out every existing session.
This is also the endpoint that sets a first password for an invited team member - the invitation email carries the same kind of token, valid for 7 days. See Team & roles.
Programmatic access
Logging in with a username and password is for humans. For scripts, CI, or the automation / MCP endpoint, create an API key instead - it does not expire and you can scope it down to exactly what it needs.
Example
bash
curl -X POST https://cloud-api.microapps.io/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"alice","password":"Sup3rSecret"}'Grab the token and use it straight away:
bash
export MICROAPPS_TOKEN=$(curl -s -X POST https://cloud-api.microapps.io/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"alice","password":"Sup3rSecret"}' | jq -r .token)
curl https://cloud-api.microapps.io/api/user/profile \
-H "Authorization: Bearer $MICROAPPS_TOKEN"