Teams & User Management
RBAC scoped per project, not globally. Owners hold billing, keys, and member management; members get inference and read-only views. Owners can delegate risk-tiered approval without handing over the whole project.
Access control operates at two layers:
- System-level roles, platform-wide capabilities (
adminvsoperator). - Project-level roles, what a user can do inside a specific project (
ownervsmember).
flowchart LR
User["User account"]
SysAdmin["System role: admin
platform-wide"]
SysOp["System role: operator
scoped to memberships"]
ProjA["Project A
role: owner"]
ProjB["Project B
role: member"]
ProjC["Project C
role: owner + delegate"]
User --> SysOp
User -.->|"if elevated"| SysAdmin
SysOp --> ProjA
SysOp --> ProjB
SysOp --> ProjC
Roles are scoped per project, not globally. A user can hold different roles in different projects, owner in one, member in another. The system-level role only gates platform administration; project capabilities live on the project pivot row.
Roles & Permissions
System-Level Roles
System-level roles control access to platform administration features.
| Role | Description | Capabilities |
|---|---|---|
| Admin | Platform administrator | Full access to admin panel, user management, system settings, audit logs, and all projects |
| Operator | Standard user | Access only to projects where they are a team member |
Project-Level Permissions
Within each project, team members are either an owner or a member. The table below shows what each role can do.
| Capability | Owner | Member |
|---|---|---|
| View team members | yes | yes |
| Use inference endpoints | yes | yes |
| View models and agents | yes | yes |
| Manage team members | yes | no |
| Manage invitations | yes | no |
| Project settings | yes | no |
| Billing management | yes | no |
| API key management | yes | no |
| Manage agents | yes | no |
| Manage endpoints | yes | no |
| Approve risk-tiered operations | yes | delegated |
Owners can scope approval authority to specific members for specific risk tiers through project role delegations, see Delegations below.
Team Management
Inviting Members
Project owners can invite users to join their team. The invitation flow depends on whether the user already has a Xerotier.ai account:
- Existing user: The user is added to the project team immediately and can access the project on their next login.
- New user: An invitation record is created (wire
object:invitation). When the invited email address registers an account, they are automatically added to the project team.
Delegations
Owners can scope approval authority for risk-tiered operations to specific members via project role delegations. Delegations are managed through the management API:
GET /:project_id/v1/management/project/delegations, list current delegationsPOST /:project_id/v1/management/project/delegations, create a delegation
This lets owners grant approval rights for specific operation risk tiers without promoting the member to owner.
Search Team Members
For typeahead and access-control UIs, member search is exposed at GET /:project_id/v1/project-members/search with API-key auth. The handler returns a ProjectMemberSearchResponse envelope.
Tip: You can invite users before they have created an account. The invitation will be fulfilled automatically when they register with the invited email address.
Changing Roles
Owners can change the role of other team members between owner and member. Two safety rules apply:
Self-demotion blocked. You cannot change your own role. Another owner must do it. The server rejects self-PATCH with 403 Forbidden.
Last-owner protection. The last remaining owner cannot be demoted. Promote another member to owner first, then the previous owner can step down.
Removing Members
Owners can remove other members from the project team. Two safety rules apply:
Self-removal blocked. You cannot remove yourself from the team. Ask another owner to remove you, or transfer ownership first.
Last-owner protection. The last remaining owner cannot be removed. Transfer ownership first by promoting another member, then the previous owner can be removed.
Revoking Invitations
Owners can revoke pending invitations that have not yet been accepted. Once revoked, the invitation link is invalidated and the email address will not be auto-added on registration.
Invitations
When you invite a user who does not yet have a Xerotier.ai account, an invitation record is created (wire object: invitation). Invitations are stored per-project and carry the invited email address, role, the inviting user id, creation timestamp, and a hard expiry.
Invitation Lifecycle
- Owner calls
POST /:project_id/v1/management/project/members(router API, Bearer key withmanagementscope), or the dashboard equivalentPOST /projects/members(session cookie + CSRF), with an email that does not exist in the system. - An invitation row is created. If email is configured, an invitation email is sent.
- When the invited email registers an account, the system automatically adds them to the project team with the specified role and clears the invitation.
- The owner can view outstanding invitations via
GET /:project_id/v1/management/project/invitations(owner-only). - The owner can resend the invitation email via
POST /:project_id/v1/management/project/invitations/:id/resend. - The owner can cancel a pending invitation at any time via
DELETE /:project_id/v1/management/project/invitations/:id.
Pending Invitation Fields
| Field | Type | Description |
|---|---|---|
| object | string | Always invitation. |
| invitation_id | string (UUID) | Unique invitation identifier. Used to resend or revoke the invitation. |
| string | Email address of the invited user. | |
| role | string | Role the user will receive on registration: owner or member. |
| invited_by | string (UUID) | User id of the owner who sent the invitation. |
| created_at | string (ISO 8601) | Timestamp when the invitation was created. |
| expires_at | string (ISO 8601) | Hard expiry of the invitation. After this time the invitation is no longer redeemable. |
| accepted_at | string (ISO 8601) or null | Set when the invited user registers and joins the project. |
| revoked_at | string (ISO 8601) or null | Set when an owner cancels the invitation before acceptance. |
Rate limit: The Frontend dashboard proxy (/projects/*) limits invitation operations to 20 per hour per project. Direct calls to the router-side management API (/:project_id/v1/management/project/*) are governed by the standard sliding-window limiter on that surface and do not share the dashboard bucket.
Access Control
The following table lists the canonical router-side team management endpoints and the role required to access each one. The dashboard Frontend (/projects/*) exposes a parallel cookie+CSRF surface that proxies to these routes; see the "Two surfaces" callout in API Examples.
| Endpoint | Method | Required Role |
|---|---|---|
/:project_id/v1/management/project/members |
GET | Any member |
/:project_id/v1/project-members/search |
GET | Any member |
/:project_id/v1/management/project/invitations |
GET | Owner |
/:project_id/v1/management/project/members |
POST | Owner |
/:project_id/v1/management/project/members/:user_id |
PATCH | Owner |
/:project_id/v1/management/project/members/:user_id |
DELETE | Owner |
/:project_id/v1/management/project/invitations/:id/resend |
POST | Owner |
/:project_id/v1/management/project/invitations/:id |
DELETE | Owner |
/:project_id/v1/management/project/delegations |
GET, POST | Owner |
API Examples
Two surfaces. Team management is exposed on two distinct surfaces with different authentication. The examples below target the canonical router-side management API.
- Router management API,
/:project_id/v1/management/project/*. Authenticate with an API key (Authorization: Bearer xero_<project_slug>_<token>) that includes themanagementscope. No CSRF token required. - Frontend dashboard proxy,
/projects/*and/projects/api/*. Authenticate with the browser session cookie and anX-CSRF-Tokenheader. API-key Bearer auth is not accepted on this surface. Used by the dashboard UI; the proxy forwards to the router management API.
The :project_id path segment accepts either the project UUID or the operator-facing project external id.
List Team Members
Returns an OpenAI-style list envelope ({"object":"list","data":[...]}) of member objects. Each member object includes the following fields:
| Field | Type | Description |
|---|---|---|
| user_id | string (UUID) | Unique identifier of the user. |
| string | User's email address. | |
| display_name | string | User's display name. |
| role | string | Role raw value: owner or member. |
| joined_at | string (ISO 8601) | Timestamp when the user joined the project. |
curl https://api.xerotier.ai/PROJECT_ID/v1/management/project/members \
-H "Authorization: Bearer xero_PROJECT_SLUG_TOKEN"
import requests
headers = {"Authorization": "Bearer xero_PROJECT_SLUG_TOKEN"}
response = requests.get(
"https://api.xerotier.ai/PROJECT_ID/v1/management/project/members",
headers=headers
)
for member in response.json()["data"]:
print(f"{member['email']} - {member['role']}")
const response = await fetch(
"https://api.xerotier.ai/PROJECT_ID/v1/management/project/members",
{
headers: { "Authorization": "Bearer xero_PROJECT_SLUG_TOKEN" }
}
);
const payload = await response.json();
payload.data.forEach(m => console.log(`${m.email} - ${m.role}`));
Invite a Team Member
curl -X POST https://api.xerotier.ai/PROJECT_ID/v1/management/project/members \
-H "Authorization: Bearer xero_PROJECT_SLUG_TOKEN" \
-H "Content-Type: application/json" \
-d '{"email": "newuser@example.com", "role": "member"}'
import requests
headers = {
"Authorization": "Bearer xero_PROJECT_SLUG_TOKEN",
"Content-Type": "application/json"
}
response = requests.post(
"https://api.xerotier.ai/PROJECT_ID/v1/management/project/members",
headers=headers,
json={"email": "newuser@example.com", "role": "member"}
)
print(response.json())
const response = await fetch(
"https://api.xerotier.ai/PROJECT_ID/v1/management/project/members",
{
method: "POST",
headers: {
"Authorization": "Bearer xero_PROJECT_SLUG_TOKEN",
"Content-Type": "application/json"
},
body: JSON.stringify({
email: "newuser@example.com",
role: "member"
})
}
);
const data = await response.json();
console.log(data);
Change a Member's Role
curl -X PATCH https://api.xerotier.ai/PROJECT_ID/v1/management/project/members/USER_ID \
-H "Authorization: Bearer xero_PROJECT_SLUG_TOKEN" \
-H "Content-Type: application/json" \
-d '{"role": "owner"}'
Remove a Team Member
curl -X DELETE https://api.xerotier.ai/PROJECT_ID/v1/management/project/members/USER_ID \
-H "Authorization: Bearer xero_PROJECT_SLUG_TOKEN"
List Pending Invitations
Only accessible to project owners. Returns an OpenAI-style list envelope ({"object":"list","data":[...]}) of invitation objects (each with object: invitation).
curl https://api.xerotier.ai/PROJECT_ID/v1/management/project/invitations \
-H "Authorization: Bearer xero_PROJECT_SLUG_TOKEN"
import requests
headers = {"Authorization": "Bearer xero_PROJECT_SLUG_TOKEN"}
response = requests.get(
"https://api.xerotier.ai/PROJECT_ID/v1/management/project/invitations",
headers=headers
)
for inv in response.json()["data"]:
print(f"{inv['email']} ({inv['role']}) - invited by {inv['invited_by']}")
Resend a Pending Invitation
curl -X POST https://api.xerotier.ai/PROJECT_ID/v1/management/project/invitations/INVITATION_ID/resend \
-H "Authorization: Bearer xero_PROJECT_SLUG_TOKEN"
Revoke a Pending Invitation
curl -X DELETE https://api.xerotier.ai/PROJECT_ID/v1/management/project/invitations/INVITATION_ID \
-H "Authorization: Bearer xero_PROJECT_SLUG_TOKEN"
API Key Security
API keys authenticate inference requests to your project endpoints. Xerotier.ai uses several layers of protection to keep your keys secure.
- 256-bit random generation: Keys are generated using 32 bytes of cryptographically secure random data, providing 256 bits of entropy.
- SHA-256 hash storage: The raw key is never stored. Only a SHA-256 hash is persisted in the database. The full key is shown once at creation time.
- Scoped permissions: Each key can be restricted to one or more permission scopes drawn from the canonical set
inference,management,execution,research. - Endpoint restriction: Keys can be locked to specific endpoints, preventing use on other endpoints within the same project.
- IP allowlist/blocklist: Keys support optional IP address filtering to restrict which networks can use them.
Important: Copy your API key immediately after creation. It cannot be retrieved later because only the hash is stored.
See Authentication for the full key lifecycle (creation, rotation, revocation) and the canonical scope set.
Audit Trail
All team management actions are recorded in the project audit log. The following events are tracked:
| Audit Action | Description |
|---|---|
team_member_added |
A user was added to the project team |
team_member_role_changed |
A team member's role was changed (owner/member) |
team_member_removed |
A user was removed from the project team |
pending_invitation_revoked |
A pending invitation was cancelled before acceptance |
api_key_created |
A new API key was generated |
api_key_rotated |
An existing API key was rotated (old key invalidated) |
api_key_revoked |
An API key was permanently revoked |
Encryption: The changes payload on each audit row is encrypted at rest using AES-256-GCM. Metadata fields (action, resource, ip_address, user_agent, success, error_message, created_at) are stored in plaintext.
Security Practices
Xerotier.ai enforces several security measures to protect your team and project:
- Session destruction on suspension: When a user account is suspended by an admin, all active sessions are immediately invalidated.
- CSRF protection: All state-changing requests require a valid CSRF token, preventing cross-site request forgery attacks.
- Password change invalidates sessions: Changing your password automatically destroys all existing sessions, forcing re-authentication on all devices.
- Optional TOTP two-factor authentication: Users can enable time-based one-time password (TOTP) 2FA for an additional layer of login security.
- Principle of least privilege: Members receive only the permissions they need. Use the member role for users who only need inference access.
- Multiple owners for redundancy: Promote at least two team members to the owner role to avoid single points of failure for administrative tasks.
Project Quotas
Each project has configurable resource quotas that control rate limiting and storage allocation. Quotas are managed by project owners and can override the defaults set by your subscription tier.
| Quota | Description | Default |
|---|---|---|
| maxRequestsPerMinute | Maximum inference requests allowed per minute | Tier default |
| maxConcurrentRequests | Maximum simultaneous inference requests | Tier default |
| burstLimit | Short burst allowance above the per-minute rate | Tier default |
| storageQuotaGB | Maximum model storage in gigabytes | 4 GB (free) / 500 GB (premium) |
When a project exceeds its rate limits, the isRateLimited flag is set and subsequent requests receive a 429 (Too Many Requests) response until the rate drops below the limit.
Team Billing Context
Billing is managed at the project level. Key billing fields on each project include:
- creditsBalance: Available credits for per-token inference billing
- accountTier: Free or Premium, determines default quotas and storage limits
- tierId: Service tier identifier for pricing and feature access
- isDelinquent: Set when credit balance is exhausted and usage continues
- status: Active or Suspended, suspended projects cannot process inference requests
See Billing & Subscriptions for details on managing your plan, and Usage Tracking & Billing for cost monitoring.
Frequently Asked Questions
Can a user belong to multiple projects?
Yes. A single Xerotier.ai account can be a member of multiple projects, each with an independent role. You might be an owner in your primary project and a member in a collaborator's project.
What happens when I invite someone who does not have an account?
A pending invitation is created for that email address. When the person registers with that email, they are automatically added to your project team with the role you specified. You can revoke the invitation at any time before they register.
Can a project have multiple owners?
Yes, and it is recommended. Multiple owners keep administrative tasks (billing, settings, member management) unblocked if one owner is unavailable. There is no limit on the number of owners.
What happens to a suspended user's sessions?
When an admin suspends a user account, all of that user's active sessions are immediately destroyed. The user is logged out on all devices and cannot create new sessions until the suspension is lifted.
How are API keys protected?
API keys are generated with 256 bits of cryptographic randomness; only the SHA-256 hash is stored. The full key is shown once at creation and cannot be recovered. Keys can be scoped to specific permissions, locked to specific endpoints, and filtered by IP address. See Authentication for the full key lifecycle.