Users
A user account is what a person signs in to your deployment with. An administrator creates accounts one at a time with a password, in bulk, or by importing them from Microsoft Entra ID (see Directory synchronisation). Where the deployment allows it, people who sign in with Microsoft for the first time are also given an account automatically.
An account signs in with its password (auth_source is local) or with Microsoft Entra ID (entra). For an entra account, the name, email address and password can't be changed through this API, and its role can be changed only once the role is pinned with role_locked.
Accounts are never removed through this API. Deactivate a user keeps the account and its data and stops the user signing in; Update a user reactivates it. The last active administrator who can sign in with a password can't be deactivated, so that somebody can still sign in if Microsoft sign-in becomes unavailable.
Administrator access. Every endpoint on this page requires an administrator account.
- GET /admin/users List users
- POST /admin/users Create a user
- GET /admin/users/{user_id} Retrieve a user
- PUT /admin/users/{user_id} Update a user
- DEL /admin/users/{user_id} Deactivate a user
- POST /admin/users/{user_id}/resend-password Send a password reset email
- POST /admin/users/{user_id}/unlink-sso Unlink a user from single sign-on
- GET /admin/users/bulk/preflight Retrieve bulk creation limits
- POST /admin/users/bulk Create users in bulk
Paths are relative to /api
The user object
Retrieve, create and update return every field below except those marked as list results only. List users returns those fields instead of email_verified and email_verified_at.
Attributes
-
idintegerUnique identifier for the user.
-
emailstringThe user's email address.
-
namestringThe user's display name.
-
user_typestringThe user's role. The possible values are listed under
user_typein Create a user. -
is_activebooleanWhether the account is active. A deactivated account can't sign in.
-
is_superadminbooleanWhether the user is an administrator.
-
auth_sourcestringHow the user signs in:
localwith a password, orentrawith Microsoft Entra ID. List results only. -
role_lockedbooleanWhether the role is pinned, so that signing in with Microsoft doesn't change it through directory group role mappings. List results only.
-
last_role_sync_atnullable stringWhen the role was last checked against directory group role mappings at a Microsoft sign-in, or
nullif it never has been. List results only. -
email_verifiedbooleanWhether the email address is marked as verified. Accounts created by an administrator are created verified. Not included in list results.
-
email_verified_atnullable stringWhen the email address was marked as verified. Not included in list results.
-
created_atstringWhen the account was created, in ISO 8601 format.
-
updated_atstringWhen the account was last changed, in ISO 8601 format. List results only.
{
"id": 42,
"email": "ada@example.com",
"name": "Ada Lovelace",
"user_type": "default",
"is_active": true,
"is_superadmin": false,
"email_verified": true,
"email_verified_at": "2026-09-01T09:30:00.412893+00:00",
"created_at": "2026-09-01T09:30:00.412893+00:00"
} List users
GET /api/admin/users
Returns a page of user accounts, newest first, with optional filters.
Returns the deployment's accounts, including deactivated ones, ordered by creation time with the newest first. Filters are optional and combine. The response gives total (the number of matching accounts), page, limit, and pages (the number of pages at this limit).
- Authentication
- Bearer token How it works
- Permission
- Requires an administrator account.
Query parameters
-
pageintegerThe page to return, counting from 1.
-
limitintegerAccounts per page. Values above 100 are treated as 100.
-
emailstringText to look for, case-insensitively, in the email address or the name.
-
user_typestringOnly accounts with this role, for example
default. -
auth_sourcestringOnly accounts that sign in this way:
localorentra. -
is_activebooleantruereturns active accounts only; any other value, such asfalse, returns deactivated accounts only. -
date_fromstringOnly accounts created on or after this date, as
YYYY-MM-DD. An invalid date is ignored. -
date_tostringOnly accounts created up to the start (00:00) of this date, as
YYYY-MM-DD. To include the whole day, pass the next day. An invalid date is ignored.
Returns
Returns a page of user objects and the pagination details.
curl "$VDF_BASE_URL/api/admin/users?page=1&limit=50" \
-H "Authorization: Bearer $VDF_ACCESS_TOKEN" const response = await fetch(`${process.env.VDF_BASE_URL}/api/admin/users?page=1&limit=50`, {
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/admin/users",
headers={"Authorization": f"Bearer {os.environ['VDF_ACCESS_TOKEN']}"},
params={
"page": 1,
"limit": 50,
},
timeout=30,
)
response.raise_for_status()
data = response.json() {
"success": true,
"users": [
{
"id": 43,
"email": "grace@example.com",
"name": "Grace Hopper",
"user_type": "expert",
"is_active": true,
"is_superadmin": false,
"auth_source": "entra",
"role_locked": false,
"last_role_sync_at": "2026-09-03T07:58:41.905126+00:00",
"created_at": "2026-09-02T08:15:27.118402+00:00",
"updated_at": "2026-09-02T08:15:27.118402+00:00"
},
{
"id": 42,
"email": "ada@example.com",
"name": "Ada Lovelace",
"user_type": "default",
"is_active": true,
"is_superadmin": false,
"auth_source": "local",
"role_locked": false,
"last_role_sync_at": null,
"created_at": "2026-09-01T09:30:00.412893+00:00",
"updated_at": "2026-09-01T09:30:00.412893+00:00"
}
],
"total": 2,
"page": 1,
"limit": 50,
"pages": 1
} Create a user
POST /api/admin/users
Creates a user account that signs in with a password.
Creates an account that signs in with the password you set. The email address is stored in lower case. Accounts created by an administrator are marked as verified (email_verified is true), so there is no confirmation step.
The new user is sent a welcome email that doesn't contain the password, so share the password with them separately. A welcome email that can't be sent doesn't fail the request.
Creating an active account needs a free licence seat where your deployment enforces a limit on active users.
- Authentication
- Bearer token How it works
- Permission
- Requires an administrator account.
Body parameters application/json
-
emailstring RequiredThe user's email address, at most 120 characters. It must not already belong to an account, in any letter case.
-
passwordstring RequiredThe user's password: at least 6 characters and at most 72 bytes.
-
namestringThe user's display name, at most 120 characters. Defaults to the part of the email address before the
@. -
user_typestringThe user's role. Assigning the administrator role makes the user an administrator, with
is_superadminset totrue.adminis an ordinary role and doesn't give access to administration endpoints.Possible values-
default -
explorative -
expert -
admin -
superadmin
-
-
is_activebooleanSet to
falseto create the account deactivated. A deactivated account can't sign in and doesn't use a licence seat.
Returns
Returns the new user object.
Errors
- 400
emailis missing, longer than 120 characters or not a valid address;nameis longer than 120 characters;passwordis missing, shorter than 6 characters or longer than 72 bytes; oruser_typeis not a known role. - 402 The licence doesn't currently allow adding users.
- 403 Your deployment enforces a limit on active users and no seats remain. The response's
errorisUSER_LIMIT_REACHED, andseatsgives the numbers. - 409 An account with this email address already exists.
curl -X POST "$VDF_BASE_URL/api/admin/users" \
-H "Authorization: Bearer $VDF_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"email": "katherine@example.com",
"name": "Katherine Johnson",
"password": "<PASSWORD>",
"user_type": "expert"
}' const response = await fetch(`${process.env.VDF_BASE_URL}/api/admin/users`, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.VDF_ACCESS_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
email: 'katherine@example.com',
name: 'Katherine Johnson',
password: '<PASSWORD>',
user_type: 'expert',
}),
});
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/admin/users",
headers={"Authorization": f"Bearer {os.environ['VDF_ACCESS_TOKEN']}"},
json={
"email": "katherine@example.com",
"name": "Katherine Johnson",
"password": "<PASSWORD>",
"user_type": "expert",
},
timeout=30,
)
response.raise_for_status()
data = response.json() {
"success": true,
"user": {
"id": 44,
"email": "katherine@example.com",
"name": "Katherine Johnson",
"user_type": "expert",
"is_active": true,
"is_superadmin": false,
"email_verified": true,
"email_verified_at": "2026-09-04T10:12:45.203117+00:00",
"created_at": "2026-09-04T10:12:45.203117+00:00"
},
"message": "User created successfully"
} Retrieve a user
GET /api/admin/users/{user_id}
Returns one user account.
Returns the account with this ID, whether it is active or deactivated.
- Authentication
- Bearer token How it works
- Permission
- Requires an administrator account.
Path parameters
-
user_idinteger RequiredThe user's ID.
Returns
Returns the user object.
Errors
- 404 No account has this ID.
curl "$VDF_BASE_URL/api/admin/users/42" \
-H "Authorization: Bearer $VDF_ACCESS_TOKEN" const response = await fetch(`${process.env.VDF_BASE_URL}/api/admin/users/42`, {
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/admin/users/42",
headers={"Authorization": f"Bearer {os.environ['VDF_ACCESS_TOKEN']}"},
timeout=30,
)
response.raise_for_status()
data = response.json() {
"success": true,
"user": {
"id": 42,
"email": "ada@example.com",
"name": "Ada Lovelace",
"user_type": "default",
"is_active": true,
"is_superadmin": false,
"email_verified": true,
"email_verified_at": "2026-09-01T09:30:00.412893+00:00",
"created_at": "2026-09-01T09:30:00.412893+00:00"
}
} Update a user
PUT /api/admin/users/{user_id}
Changes a user's details, role, password or active state.
Changes only the fields you send. Reactivating a deactivated account (setting is_active to true) needs a free licence seat where your deployment enforces a limit on active users. The request is refused if it would deactivate the last active administrator who can sign in with a password.
For an account that signs in with Microsoft Entra ID (auth_source is entra), the request is refused if it sets name, email or password. Its role can be changed only when the role is pinned, with role_locked set to true in the same request or already; otherwise the directory group role mappings would set the role again at the user's next Microsoft sign-in.
- Authentication
- Bearer token How it works
- Permission
- Requires an administrator account.
Path parameters
-
user_idinteger RequiredThe user's ID.
Body parameters application/json
-
namestringThe new display name.
-
emailstringThe new email address. It must not belong to another account.
-
passwordstringA new password. An empty value leaves the password unchanged.
-
user_typestringThe new role, written exactly as one of the values below. Assigning the administrator role makes the user an administrator; assigning any other role removes administrator access.
Possible values-
default -
explorative -
expert -
admin -
superadmin
-
-
is_activebooleanfalsedeactivates the account;truereactivates it. -
role_lockedbooleantruepins the role, so that signing in with Microsoft doesn't change it through directory group role mappings.
Returns
Returns the updated user object.
Errors
- 400
user_typeis not exactly one of the listed roles. - 402 Reactivation was refused because the licence doesn't currently allow adding users.
- 403 Reactivation was refused because your deployment enforces a limit on active users and no seats remain. The response's
errorisUSER_LIMIT_REACHED. - 404 No account has this ID.
- 409 Another account uses this email address; the account signs in with Microsoft Entra ID and the request sets
name,emailorpassword, or changes its role without pinning it; or the request would deactivate the last active administrator who can sign in with a password.
curl -X PUT "$VDF_BASE_URL/api/admin/users/42" \
-H "Authorization: Bearer $VDF_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"user_type": "superadmin"
}' const response = await fetch(`${process.env.VDF_BASE_URL}/api/admin/users/42`, {
method: 'PUT',
headers: {
Authorization: `Bearer ${process.env.VDF_ACCESS_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
user_type: 'superadmin',
}),
});
if (!response.ok) throw new Error(`Request failed with status ${response.status}`);
const data = await response.json(); import os
import requests
response = requests.put(
f"{os.environ['VDF_BASE_URL']}/api/admin/users/42",
headers={"Authorization": f"Bearer {os.environ['VDF_ACCESS_TOKEN']}"},
json={
"user_type": "superadmin",
},
timeout=30,
)
response.raise_for_status()
data = response.json() {
"success": true,
"user": {
"id": 42,
"email": "ada@example.com",
"name": "Ada Lovelace",
"user_type": "superadmin",
"is_active": true,
"is_superadmin": true,
"email_verified": true,
"email_verified_at": "2026-09-01T09:30:00.412893+00:00",
"created_at": "2026-09-01T09:30:00.412893+00:00"
},
"message": "User updated successfully"
} Deactivate a user
DEL /api/admin/users/{user_id}
Deactivates a user account without deleting it.
Sets the account's is_active to false. The account, its data, its role and its group memberships are kept, and the user can no longer sign in. A deactivated account doesn't count towards the licence's active users. Reactivate it with Update a user.
The last active administrator who can sign in with a password can't be deactivated.
- Authentication
- Bearer token How it works
- Permission
- Requires an administrator account.
Path parameters
-
user_idinteger RequiredThe user's ID.
Returns
Returns a success marker.
Errors
- 404 No account has this ID.
- 409 The account is the last active administrator who can sign in with a password.
curl -X DELETE "$VDF_BASE_URL/api/admin/users/42" \
-H "Authorization: Bearer $VDF_ACCESS_TOKEN" const response = await fetch(`${process.env.VDF_BASE_URL}/api/admin/users/42`, {
method: 'DELETE',
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.delete(
f"{os.environ['VDF_BASE_URL']}/api/admin/users/42",
headers={"Authorization": f"Bearer {os.environ['VDF_ACCESS_TOKEN']}"},
timeout=30,
)
response.raise_for_status()
data = response.json() {
"success": true,
"message": "User deleted successfully"
} Send a password reset email
POST /api/admin/users/{user_id}/resend-password
Sends the user an email with a link to set a new password. The link expires after 48 hours and can be used once. Use it when a user has forgotten their password, or to send again an invitation that didn't arrive.
- Authentication
- Bearer token How it works
- Permission
- Requires an administrator account.
Path parameters
-
user_idinteger RequiredThe user's ID.
Returns
Returns a confirmation naming the address the email was sent to.
Errors
- 404 No account has this ID.
- 500 The email couldn't be sent, for example because outgoing email isn't configured.
curl -X POST "$VDF_BASE_URL/api/admin/users/42/resend-password" \
-H "Authorization: Bearer $VDF_ACCESS_TOKEN" const response = await fetch(`${process.env.VDF_BASE_URL}/api/admin/users/42/resend-password`, {
method: 'POST',
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.post(
f"{os.environ['VDF_BASE_URL']}/api/admin/users/42/resend-password",
headers={"Authorization": f"Bearer {os.environ['VDF_ACCESS_TOKEN']}"},
timeout=30,
)
response.raise_for_status()
data = response.json() {
"success": true,
"message": "Password reset email sent to ada@example.com"
} Unlink a user from single sign-on
POST /api/admin/users/{user_id}/unlink-sso
Detaches a user from their Microsoft Entra ID identity so that they sign in with their password.
Removes the account's link to its Microsoft Entra ID identity and sets auth_source to local. The account then signs in with its password, and its name, email address and password can be changed with Update a user. The account must already have a password; otherwise the request is refused, because the user would be left with no way to sign in.
Unlinking doesn't block Microsoft sign-in: if the person signs in with Microsoft again, the deployment can link the account again by its email address.
- Authentication
- Bearer token How it works
- Permission
- Requires an administrator account.
Path parameters
-
user_idinteger RequiredThe user's ID.
Returns
Returns a success marker.
Errors
- 404 No account has this ID.
- 409 The account has no password.
curl -X POST "$VDF_BASE_URL/api/admin/users/46/unlink-sso" \
-H "Authorization: Bearer $VDF_ACCESS_TOKEN" const response = await fetch(`${process.env.VDF_BASE_URL}/api/admin/users/46/unlink-sso`, {
method: 'POST',
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.post(
f"{os.environ['VDF_BASE_URL']}/api/admin/users/46/unlink-sso",
headers={"Authorization": f"Bearer {os.environ['VDF_ACCESS_TOKEN']}"},
timeout=30,
)
response.raise_for_status()
data = response.json() {
"success": true,
"message": "SSO identity unlinked"
} Retrieve bulk creation limits
GET /api/admin/users/bulk/preflight
Returns the free licence seats, whether invitation emails can be sent, and the most rows a batch may contain.
Call this before Create users in bulk to size a batch.
seatsis the licence position:maxUsersis the limit on active users (nullwhen there is none),activeUsersthe number of active accounts,seatsRemainingthe free seats (nullwhen there is no limit), andenforcedwhether a limit applies.emailConfiguredsays whether outgoing email is set up, which invitation emails need. When it isfalse,emailIssuedescribes what is missing.maxRowsis the largest number of rows a batch may contain.
- Authentication
- Bearer token How it works
- Permission
- Requires an administrator account.
Parameters
No parameters.
Returns
Returns the seat position, the email readiness and the row limit.
Errors
- 402 The licence doesn't currently allow adding users.
curl "$VDF_BASE_URL/api/admin/users/bulk/preflight" \
-H "Authorization: Bearer $VDF_ACCESS_TOKEN" const response = await fetch(`${process.env.VDF_BASE_URL}/api/admin/users/bulk/preflight`, {
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/admin/users/bulk/preflight",
headers={"Authorization": f"Bearer {os.environ['VDF_ACCESS_TOKEN']}"},
timeout=30,
)
response.raise_for_status()
data = response.json() {
"success": true,
"seats": {
"maxUsers": 50,
"activeUsers": 12,
"seatsRemaining": 38,
"enforced": true
},
"emailConfigured": true,
"emailIssue": null,
"maxRows": 500
} Create users in bulk
POST /api/admin/users/bulk
Validates a batch of up to 500 accounts and, unless it is a dry run, creates them together.
Validates every row, then creates the accounts in a single transaction: if the batch fails, none of them are created. Set dry_run to see the outcome without creating anything. A bad row doesn't fail the batch; it is reported as invalid with a reason, and the other rows go ahead.
Each row gets a status:
create: a new account is created (in a dry run, would be created). It is active, signs in with a password and has its email address marked as verified. A row without apasswordcreates the account without one, and the user sets it from the invitation email described below.existing: an account with this email address already exists. The account is left as it is, except that it is added to the row's groups.invalid: the row can't be used, andreasonsays why. A row with the same email address as an earlier row is invalid.
When the licence limits active users, the whole batch must fit in the free seats: otherwise nothing is created and the request fails with 403. A dry run reports the shortfall in seatShortfall instead.
After the accounts are created, and unless send_invites is false, each new user is emailed: a welcome email when the row had a password, or a link to set a password, valid for 48 hours, when it didn't. Each row's inviteStatus reports the outcome: sent, failed, not_configured when outgoing email isn't set up, or skipped when sending stopped after three failures in a row. Send a missing email later with Send a password reset email.
- Authentication
- Bearer token How it works
- Permission
- Requires an administrator account.
Body parameters application/json
-
rowsarray of objects RequiredThe accounts to create, from 1 to 500 rows.
Show child parameters Hide child parameters
-
emailstringThe user's email address, at most 120 characters. A row without a valid address is invalid.
-
namestringThe user's display name, at most 120 characters. Defaults to the part of the email address before the
@, with a warning. -
passwordstringAn initial password: at least 6 characters and at most 72 bytes. Omit it to email the user a link to choose their own.
-
rolestringThe account's role, from the values listed under
user_typein Create a user. Defaults todefault_role. The administrator role can't be assigned in bulk; a row that asks for it is invalid. -
groupsstringPermission groups to join, by slug or name in any letter case, separated by
;or|. An array of names is also accepted. Unknown and inactive groups are skipped with a warning.
-
-
default_rolestringThe role for rows that don't name one. The administrator role isn't allowed. Also accepted as
defaultRole. -
group_idsarray of integersIDs of permission groups that every usable row joins, including rows for existing accounts. Each group must exist and be active. Also accepted as
groupIds. -
dry_runbooleanValidate and report without creating accounts, adding group members or sending email. Also accepted as
dryRun. -
send_invitesbooleanEmail each new user once the accounts are created. Also accepted as
sendInvites.
Returns
Returns the outcome of the batch and of each row.
Errors
- 400
rowsis missing, empty, longer than 500 rows or contains something other than objects (the response includesmaxRows);default_roleis unknown or the administrator role;group_idsisn't a list of integers; or a group ingroup_idsis inactive. - 402 The licence doesn't currently allow adding users. Dry runs fail in the same way.
- 403 The licence limits active users and the batch doesn't fit in the free seats. Nothing was created; the response's
errorisUSER_LIMIT_REACHED, andseatsgives the numbers. - 404 A group in
group_idsdoesn't exist.
curl -X POST "$VDF_BASE_URL/api/admin/users/bulk" \
-H "Authorization: Bearer $VDF_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"rows": [
{
"email": "dorothy@example.com",
"name": "Dorothy Vaughan",
"role": "expert",
"groups": "engineering; research"
},
{
"email": "ada@example.com",
"name": "Ada Lovelace"
},
{
"email": "not-an-email"
}
],
"default_role": "default",
"group_ids": [
7
]
}' const response = await fetch(`${process.env.VDF_BASE_URL}/api/admin/users/bulk`, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.VDF_ACCESS_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
rows: [
{
email: 'dorothy@example.com',
name: 'Dorothy Vaughan',
role: 'expert',
groups: 'engineering; research',
},
{
email: 'ada@example.com',
name: 'Ada Lovelace',
},
{
email: 'not-an-email',
},
],
default_role: 'default',
group_ids: [7],
}),
});
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/admin/users/bulk",
headers={"Authorization": f"Bearer {os.environ['VDF_ACCESS_TOKEN']}"},
json={
"rows": [
{
"email": "dorothy@example.com",
"name": "Dorothy Vaughan",
"role": "expert",
"groups": "engineering; research",
},
{
"email": "ada@example.com",
"name": "Ada Lovelace",
},
{
"email": "not-an-email",
},
],
"default_role": "default",
"group_ids": [7],
},
timeout=30,
)
response.raise_for_status()
data = response.json() {
"success": true,
"dryRun": false,
"counts": {
"total": 3,
"create": 1,
"existing": 1,
"invalid": 1,
"warnings": 1
},
"seats": {
"maxUsers": 50,
"activeUsers": 12,
"seatsRemaining": 38,
"enforced": true
},
"seatShortfall": 0,
"emailConfigured": true,
"groups": [
{
"id": 7,
"slug": "engineering",
"name": "Engineering",
"added": 1
}
],
"rows": [
{
"index": 0,
"email": "dorothy@example.com",
"name": "Dorothy Vaughan",
"role": "expert",
"status": "create",
"reason": null,
"warnings": [
"unknown group 'research'"
],
"groups": [
{
"id": 7,
"slug": "engineering",
"name": "Engineering"
}
],
"unknownGroups": [
"research"
],
"userId": 45,
"invite": "set-password",
"inviteStatus": "sent",
"inviteError": null
},
{
"index": 1,
"email": "ada@example.com",
"name": "Ada Lovelace",
"role": "default",
"status": "existing",
"reason": "existing account left unchanged",
"warnings": [],
"groups": [
{
"id": 7,
"slug": "engineering",
"name": "Engineering"
}
],
"unknownGroups": [],
"userId": 42,
"invite": "none",
"inviteStatus": null,
"inviteError": null
},
{
"index": 2,
"email": "not-an-email",
"name": "",
"role": "",
"status": "invalid",
"reason": "invalid email address",
"warnings": [],
"groups": [],
"unknownGroups": [],
"userId": null,
"invite": "none",
"inviteStatus": null,
"inviteError": null
}
]
}