Users
Manage user accounts. Standard users can see who the users of their organization are and update their own account. Administrative endpoints allow creating, archiving, and managing other user accounts.
All endpoints require authentication via X-API-Key header and the appropriate scope.
User Object
GET /users and GET /users/:id answer each user as exactly three fields:
{
"username": "Jane Smith",
"email": "jane@example.com",
"role": "developer"
}
| Field | Type | Description |
|---|---|---|
username | string | The user's name |
email | string | The user's email address |
role | string or null | The user's platform role: admin, developer or app. null for a user who has none |
Nothing else about a user is returned: no id, profile or organization. The id of a member of your organization is in GET /organizations/:id/members; the id of a user you create is in the POST /users response. Your own id, organization and API key are in GET /me.
GET /api/v1/users
List the users in your organization, each as a user object.
Scope: users:read
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
q | string | No | Case-insensitive match on email address or name (username is not searched) |
archived | boolean | No | true returns only archived users; otherwise archived users are left out |
active | boolean | No | Filter by active status |
limit | integer | No | Number of results to return (default: 50, max: 200) |
cursor | string | No | meta.nextCursor of the previous page; omit for the first page |
sort | string | No | Sort field, prefixed with - for descending (default: -createdAt) |
Response 200 OK
{
"data": [
{ "username": "Jane Smith", "email": "jane@example.com", "role": "developer" }
],
"meta": {
"total": 25,
"limit": 50,
"nextCursor": null,
"requestId": "req_abc123"
}
}
GET /api/v1/me
Who you are authenticated as: your user (a user object plus its _id), your organization and your role in it, and the API key the call used (absent when you call with a session). It needs no scope, so it is the simplest way to check a key works.
Response 200 OK
{
"data": {
"_id": "fJHgtbiLZyMkFqJC6",
"username": "Jane Smith",
"email": "jane@example.com",
"role": "developer",
"organization": { "_id": "org_xyz", "role": "owner", "isMultiTenant": false, "isSolo": false },
"apiKey": { "_id": "key_abc123", "name": "CI", "keyPrefix": "sk-prod-ab12", "scopes": ["workflows:read"], "expiresAt": null }
},
"meta": { "requestId": "req_abc123" }
}
GET /api/v1/me/github-ssh-keys
List your GitHub SSH keys: id, label, public key and fingerprint (never the private key). A GitHub source (an app's bundleSource, a volume's or project's code) names one of them as sshKeyId. Keys are added in your profile settings. It pages with limit and cursor (Pagination).
Scope: users:read
{
"data": [
{ "_id": "k1", "label": "laptop", "publicKey": "ssh-ed25519 AAAA...", "fingerprint": "SHA256:...", "createdAt": "2026-09-01T10:00:00.000Z" }
],
"meta": { "total": 1, "limit": 50, "nextCursor": null, "requestId": "req_abc123" }
}
PATCH /api/v1/me
Update the currently authenticated user's own profile.
Scope: users:write
Request Body
{
"name": "Jane M. Smith",
"stanPersonality": "friendly"
}
| Field | Type | Required | Description |
|---|---|---|---|
name | string | No | Display name |
photo | string | No | Profile photo, as a URL or base64 image data |
stanPersonality | string | No | STAN's personality: professional, friendly, or concise |
Any other field returns 400 validation-error.
Response 200 OK
You, as GET /api/v1/me shows you.
DELETE /api/v1/me
Delete your own account: it is deactivated at once, its API keys are revoked, and its data is erased on schedule.
Scope: users:write
Response 200 OK: what deleting the account removed: { assetAction, assetResult }, assetAction delete and assetResult what was deleted (both null when the account was already deleted).
GET /api/v1/users/:id
Get a user by ID as a user object.
Scope: users:read
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | Yes | User ID |
Response 200 OK
{
"data": { "username": "Bob Johnson", "email": "bob@example.com", "role": "app" },
"meta": { "requestId": "req_abc123" }
}
POST /api/v1/users
Create a new user account. Requires administrator privileges.
Scope: users:admin
Request Body
{
"email": "newuser@example.com",
"name": "New User",
"role": "developer",
"sendEmail": true
}
| Field | Type | Required | Description |
|---|---|---|---|
email | string | Yes | User email address. Must be a valid address not already used by another user |
name | string | Yes | Display name |
role | string | No | The user's role: app, developer, or admin (default: app; any other value is treated as app) |
sendEmail | boolean | No | Send a welcome email with a link to set a password, when the platform can send email (default: true). Pass false to send no email and get a temporary password in the response instead |
profile | object | No | Extra profile fields stored on the new user |
There is no password field: the user sets their own password from the welcome email, or signs in with the temporary password. Any field not listed above returns 400 validation-error, as does a missing email or name or an invalid email address. An email that already belongs to a user is refused with 500 internal-error and the message User with this email already exists.
Response 201 Created
{
"data": {
"userId": "user_new001",
"temporaryPassword": null
},
"meta": { "requestId": "req_abc123" }
}
temporaryPassword is the generated password when sendEmail is false, otherwise null.
PATCH /api/v1/users/:id
Update a user account. Requires administrator privileges.
Scope: users:admin
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | Yes | User ID |
Request Body
{
"name": "Updated Name",
"isDeveloper": true,
"active": true
}
| Field | Type | Required | Description |
|---|---|---|---|
name | string | No | Display name |
email | string | No | New email address. It is marked unverified and must be a valid address not used by another user |
active | boolean | No | false deactivates the user |
archived | boolean | No | Archive (true) or unarchive (false) the user without handling their resources; see archive for that |
isAdmin | boolean | No | Sets the user's single role: admin if isAdmin is true, otherwise developer if isDeveloper is true, otherwise app |
isDeveloper | boolean | No | See isAdmin |
isReviewer | boolean | No | Accepted; sending it recomputes the role from isAdmin and isDeveloper as above |
Any other field returns 400 validation-error.
Response 200 OK
The updated user.
GET /api/v1/users/:id/assets
Get a summary of all resources owned by a user. Useful before archiving to understand the impact.
Scope: users:admin
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | Yes | User ID |
Response 200 OK
{
"data": {
"userId": "user_789",
"userName": "Jane Smith",
"userEmail": "jane@example.com",
"organizationId": "org_xyz",
"isSSO": false,
"assets": {
"apps": 3,
"workspaces": 2,
"workflows": 5,
"projects": 1,
"volumes": 0,
"addons": 1,
"datasources": 2,
"prompts": 8,
"abTests": 0,
"routers": 1,
"aiModels": 4,
"guardrails": 2,
"mlModels": 0,
"total": 29
},
"deletion": {
"items": [
{ "label": "Apps", "count": 3 }
],
"total": 29,
"othersWorkspacesInProjects": 0
}
},
"meta": { "requestId": "req_abc123" }
}
deletion previews what the delete asset action would remove: each resource type the user owns with its count, and how many other users' workspaces sit inside projects the user owns (they are deleted with those projects).
POST /api/v1/users/:id/archive
Archive a user account with asset handling. Archived users cannot log in. Admin chooses how to handle owned resources: transfer to another user, transfer to org admin, delete, or leave as-is.
Scope: users:admin
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | Yes | User ID |
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
assetAction | string | No | transfer, transfer-to-admin, delete, or leave-as-is. Defaults to leave-as-is |
transferToUserId | string | Conditional | Required when assetAction is transfer. Must be an active user in the same organization |
Response 200 OK
The user, with what archiving did with their resources (assetResult is null when they were left as they are).
{
"data": {
"username": "Jane Smith",
"email": "jane@example.com",
"role": "developer",
"assetAction": "transfer-to-admin",
"assetResult": {
"success": true,
"transferred": { "apps": 3, "workflows": 5, "total": 29 },
"transferredTo": "admin_user_id"
}
},
"meta": { "requestId": "req_abc123" }
}
POST /api/v1/users/:id/unarchive
Unarchive a previously archived user. Reactivates the account. Resources transferred or deleted during archival are not restored.
Scope: users:admin
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | Yes | User ID |
Response 200 OK
The user, active again.
{ "data": { "username": "Jane Smith", "email": "jane@example.com", "role": "developer" }, "meta": { "requestId": "req_abc123" } }
POST /api/v1/users/:id/reset-password
Reset a user's password. Sends a password reset email or generates a temporary password. Requires administrator privileges.
Scope: users:admin
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | Yes | User ID |
Response 200 OK
The user, with emailSent. When the email could not be sent, resetToken is the token for the reset link (/reset-password/<resetToken>), shown once, to pass on another way.
{ "data": { "username": "Jane Smith", "email": "jane@example.com", "role": "developer", "emailSent": false, "resetToken": "..." }, "meta": { "requestId": "req_abc123" } }