Everything the portals do goes through the same REST API under /api/v1. There is no private back channel, so anything the UI can do is available to your own integrations.
Route groups
| Prefix | Audience | Middleware |
|---|---|---|
| /api/v1/auth | Sign-in, MFA, passkeys, password reset | Throttled; mostly public |
| /api/v1/admin | Provider staff | Authenticated, tenant-resolved, licence-checked |
| /api/v1/client | Client portal users | Authenticated, tenant-resolved, licence-checked |
| /api/v1/platform | Platform owner | Authenticated, super_admin only |
| /api/v1/licensing | Licence validation, activation, download, checkout | Throttled |
| /api/v1/webhooks/{gateway} | Payment gateway callbacks | Signature-verified |
Authentication
Sanctum in stateful mode. Fetch the CSRF cookie, sign in, and the session cookie carries the rest. Requests must send the XSRF token header and be made with credentials included.
Signing in from a browser client
await fetch('https://api.example.com/sanctum/csrf-cookie', { credentials: 'include' });
await fetch('https://api.example.com/api/v1/auth/login', {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest',
'X-XSRF-TOKEN': tokenFromCookie,
},
body: JSON.stringify({ email, password }),
});Response shape
Successful responses wrap the payload in data, with meta and errors alongside. Validation failures return 422 with an errors object keyed by field.
{
"data": { "...": "..." },
"meta": {},
"errors": []
}TipA 403 from an /admin or /client route with a licensing reason means the licence is not valid for this installation, not that the user lacks permission.