Management authentication
Login by email and password. This is the only profile with public self-registration.
| Endpoint | What it does |
|---|---|
POST /v1/auth/management/register | Public self-registration, protected by Cloudflare Turnstile. Requires confirming the email before logging in. |
POST /v1/auth/management/login | Login with email/password. Sets wallet_management_access_token, wallet_management_refresh_token (HttpOnly) and wallet_management_csrf_token. Rejects an operator credential even if valid. |
POST /v1/auth/management/refresh | Rotates the refresh token. Accepts the token from the body, the X-Refresh-Token header, or the cookie — in that priority order. Re-issues all three cookies. |
POST /v1/auth/management/logout | Revokes the current session and clears all three cookies. |
Example: login
- curl
- JavaScript
- Python
curl -X POST http://127.0.0.1:8787/v1/auth/management/login \
-H "Content-Type: application/json" \
-d '{ "email": "manager@example.com", "password": "a-strong-password" }'
const response = await fetch("http://127.0.0.1:8787/v1/auth/management/login", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email: "manager@example.com", password: "a-strong-password" }),
});
const session = await response.json();
import requests
response = requests.post(
"http://127.0.0.1:8787/v1/auth/management/login",
json={"email": "manager@example.com", "password": "a-strong-password"},
)
session = response.json()
Typical flow in a web app (cookie-based)
POST /v1/auth/management/login { email, password }
↓
receives a body with accessToken/refreshToken
+ Set-Cookie: wallet_management_access_token, wallet_management_refresh_token, wallet_management_csrf_token
↓
subsequent requests: the browser sends the cookies on its own
↓
a state-changing request (POST/PATCH/DELETE) also sends
X-CSRF-Token: <value read from the wallet_management_csrf_token cookie>
A client that prefers Bearer tokens can simply ignore the cookies and use
the accessToken from the response body in the Authorization: Bearer ...
header — both modes work at the same time, with no conflict.
What the management JWT contains
{
"sub": "supabase-auth-user-id",
"principal_type": "management",
"app_user_id": "global-business-user-id",
"session_id": "global-session-id",
"exp": 1784000000
}
It never contains an organization, role, or permission — that's resolved on
every call by looking up app.organization_members by app_user_id. A
manager can belong to several organizations with different roles in each;
the token doesn't pin that down.