Invitations API
Quick example
- curl
- Python
- Node.js
curl "$TRINITITE_BASE/v1/invitations" \
-H "Authorization: Bearer $SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"email": "alex@example.com",
"roles": ["GuardianAuthor"],
"force_sso": true,
"ttl_hours": 72
}'
import os, requests
requests.post(f"{os.environ['TRINITITE_BASE']}/v1/invitations",
headers={"Authorization": f"Bearer {os.environ['SESSION_TOKEN']}"},
json={"email": "alex@example.com", "roles": ["GuardianAuthor"], "force_sso": True, "ttl_hours": 72})
await fetch(`${process.env.TRINITITE_BASE}/v1/invitations`, {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.SESSION_TOKEN}`, 'Content-Type': 'application/json' },
body: JSON.stringify({ email: 'alex@example.com', roles: ['GuardianAuthor'], force_sso: true, ttl_hours: 72 }),
});
Overview
Invite a new user to your organization via secure email link. The recipient redeems a one-time token to provision their account, optionally pre-bound to roles and an SSO provider.
Authentication: session token only for the management endpoints (Authorization: Bearer <session_token>). Token lookup and acceptance endpoints are unauthenticated by design — they're called from the invitation email link.
Endpoints
| Method | Path | Auth | Permission |
|---|---|---|---|
POST | /v1/invitations | session | users:create |
GET | /v1/invitations | session | users:read |
DELETE | /v1/invitations/{id} | session | users:create |
GET | /v1/invitations/token/{token} | none | — |
POST | /v1/invitations/accept | none | — |
POST /v1/invitations
Issue an invitation. The platform sends an email containing a one-time link.
{
"email": "new.user@yourcompany.com",
"displayName": "New User",
"roles": ["developer"],
"forceSso": true,
"ttlDays": 7,
"message": "Welcome to the team!"
}
| Field | Type | Required | Description |
|---|---|---|---|
email | string | Yes | Recipient address — must be unique among users + open invitations in your org |
displayName | string | No | Pre-filled display name |
roles | string[] | No | Role IDs (role_*) or system role names. Applied on acceptance |
forceSso | boolean | No | Require the recipient to complete acceptance via SSO. Default false |
ttlDays | integer | No | Invitation expiry. Default 7, max 30 |
message | string | No | Custom welcome message included in the email |
Response — 201 Created:
{
"invitation_id": "inv_01JF8RIN1A2B3C4D5E6F7G8H9I",
"email": "new.user@yourcompany.com",
"display_name": "New User",
"roles": ["developer"],
"force_sso": true,
"expires_at": "2026-05-08T22:14:00Z",
"created_at": "2026-05-01T22:14:00Z",
"status": "pending"
}
GET /v1/invitations
List pending and recently expired invitations for your organization.
| Query | Type | Default | Description |
|---|---|---|---|
status | string | — | pending, accepted, expired, revoked |
limit | integer | 50 | Page size, max 100 |
offset | integer | 0 | — |
{
"invitations": [
{
"invitation_id": "inv_01JF8RIN1A2B3C4D5E6F7G8H9I",
"email": "new.user@yourcompany.com",
"display_name": "New User",
"roles": ["developer"],
"force_sso": true,
"status": "pending",
"created_at": "2026-05-01T22:14:00Z",
"expires_at": "2026-05-08T22:14:00Z",
"invited_by": "usr_01JF8RUS1A2B3C4D5E6F7G8H9I"
}
],
"pagination": { "total": 1, "limit": 50, "offset": 0 }
}
DELETE /v1/invitations/{id}
Revoke an invitation that has not yet been accepted. Returns 204 No Content. The token from the email is invalidated immediately.
GET /v1/invitations/token/{token}
Unauthenticated. Used by the invitation email landing page to look up invitation metadata before acceptance.
{
"email": "new.user@yourcompany.com",
"display_name": "New User",
"organization_name": "Acme Corp",
"force_sso": true,
"sso_provider": "okta",
"expires_at": "2026-05-08T22:14:00Z"
}
Returns 404 not_found if the token is unknown, 410 resource_gone if it has been revoked, and 422 unprocessable_entity if it has expired.
POST /v1/invitations/accept
Unauthenticated. Exchange the one-time token for a session.
{
"token": "inv_token_4f8d9e2a1c6b7f3a9e1d2c4b5a6f7e8d",
"password": "SecureP@ssword1",
"displayName": "New User"
}
| Field | Type | Required | Description |
|---|---|---|---|
token | string | Yes | The one-time token from the email |
password | string | Conditional | Required when the inviter did not set forceSso: true |
displayName | string | No | Override the pre-filled display name |
Response — 200 OK:
{
"sessionToken": "sess_01JF8RSE3B4C5D6E7F8G9H0I1J",
"user": {
"userId": "usr_01JF8RUS3B4C5D6E7F8G9H0I1J",
"email": "new.user@yourcompany.com",
"displayName": "New User"
},
"organization": {
"organizationId": "org_01JF8RORG1A2B3C4D5E6F7G8H9I",
"organizationName": "Acme Corp"
},
"roles": ["developer"]
}
When forceSso: true, the response carries no sessionToken; instead it includes an sso_redirect_url that the client should follow to complete sign-in with the configured identity provider.
Errors
| HTTP | error.code | Cause |
|---|---|---|
400 | validation_error | Body or query failed schema validation |
401 | unauthenticated | Missing or invalid session token (management endpoints only) |
403 | forbidden | Caller lacks users:create / users:read |
404 | not_found | Invitation, token, or referenced role not found |
409 | conflict | Email already belongs to a user or another open invitation |
410 | resource_gone | Token revoked |
422 | unprocessable_entity | Token expired |
Next steps
- Manage existing users → Users API
- Define custom roles before inviting → Roles API
- Configure SSO at the org level → Authentication