View as Markdown

Users

Users are the people who can sign in to your tenant: advisors, principals and family members. A user is an admin or a user. What a user sees comes from their access grants, and what they may do there from their groups; a lens is no access point and is shared on the lens. An admin sees and does everything. Machine access hangs off users too: see credentials for API keys, which act as their owner.

Reading the tenant's people is admin work: GET /v1/users answers 403 forbidden to a user, and so does GET /v1/users/{id} for anyone but the user themselves. An API key reads as its owner. Reads need access:read. Creating, updating and deleting need access:write and an admin: a person, or an API key whose owner is an admin; anyone else gets 403 forbidden.

Data model

idstring

Stable identifier for the user. A UUID — treat it as opaque.

first_namestring

Given name, as stored. It may hold a space ("Lars Christian").

last_namestring

Family name, as stored.

contact_emailstring

The user's contact address — also where invitations land. Shown as stored, so its casing can differ from the sign-in address; "" for a user migrated without a contact address.

rolestring

admin or user. Admins administer the tenant; what a user may see comes from their access. Aleta staff are not users of your tenant and are never listed. Machine principals (credentials and API keys) are not users either: they are never listed and their id reads as 404 here.

groupsarray

The groups the user is in, each {id, name}, by name. See groups for what each gives. Empty for an admin.

access_pointsarray

The user's live access points, each {kind, id}: kind is tenant (id null), client, party, entity, cash_account or custody_account. A lens is never one. Empty for an admin, who sees everything.


GET/v1/users

List users

Returns the tenant's users in the standard v1 list envelope, oldest first. Admin only: a user, or an API key a user owns, gets 403 forbidden.

Query parameters

limitinteger

Page size. Defaults to 100, capped at 1 000.

cursorstring

Opaque cursor from the previous page's next_cursor. Omit for the first page.

An unknown query parameter is a 400.

Request
GET/v1/users
curl -G https://api.aleta.ai/v1/users \
  -H "Authorization: Bearer {access_token}" \
  -d limit=2
Response
{
  "data": [
    {
      "id": "019eb68d-5b44-7491-911a-ac66602675a7",
      "first_name": "Jane",
      "last_name": "Doe",
      "contact_email": "jane.doe@example.com",
      "role": "admin",
      "groups": [],
      "access_points": []
    }
  ],
  "next_cursor": null,
  "limit": 2
}

POST/v1/users

Create a user

Creates a user. All fields are required. client_id is not stored and gives no access: a user belongs to the tenant. A new user sees nothing until you give access with POST /v1/access-grants, or invite them with POST /v1/invites, which carries their groups and access points. Returns 201 with the created resource. A new user gets the role user. Creating a user who is already a member of the firm leaves their existing role untouched. The address of an Aleta staff member, or of a login that is deleted or suspended, returns 409 conflict ("a user with this email cannot be added"). A non-empty read_access_to_lenses (the v2 field) returns a 400 problem; other unknown body fields are ignored. This route has no dry run and takes no Idempotency-Key: ?dry_run=true is a 400 and creates nothing. It is retiring: invite people with POST /v1/invites.

Body parameters

client_idstring

Required for compatibility; not stored.

first_namestring

Given name. Required.

last_namestring

Family name. Required.

contact_emailstring

Contact address. Required. Stored lower-cased; a value without @ is a 400.

Request
POST/v1/users
curl -X POST https://api.aleta.ai/v1/users \
  -H "Authorization: Bearer {access_token}" \
  -H "Content-Type: application/json" \
  -d '{
    "client_id": "3f600564-957b-4aab-96ed-fd0118cfd805",
    "first_name": "Jane",
    "last_name": "Doe",
    "contact_email": "jane.doe@example.com"
  }'
Response
{
  "id": "019eb68d-5b44-7491-911a-ac66602675a7",
  "first_name": "Jane",
  "last_name": "Doe",
  "contact_email": "jane.doe@example.com",
  "role": "user",
  "groups": [],
  "access_points": []
}

GET/v1/users/{id}

Retrieve a user

Fetch a single user by id. An admin reads anyone; a user reads only themselves and gets 403 forbidden for any other id. Returns the bare object, or a 404 problem when the id doesn't exist for this tenant or belongs to Aleta staff or a machine.

Request
GET/v1/users/{id}
curl https://api.aleta.ai/v1/users/019eb68d-5b44-7491-911a-ac66602675a7 \
  -H "Authorization: Bearer {access_token}"
