Sign-in and sessions
POST /api/auth/login is where an integration starts: it exchanges an email and password for an access token that every VDF AI service accepts. Send that token as Authorization: Bearer <ACCESS_TOKEN> on subsequent calls.
The login response also sets the token as an HttpOnly cookie, which is how the browser portal shares one session across its separate front-end bundles. Server-to-server callers should ignore the cookie and use the Authorization header.
Where Microsoft Entra ID sign-in is enabled, GET /api/auth/sso/entra/login begins a browser redirect flow that ends with the same kind of access token; see Start Microsoft sign-in. Password reset is available to signed-out users through the portal rather than the API.
- POST /auth/login Sign in
- GET /auth/session Read the current session
- GET /auth/permissions List your feature permissions
- POST /auth/logout Sign out
- GET /auth/sso/entra/config Retrieve Microsoft sign-in availability
- GET /auth/sso/entra/login Start Microsoft sign-in
- GET /roles List roles
Paths are relative to /api
Sign in
POST /api/auth/login
Verifies the credentials and returns an access token that identifies the user to every VDF AI service. The token is returned twice — as access_token and again inside user.token — for client compatibility; both are the same string. The same token is also set as an HttpOnly cookie on the response so that browser applications on the same site share one session.
- Authentication
- None
Body parameters application/json
-
emailstring RequiredThe user's email address.
-
passwordstring RequiredThe user's password.
Returns
Returns the access token and the signed-in user.
Errors
- 400 The request body is missing, or
emailorpasswordwas not supplied. - 401 The email is unknown or the password is incorrect. The message does not distinguish the two.
- 403 The account has been deactivated.
curl -X POST "$VDF_BASE_URL/api/auth/login" \
-H "Content-Type: application/json" \
-d '{
"email": "ada@example.com",
"password": "<PASSWORD>"
}' const response = await fetch(`${process.env.VDF_BASE_URL}/api/auth/login`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
email: 'ada@example.com',
password: '<PASSWORD>',
}),
});
if (!response.ok) throw new Error(`Request failed with status ${response.status}`);
const data = await response.json(); import os
import requests
response = requests.post(
f"{os.environ['VDF_BASE_URL']}/api/auth/login",
json={
"email": "ada@example.com",
"password": "<PASSWORD>",
},
timeout=30,
)
response.raise_for_status()
data = response.json() {
"success": true,
"access_token": "<ACCESS_TOKEN>",
"user": {
"id": 42,
"email": "ada@example.com",
"name": "Ada Lovelace",
"userType": "default",
"permissions": {
"chat-history": {
"enabled": true,
"limit": null
},
"chat-messages": {
"enabled": true,
"limit": null
}
},
"company": {
"id": 1,
"name": "Contoso",
"address": "1 Example Way",
"city": "London",
"country": "United Kingdom",
"postal_code": "EC1A 1BB"
},
"token": "<ACCESS_TOKEN>"
}
} Read the current session
GET /api/auth/session
Returns the signed-in user, or an unauthenticated marker when there is no valid token.
Reads the session from either the Authorization header or the session cookie. When no valid token is present it returns {"authenticated": false} with a 200, not a 401, so a front-end can call it unconditionally on load.
This endpoint re-reads the user from the server rather than trusting the token, so it reflects a role change or a deactivation immediately. A deactivated account returns 401 and the session cookie is cleared.
- Authentication
- Optional bearer token How it works
Parameters
No parameters.
Returns
Returns the current session, authenticated or not.
Errors
- 401 The account has been deactivated. The session cookie is cleared.
curl "$VDF_BASE_URL/api/auth/session" \
-H "Authorization: Bearer $VDF_ACCESS_TOKEN" const response = await fetch(`${process.env.VDF_BASE_URL}/api/auth/session`, {
headers: {
Authorization: `Bearer ${process.env.VDF_ACCESS_TOKEN}`,
},
});
if (!response.ok) throw new Error(`Request failed with status ${response.status}`);
const data = await response.json(); import os
import requests
response = requests.get(
f"{os.environ['VDF_BASE_URL']}/api/auth/session",
headers={"Authorization": f"Bearer {os.environ['VDF_ACCESS_TOKEN']}"},
timeout=30,
)
response.raise_for_status()
data = response.json() {
"authenticated": true,
"user": {
"id": 42,
"email": "ada@example.com",
"name": "Ada Lovelace",
"userType": "default",
"permissions": {
"chat-history": {
"enabled": true,
"limit": null
}
},
"authSource": "local",
"token": null
}
} List your feature permissions
GET /api/auth/permissions
Returns the caller's role and the features enabled for it.
Returns the feature flags that apply to the caller's own role. Each feature reports whether it is enabled and any usage limit that applies. Use it to decide which parts of your integration to offer a given user.
- Authentication
- Bearer token How it works
Parameters
No parameters.
Returns
Returns the caller's feature permissions in data.
curl "$VDF_BASE_URL/api/auth/permissions" \
-H "Authorization: Bearer $VDF_ACCESS_TOKEN" const response = await fetch(`${process.env.VDF_BASE_URL}/api/auth/permissions`, {
headers: {
Authorization: `Bearer ${process.env.VDF_ACCESS_TOKEN}`,
},
});
if (!response.ok) throw new Error(`Request failed with status ${response.status}`);
const data = await response.json(); import os
import requests
response = requests.get(
f"{os.environ['VDF_BASE_URL']}/api/auth/permissions",
headers={"Authorization": f"Bearer {os.environ['VDF_ACCESS_TOKEN']}"},
timeout=30,
)
response.raise_for_status()
data = response.json() {
"success": true,
"data": {
"user_type": "default",
"features": {
"chat-history": {
"enabled": true,
"limit": null
},
"chat-messages": {
"enabled": true,
"limit": null
},
"report-analysis": {
"enabled": true,
"limit": null
},
"scrum-team-agent": {
"enabled": true,
"limit": null
}
}
}
} Sign out
POST /api/auth/logout
Clears the session cookie set at login. Clients that send the token in the Authorization header should also discard their stored copy.
- Authentication
- None
Parameters
No parameters.
Returns
Returns success: true and clears the session cookie.
curl -X POST "$VDF_BASE_URL/api/auth/logout" const response = await fetch(`${process.env.VDF_BASE_URL}/api/auth/logout`, {
method: 'POST',
});
if (!response.ok) throw new Error(`Request failed with status ${response.status}`);
const data = await response.json(); import os
import requests
response = requests.post(
f"{os.environ['VDF_BASE_URL']}/api/auth/logout",
timeout=30,
)
response.raise_for_status()
data = response.json() {
"success": true
} Retrieve Microsoft sign-in availability
GET /api/auth/sso/entra/config
Reports whether Microsoft Entra ID sign-in is available.
A public endpoint that a sign-in page uses to decide whether to show the Microsoft sign-in button. It returns only whether the feature is enabled and configured, and a label to show — never any client secret or tenant detail.
- Authentication
- None
Parameters
No parameters.
Returns
Returns whether Microsoft sign-in is available.
curl "$VDF_BASE_URL/api/auth/sso/entra/config" const response = await fetch(`${process.env.VDF_BASE_URL}/api/auth/sso/entra/config`);
if (!response.ok) throw new Error(`Request failed with status ${response.status}`);
const data = await response.json(); import os
import requests
response = requests.get(
f"{os.environ['VDF_BASE_URL']}/api/auth/sso/entra/config",
timeout=30,
)
response.raise_for_status()
data = response.json() {
"enabled": true,
"buttonLabel": "Sign in with Microsoft"
} Start Microsoft sign-in
GET /api/auth/sso/entra/login
Begins the Microsoft Entra ID sign-in flow by redirecting the browser to Microsoft. After the user signs in, Microsoft returns them to this deployment, which completes the sign-in, issues the same kind of session as a password sign-in, and redirects to return_url (or the application home). The intermediate return step is handled entirely by the deployment and is not called directly by clients.
This is a browser redirect, not a JSON endpoint: open it as a top-level navigation rather than fetching it. It is available only where Microsoft sign-in is enabled and configured.
- Authentication
- None
Query parameters
-
return_urlstringWhere to send the browser after a successful sign-in. Must be a path on this deployment or a URL whose host is one of its allowed origins; any other value is ignored.
returnUrlis accepted as an alias.
Returns
Redirects to Microsoft to continue sign-in.
The response is an HTTP redirect; there is no JSON body.
Errors
- 404 Microsoft sign-in is not enabled on this deployment.
- 503 Microsoft sign-in is enabled but not fully configured, so the flow cannot start.
curl -i "$VDF_BASE_URL/api/auth/sso/entra/login?return_url=%2Fconsultant%2Fapp" const response = await fetch(`${process.env.VDF_BASE_URL}/api/auth/sso/entra/login?return_url=%2Fconsultant%2Fapp`);
if (!response.ok) throw new Error(`Request failed with status ${response.status}`); import os
import requests
response = requests.get(
f"{os.environ['VDF_BASE_URL']}/api/auth/sso/entra/login",
params={
"return_url": "/consultant/app",
},
allow_redirects=False,
timeout=30,
)
response.raise_for_status()
print(response.headers["Location"]) List roles
GET /api/roles
Returns the catalogue of roles the deployment defines.
Returns the roles a deployment recognises, so an application can render role labels and descriptions from one source of truth rather than hard-coding them. Any signed-in user may read it. Each role has a name, a numeric rank (higher grants more), a display label, and a description.
- Authentication
- Bearer token How it works
Parameters
No parameters.
Returns
Returns the role catalogue in roles.
curl "$VDF_BASE_URL/api/roles" \
-H "Authorization: Bearer $VDF_ACCESS_TOKEN" const response = await fetch(`${process.env.VDF_BASE_URL}/api/roles`, {
headers: {
Authorization: `Bearer ${process.env.VDF_ACCESS_TOKEN}`,
},
});
if (!response.ok) throw new Error(`Request failed with status ${response.status}`);
const data = await response.json(); import os
import requests
response = requests.get(
f"{os.environ['VDF_BASE_URL']}/api/roles",
headers={"Authorization": f"Bearer {os.environ['VDF_ACCESS_TOKEN']}"},
timeout=30,
)
response.raise_for_status()
data = response.json() {
"success": true,
"roles": [
{
"name": "default",
"rank": 10,
"label": "Default",
"description": "Access to the master agent with a basic set of tools, skills and MCP servers."
},
{
"name": "expert",
"rank": 30,
"label": "Expert",
"description": "May create skills, tools and MCP servers and share them with individuals."
}
]
}