View as Markdown

Invites

An invite is how a new person joins your tenant. It names the person's e-mail, their role and, for a user, the groups they join and the access points they get. The person accepts from the invitation e-mail and signs in; from then on the invite reads accepted and the person holds what it carried.

Invites are admin work, reads too: a user gets 403 forbidden. An API key acts as its owner, so a key an admin owns may invite. The token needs access:read to read and access:write to invite or cancel.

The invite

idstring

Stable identifier. A UUID.

emailstring

The invited address, lower case.

rolestring

admin or user: the role the person gets.

statusstring

pending, accepted, expired or cancelled.

deliverystring

workos when the invitation e-mail was sent, not_sent when the tenant has no sign-in set up yet.

group_idsarray

The groups the person joins on acceptance.

access_pointsarray

The access points the person gets on acceptance, each {kind, id}: tenant (id is null), client, party, entity, cash_account or custody_account.

expires_atstring

When the invite stops being acceptable (ISO 8601, UTC).

invited_by_user_idstring

The admin who invited. The person's grants are given in this admin's name.

created_atstring

When the invite was made.

accepted_atstring

When the person accepted; null until then.

cancelled_atstring

When the invite was cancelled; null unless it was.

cancel_reasonstring

Why it was cancelled: your reason, replaced by a newer invite to the same address, or expired; null when there is none.


GET/v1/invites

List invites

The tenant's invites, oldest first, in the standard v1 list envelope. An invalid cursor is a 400.

Query parameters

statusstring

Only invites in this status: pending, accepted, expired or cancelled. Anything else is a 400.

limitinteger

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

cursorstring

Opaque cursor from the previous page's next_cursor.

Request
GET/v1/invites
curl -G https://api.aleta.ai/v1/invites \
  -H "Authorization: Bearer {access_token}" \
  -d status=pending
Response
{
  "data": [
    {
      "id": "019f3b2e-6a1d-7c40-9f11-0d2e3c4b5a69",
      "email": "jane.doe@example.com",
      "role": "user",
      "status": "pending",
      "delivery": "workos",
      "group_ids": ["019f2a10-7c1e-7b55-9d8e-4b2f0f1c2a11"],
      "access_points": [
        { "kind": "client", "id": "019e8d3c-1f2a-7e44-8a10-2b9c4d5e6f70" }
      ],
      "expires_at": "2026-11-17T09:12:44.000000Z",
      "invited_by_user_id": "019eb68d-5b44-7491-911a-ac66602675a7",
      "created_at": "2026-10-18T09:12:44.000000Z",
      "accepted_at": null,
      "cancelled_at": null,
      "cancel_reason": null
    }
  ],
  "next_cursor": null,
  "limit": 100
}

POST/v1/invites

Invite a person

Sends the invitation e-mail and answers 201 with the invite. When the tenant has no sign-in set up, nothing is sent and the invite is stored with delivery: not_sent. Takes ?dry_run=true (nothing is sent) and Idempotency-Key, and is logged once. A newer invite to the same address replaces an expired one, and an unsent one when this invite is sent; any other open invite to the address answers 409. A person who is already a member answers 409: give them access with POST /v1/access-grants. When the invitation cannot be sent, the answer is 502 and nothing is stored.

Body parameters

emailstring

The person's address. Required.

rolestring

admin or user. Defaults to user.

group_idsarray

Groups the person joins. Open groups of the tenant only. An admin is in no group: 400.

access_pointsarray

Access points the person gets, each {kind, id}; a tenant access point has no id. An admin sees everything: 400. At most 100 groups and access points together.

expires_in_daysinteger

1 to 30. Defaults to 30.

Any other field is a 400.

Request
POST/v1/invites
curl -X POST https://api.aleta.ai/v1/invites \
  -H "Authorization: Bearer {access_token}" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "jane.doe@example.com",
    "role": "user",
    "group_ids": ["019f2a10-7c1e-7b55-9d8e-4b2f0f1c2a11"],
    "access_points": [{ "kind": "client", "id": "019e8d3c-1f2a-7e44-8a10-2b9c4d5e6f70" }]
  }'
201 Created
{
  "id": "019f3b2e-6a1d-7c40-9f11-0d2e3c4b5a69",
  "email": "jane.doe@example.com",
  "role": "user",
  "status": "pending",
  "delivery": "workos",
  "group_ids": ["019f2a10-7c1e-7b55-9d8e-4b2f0f1c2a11"],
  "access_points": [
    { "kind": "client", "id": "019e8d3c-1f2a-7e44-8a10-2b9c4d5e6f70" }
  ],
  "expires_at": "2026-11-17T09:12:44.000000Z",
  "invited_by_user_id": "019eb68d-5b44-7491-911a-ac66602675a7",
  "created_at": "2026-10-18T09:12:44.000000Z",
  "accepted_at": null,
  "cancelled_at": null,
  "cancel_reason": null
}

GET/v1/invites/{id}

Retrieve an invite

One invite, or a 404 problem for an id that is no invite of the tenant.

Request
GET/v1/invites/{id}
curl https://api.aleta.ai/v1/invites/019f3b2e-6a1d-7c40-9f11-0d2e3c4b5a69 \
  -H "Authorization: Bearer {access_token}"

DELETE/v1/invites/{id}

Cancel an invite

Withdraws the invitation, so it can no longer be accepted, marks the invite cancelled and answers 204, also for an invite that is cancelled already; the invite stays readable. ?reason= is kept as cancel_reason. Takes ?dry_run=true (nothing is withdrawn) and Idempotency-Key. 404 for an id that is no invite of the tenant. An accepted invite answers 409: remove the person's access instead. When the invitation cannot be withdrawn, the answer is 502 and the invite stays open.

Request
DELETE/v1/invites/{id}
curl -X DELETE "https://api.aleta.ai/v1/invites/019f3b2e-6a1d-7c40-9f11-0d2e3c4b5a69?reason=wrong%20address" \
  -H "Authorization: Bearer {access_token}"