API reference
Team
Members, roles, invitations, ownership and the security audit log of a workspace.
A workspace has members, each with one role: viewer, member, admin or owner (ascending). These endpoints manage the member list and invitations, hand over or close the workspace, and read the security audit log. An API key acts as an admin, so it can manage members and invitations but cannot use the owner-only operations.
The server enforces one permission table for every request. You can read it with GET /team/permissions. See the team and security guide for how the roles are meant to be used.
| Operation | Method and path | Minimum role |
|---|---|---|
| List members | GET /team/members | viewer |
| Update a member | PATCH /team/members/{member_id} | admin |
| Remove a member | DELETE /team/members/{member_id} | admin |
| List invitations | GET /team/invites | admin |
| Invite a person | POST /team/invites | admin |
| Resend an invitation | POST /team/invites/{invite_id}/resend | admin |
| Revoke an invitation | DELETE /team/invites/{invite_id} | admin |
| Transfer ownership | POST /team/transfer-ownership | owner, session only |
| Delete the workspace | DELETE /team/workspace | owner, session only |
| Read the audit log | GET /team/audit | admin |
| Get the permission table | GET /team/permissions | viewer |
Role errors
When the caller's role is too low, the answer is 403 with one of these messages (the role names and the area vary):
Your role (member) can't change team members. It needs admin access; ask an owner or admin of the workspace.
Your role (viewer) can't do this. It needs admin access; ask an owner or admin of the workspace.Two rules apply on top of the table:
- Only an owner may change, disable, remove or invite an owner:
403Only an owner can add, change or remove an owner. - A workspace always keeps one owner who can log in:
400A workspace needs at least one owner. Make someone else an owner first.
Members
List members
GET /team/members
Lists the people who have joined the workspace, in the order they joined. Pending invitations are not included.
Auth: API key or session token. Role: viewer and up.
Request
curl https://api.sevennadi.si/team/members \
-H "Authorization: Bearer vk-0123456789abcdef0123456789abcdef"Response 200 OK
[
{
"id": "0144c9aa-d4fe-4341-99fd-aa27689ab6a0",
"name": "Asha Rao",
"email": "asha@example.test",
"role": "owner",
"status": "active",
"email_verified": true,
"two_factor_enabled": false,
"joined_at": "2026-10-13T07:21:47Z",
"last_active_at": "2026-10-13T09:02:10Z",
"is_you": false
}
]status is active or disabled. last_active_at comes from the member's sessions and may be null. is_you is always false for an API key.
Update a member
PATCH /team/members/{member_id}
Changes a member's role, disables their login, or restores it. Disabling ends all of that member's sessions.
Auth: API key or session token. Role: admin and up.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
member_id | path | string (UUID) | yes | The member's id. |
role | body | string | no | viewer, member, admin or owner. |
disabled | body | boolean | no | true blocks the login and ends its sessions; false restores it. |
Request
curl -X PATCH https://api.sevennadi.si/team/members/7b1c2a90-5a51-4e0e-9c43-0f7f8f1f2a11 \
-H "Authorization: Bearer vk-0123456789abcdef0123456789abcdef" \
-H "Content-Type: application/json" \
-d '{"role": "admin"}'Response 200 OK
{
"id": "7b1c2a90-5a51-4e0e-9c43-0f7f8f1f2a11",
"name": "Ravi Menon",
"email": "ravi@example.test",
"role": "admin",
"status": "active",
"email_verified": true,
"two_factor_enabled": false,
"joined_at": "2026-10-13T08:00:00Z",
"last_active_at": null,
"is_you": false
}Errors
| Status | Message |
|---|---|
| 400 | role must be one of: owner, admin, member, viewer |
| 400 | You can't change your own role. Ask another owner or admin. |
| 400 | You can't disable your own login. |
| 400 | A workspace needs at least one owner. Make someone else an owner first. |
| 403 | Only an owner can add, change or remove an owner. |
| 404 | Member not found |
Remove a member
DELETE /team/members/{member_id}
Removes a member from the workspace and ends their sessions. You cannot remove yourself.
Auth: API key or session token. Role: admin and up.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
member_id | path | string (UUID) | yes | The member's id. |
Request
curl -X DELETE https://api.sevennadi.si/team/members/7b1c2a90-5a51-4e0e-9c43-0f7f8f1f2a11 \
-H "Authorization: Bearer vk-0123456789abcdef0123456789abcdef"Response 200 OK
{"message": "success", "state": "removed"}Errors
| Status | Message |
|---|---|
| 400 | You can't remove yourself from the workspace. |
| 400 | A workspace needs at least one owner. Make someone else an owner first. |
| 403 | Only an owner can add, change or remove an owner. |
| 404 | Member not found |
Invitations
An invitation is an email with a single-use link, valid for 7 days. The invited person completes it with POST /auth/accept-invite. An email address can have a login in only one workspace.
Note: Invitation emails go through the operator's email backend. The default backend stores emails in an outbox table and sends nothing.
List invitations
GET /team/invites
Lists invitations that have not been accepted yet, oldest first.
Auth: API key or session token. Role: admin and up.
Request
curl https://api.sevennadi.si/team/invites \
-H "Authorization: Bearer vk-0123456789abcdef0123456789abcdef"Response 200 OK
[
{
"id": "c1a5f6f0-3f8f-4f1e-8a51-2d1c9d7b6e02",
"email": "ravi@example.test",
"role": "member",
"invited_at": "2026-10-13T08:00:00Z",
"invited_by": "Asha Rao",
"expires_at": "2026-10-20T08:00:00Z",
"expired": false
}
]invited_by is null when the invitation was sent with an API key.
Invite a person
POST /team/invites
Creates a pending member and issues the invitation email.
Auth: API key or session token. Role: admin and up. Only an owner can invite an owner.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
email | body | string | yes | The person's email address. |
role | body | string | no | viewer, member, admin or owner. Default member. |
Idempotency-Key | header | string | no | Makes a retry safe: the same key with the same request returns the first response. See Idempotency. |
Request
curl -X POST https://api.sevennadi.si/team/invites \
-H "Authorization: Bearer vk-0123456789abcdef0123456789abcdef" \
-H "Content-Type: application/json" \
-d '{"email": "ravi@example.test", "role": "member"}'Response 201 Created
{
"id": "c1a5f6f0-3f8f-4f1e-8a51-2d1c9d7b6e02",
"email": "ravi@example.test",
"role": "member",
"invited_at": "2026-10-13T08:00:00Z",
"invited_by": "Asha Rao",
"expires_at": "2026-10-20T08:00:00Z",
"expired": false
}Rate limit: 30 invitations per workspace per hour.
Errors
| Status | Message |
|---|---|
| 400 | A valid email is required |
| 400 | role must be one of: owner, admin, member, viewer |
| 403 | Only an owner can add, change or remove an owner. |
| 409 | This person is already a member of the workspace |
| 409 | This person already has a pending invitation. Resend it instead. |
| 409 | This email address already has a login in another workspace |
| 429 | Too many invitations in the last hour. Try again later. |
Resend an invitation
POST /team/invites/{invite_id}/resend
Issues a fresh link for a pending invitation. The earlier link stops working.
Auth: API key or session token. Role: admin and up.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
invite_id | path | string (UUID) | yes | The invitation's id. |
Request
curl -X POST https://api.sevennadi.si/team/invites/c1a5f6f0-3f8f-4f1e-8a51-2d1c9d7b6e02/resend \
-H "Authorization: Bearer vk-0123456789abcdef0123456789abcdef"Response 200 OK
The invitation, with a new invited_at and expires_at, in the same shape as Invite a person.
Rate limit: 5 resends per invitation per hour.
Errors
| Status | Message |
|---|---|
| 404 | Invitation not found |
| 429 | This invitation was resent several times already. Try again in an hour. |
Revoke an invitation
DELETE /team/invites/{invite_id}
Deletes a pending invitation. Its link stops working.
Auth: API key or session token. Role: admin and up.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
invite_id | path | string (UUID) | yes | The invitation's id. |
Request
curl -X DELETE https://api.sevennadi.si/team/invites/c1a5f6f0-3f8f-4f1e-8a51-2d1c9d7b6e02 \
-H "Authorization: Bearer vk-0123456789abcdef0123456789abcdef"Response 200 OK
{"message": "success", "state": "revoked"}Errors
| Status | Message |
|---|---|
| 404 | Invitation not found |
Owner-only operations
Both operations need a logged-in owner and that owner's password. An API key acts as an admin, so it gets 403.
Transfer ownership
POST /team/transfer-ownership
Makes another member the owner. The caller becomes an admin.
Auth: session token only. Role: owner.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
member_id | body | string (UUID) | yes | The member who becomes the owner. |
password | body | string | yes | The caller's own password. |
Request
curl -X POST https://api.sevennadi.si/team/transfer-ownership \
-H "Authorization: Bearer vs-0123456789abcdef0123456789abcdef" \
-H "Content-Type: application/json" \
-d '{"member_id": "7b1c2a90-5a51-4e0e-9c43-0f7f8f1f2a11", "password": "a-long-passphrase-42"}'Response 200 OK
{
"message": "success",
"state": "transferred",
"owner": {
"id": "7b1c2a90-5a51-4e0e-9c43-0f7f8f1f2a11",
"name": "Ravi Menon",
"email": "ravi@example.test",
"role": "owner",
"status": "active",
"email_verified": true,
"two_factor_enabled": false,
"joined_at": "2026-10-13T08:00:00Z",
"last_active_at": null,
"is_you": false
}
}Errors
| Status | Message |
|---|---|
| 400 | You already own this workspace. |
| 400 | This member's login is disabled. |
| 400 | Current password is incorrect |
| 403 | Your role (admin) can't transfer ownership of the workspace. It needs owner access; ask an owner of the workspace. |
| 404 | Member not found |
| 429 | Too many incorrect passwords. Try again in 15 minutes. |
Delete the workspace
DELETE /team/workspace
Closes the workspace. Every login, session and API key stops working at once. The data is kept (the workspace is marked deleted, not erased).
Auth: session token only. Role: owner.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
confirm | body | string | yes | The workspace's exact name. |
password | body | string | yes | The owner's own password. |
Request
curl -X DELETE https://api.sevennadi.si/team/workspace \
-H "Authorization: Bearer vs-0123456789abcdef0123456789abcdef" \
-H "Content-Type: application/json" \
-d '{"confirm": "Example Bank", "password": "a-long-passphrase-42"}'Response 200 OK
{"message": "success", "state": "deleted"}Warning: There is no endpoint to reopen a closed workspace.
Errors
| Status | Message |
|---|---|
| 400 | The confirm value is not the workspace's name (message below). |
| 400 | Current password is incorrect |
| 403 | Your role (admin) can't delete the workspace. It needs owner access; ask an owner of the workspace. |
| 429 | Too many incorrect passwords. Try again in 15 minutes. |
Type the workspace's name in `confirm` to delete it.Audit and permissions
Read the audit log
GET /team/audit
Returns the workspace's security events, newest first: sign-ups, logins and failed logins, password and two-factor changes, session revocations, API key creation and revocation, invitations, role changes, ownership transfers.
Auth: API key or session token. Role: admin and up.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
page_number | query | integer | no | From 1. Default 1. |
page_size | query | integer | no | 1 to 100. Default 50. |
event | query | string | no | Only this event, for example login_failed. |
Event names used by the server: signup, login, login_failed, logout, email_verified, password_reset_requested, password_reset, password_changed, 2fa_enabled, 2fa_disabled, session_revoked, sessions_revoked, api_key_created, api_key_revoked, invite_sent, invite_resent, invite_revoked, invite_accepted, role_changed, member_disabled, member_enabled, member_removed, ownership_transferred, workspace_deleted.
Request
curl "https://api.sevennadi.si/team/audit?event=login_failed&page_size=20" \
-H "Authorization: Bearer vk-0123456789abcdef0123456789abcdef"Response 200 OK
{
"page_number": 1,
"page_size": 20,
"total": 1,
"has_more": false,
"data": [
{
"id": 271,
"event": "login_failed",
"actor": "asha@example.test",
"member_id": "0144c9aa-d4fe-4341-99fd-aa27689ab6a0",
"detail": {"reason": "wrong password"},
"ip": "203.0.113.7",
"user_agent": "Mozilla/5.0",
"created_at": "2026-10-13T07:21:47Z"
}
]
}For an action done with an API key, actor is API key followed by the key's prefix and member_id is null.
Errors
| Status | Message |
|---|---|
| 400 | A validation message when page_number or page_size is out of range. |
| 403 | Your role (member) can't do this. It needs admin access; ask an owner or admin of the workspace. |
Get the permission table
GET /team/permissions
Returns the rule table the server checks for every authenticated request. Rules are read top to bottom and the first one that matches the method and path decides the minimum role. A path covers everything below it; a path containing * is a pattern.
Auth: API key or session token. Role: viewer and up.
Request
curl https://api.sevennadi.si/team/permissions \
-H "Authorization: Bearer vk-0123456789abcdef0123456789abcdef"Response 200 OK (rules shortened)
{
"roles": ["viewer", "member", "admin", "owner"],
"your_role": "admin",
"rank": {"viewer": 0, "member": 1, "admin": 2, "owner": 3},
"rules": [
{"methods": ["*"], "path": "/team/transfer-ownership", "role": "owner", "area": "transfer ownership"},
{"methods": ["POST", "PUT", "PATCH", "DELETE"], "path": "/billing", "role": "owner", "area": "billing"},
{"methods": ["*"], "path": "/auth/api-keys", "role": "admin", "area": "API keys"},
{"methods": ["GET", "HEAD", "OPTIONS"], "path": "/", "role": "viewer", "area": "reading"},
{"methods": ["*"], "path": "/", "role": "member", "area": "agents, knowledge bases, batches, campaigns and calls"}
]
}The last two rules are the defaults: reading needs viewer, changing anything needs member.