Response
{
  "id": "019eb68d-5b44-7491-911a-ac66602675a7",
  "first_name": "Jane",
  "last_name": "Doe",
  "contact_email": "jane.doe@example.com",
  "role": "user",
  "groups": [
    { "id": "019f2a10-7c1e-7b55-9d8e-4b2f0f1c2a11", "name": "Advisers" }
  ],
  "access_points": [
    { "kind": "client", "id": "019e8d3c-1f2a-7e44-8a10-2b9c4d5e6f70" }
  ]
}

PATCH/v1/users/{id}

Update a user

Partially updates a user's name and sign-in e-mail. It never changes access: use access grants and groups. Any other field, read_access_to_lenses included, returns a 400 problem. Changing first_name, last_name or contact_email returns 409 conflict when the user also belongs to another firm or is Aleta staff; such a user's name and email are managed by the user, not by one firm. A user from the legacy platform answers 409 legacy_managed until your move to v1 is done, in a dry run too. Takes ?dry_run=true and Idempotency-Key. Returns the updated user, or a 404 problem when the id doesn't exist for this tenant or belongs to Aleta staff.

Body parameters

first_namestring

New given name.

last_namestring

New family name.

contact_emailstring

New contact address. Stored lower-cased; a value without @ is a 400.

Request
PATCH/v1/users/{id}
curl -X PATCH https://api.aleta.ai/v1/users/019eb68d-5b44-7491-911a-ac66602675a7 \
  -H "Authorization: Bearer {access_token}" \
  -H "Content-Type: application/json" \
  -d '{
    "first_name": "Jane Marie"
  }'
Response
{
  "id": "019eb68d-5b44-7491-911a-ac66602675a7",
  "first_name": "Jane Marie",
  "last_name": "Doe",
  "contact_email": "jane.doe@example.com",
  "role": "user",
  "groups": [],
  "access_points": [
    { "kind": "client", "id": "019e8d3c-1f2a-7e44-8a10-2b9c4d5e6f70" }
  ]
}

DELETE/v1/users/{id}

Delete a user

Removes a user from this tenant: the user's access here ends at once. The keys the user owns in this tenant are revoked with it and show as revoked in /v1/credentials; a repeat delete revokes any that are still live. Returns 204 on success, 404 if the id doesn't exist for this tenant or belongs to Aleta staff. Takes ?dry_run=true and Idempotency-Key; a repeat answers 204 again, with or without the key, for a user who had access here. The tenant's last admin answers 409 conflict: make another user admin first. A user from the legacy platform answers 409 legacy_managed until your move to v1 is done. Both refusals come in a dry run too.

When the user has no access left in any tenant, the login is deleted softly: it can no longer sign in. This call erases none of the person's details; the user's id stays in the access history.

Request
DELETE/v1/users/{id}
curl -X DELETE https://api.aleta.ai/v1/users/019ebe92-52b2-7da4-a18c-d641c85c1ff0 \
  -H "Authorization: Bearer {access_token}"

GET/v1/users/me

Retrieve the current principal

Who is calling: a person, an API key or Aleta's fleet key. Needs platform:read. An API key acts as its owner, so id, email, role, groups and access_points are its owner's, and credential names the key.

idstring

The user the call acts as: the person, or the key's owner. A key without an owner is its own machine principal.

principal_kindstring

How the caller came in: user (a person), machine (an API key or client credential) or fleet (Aleta's fleet key).

emailstring or null

That user's sign-in address; null when the login has none.

tenant_idstring

The tenant the call is on.

rolestring or null

admin or user; null for Aleta (the fleet key or Aleta staff), which is no user of your tenant.

credentialobject

{id, owner_user_id} of the key the call came through; null for a person. owner_user_id is null for a key without an owner.

groupsarray

The groups the caller is in, each {id, name}. Empty for an admin and for Aleta.

access_pointsarray

The caller's access points, each {kind, id}. An admin and Aleta have one: {"kind": "tenant", "id": tenant_id}.

scopesarray

The token's scopes: the ceiling of what any call may do.

Request
GET/v1/users/me
curl https://api.aleta.ai/v1/users/me \
  -H "Authorization: Bearer {access_token}"
Response
{
  "id": "019eb68d-5b44-7491-911a-ac66602675a7",
  "principal_kind": "machine",
  "email": "jane.doe@example.com",
  "tenant_id": "3f600564-957b-4aab-96ed-fd0118cfd805",
  "role": "admin",
  "credential": {
    "id": "01a150c8-1dc2-7722-97c5-37f782295429",
    "owner_user_id": "019eb68d-5b44-7491-911a-ac66602675a7"
  },
  "groups": [],
  "access_points": [
    { "kind": "tenant", "id": "3f600564-957b-4aab-96ed-fd0118cfd805" }
  ],
  "scopes": ["platform:read", "structure:read"]
}