MIOSA has two parallel invite flows:
- Workspace invites - invite a user to a specific workspace. If the email already belongs to an org member, the user is added directly (no email sent). If not, an invite email is dispatched and accepting it creates both the
tenant_membersandworkspace_membersrows atomically. - Org invites - invite a user to join the org (tenant) without pre-selecting a workspace. The accept step only creates the
tenant_membersrow.
Workspace Invite Endpoints
| Method | Path | Auth | Description |
|---|---|---|---|
POST | /workspaces/{id}/invites | Required | Send a workspace invite |
GET | /workspaces/{id}/invites | Required | List pending invites |
DELETE | /workspaces/{id}/invites/{invite_id} | Required | Revoke an invite |
GET | /workspace-invites/{token} | None (public) | Preview invite details |
POST | /workspace-invites/{token}/accept | Required | Accept the invite |
Org Invite Endpoints
| Method | Path | Auth | Description |
|---|---|---|---|
POST | /tenants/{id}/invites | Required (admin/owner) | Send an org invite |
GET | /tenants/{id}/invites | Required (admin/owner) | List pending org invites |
DELETE | /tenants/{id}/invites/{invite_id} | Required (admin/owner) | Revoke an org invite |
GET | /invites/{token} | None (public) | Preview org invite details |
POST | /invites/{token}/accept | Required | Accept the org invite |
Workspace Invite Flow
Send Workspace Invite
POST /api/v1/workspaces/{id}/invites
Sends an invite email and returns the invite record. If the email already belongs to a tenant member the user is added directly (response type = "added", no email sent).
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
email | string | Yes | Email address to invite |
role | string | Yes | owner, admin, member, or viewer |
Response - 201 Created (new invite)
{
"type": "invited",
"data": {
"id": "inv_abc123",
"workspace_id": "ws-uuid",
"tenant_id": "ten-uuid",
"email": "alice@example.com",
"role": "member",
"invited_by": "usr_owner",
"expires_at": "2026-05-29T09:00:00Z",
"accepted_at": null,
"inserted_at": "2026-05-22T09:00:00Z"
}
} Response - 200 OK (existing member added directly)
{
"type": "added",
"data": {
"user_id": "usr_def456",
"workspace_id": "ws-uuid",
"role": "member",
"joined_at": "2026-05-22T09:00:00Z",
"added_by": "usr_owner"
}
} List Workspace Invites
GET /api/v1/workspaces/{id}/invites
Returns all pending workspace invites (excludes accepted and revoked).
Response - 200 OK
{
"data": [
{
"id": "inv_abc123",
"workspace_id": "ws-uuid",
"tenant_id": "ten-uuid",
"email": "alice@example.com",
"role": "member",
"invited_by": "usr_owner",
"expires_at": "2026-05-29T09:00:00Z",
"accepted_at": null,
"inserted_at": "2026-05-22T09:00:00Z"
}
]
} Revoke Workspace Invite
DELETE /api/v1/workspaces/{id}/invites/{invite_id}
Revokes a pending invite. Returns 409 if the invite was already accepted.
Response - 200 OK
{ "invite_id": "inv_abc123", "revoked": true } Preview Workspace Invite (public)
GET /api/v1/workspace-invites/{token}
Returns invite metadata without requiring authentication. Use this to render the pre-auth landing page.
Response - 200 OK
{
"data": {
"workspace_name": "Dr. Smith Clinic",
"role": "member",
"expires_at": "2026-05-29T09:00:00Z",
"expired": false,
"accepted": false
}
} Accept Workspace Invite
POST /api/v1/workspace-invites/{token}/accept
Accepts the invite on behalf of the authenticated user. If the user is new to the org, a tenant_members row is created atomically alongside the workspace_members row.
The caller’s API key email must match the invite email (case-insensitive).
Response - 200 OK
{
"accepted": true,
"workspace_id": "ws-uuid",
"tenant_id": "ten-uuid",
"role": "member"
} Errors
| Status | Code | Cause |
|---|---|---|
| 400 | INVALID_TOKEN | Token not found or expired |
| 400 | REVOKED | Invite was revoked |
| 422 | EMAIL_MISMATCH | Authenticated user’s email differs from invite email |
Org Invite Flow
Org invites grant membership in the tenant (org) without assigning a specific workspace.
Write operations require admin or owner role.
Send Org Invite
POST /api/v1/tenants/{id}/invites
Dispatches an invite email. The response includes an invite_url that is host-aware - on white-label tenants it uses the tenant’s custom domain.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
email | string | Yes | Email address to invite |
role | string | Yes | owner, admin, or member |
Response - 201 Created
{
"data": {
"invite_id": "inv_xyz789",
"email": "bob@example.com",
"role": "member",
"expires_at": "2026-05-29T09:00:00Z",
"invite_url": "https://app.miosa.ai/invites/eyJ..."
}
} List Org Invites
GET /api/v1/tenants/{id}/invites
Returns all pending org invites.
Revoke Org Invite
DELETE /api/v1/tenants/{id}/invites/{invite_id}
Revokes a pending org invite. Returns 409 if the invite was already accepted.
Preview Org Invite (public)
GET /api/v1/invites/{token}
Returns invite metadata without authentication.
Response - 200 OK
{
"data": {
"email": "bob@example.com",
"tenant_name": "Acme Corp",
"role": "member",
"expires_at": "2026-05-29T09:00:00Z",
"expired": false,
"accepted": false
}
} Accept Org Invite
POST /api/v1/invites/{token}/accept
Accepts an org invite on behalf of the authenticated user. Creates a tenant_members row.
The caller’s API key email must match the invite email (case-insensitive).
Response - 200 OK
{
"accepted": true,
"tenant_id": "ten-uuid",
"tenant": {
"id": "ten-uuid",
"name": "Acme Corp"
}
} Errors
| Status | Code | Cause |
|---|---|---|
| 400 | invalid_token | Token not found or expired |
| 422 | EMAIL_MISMATCH | Session email differs from invite email |
Common Errors
| Status | Code | Cause |
|---|---|---|
| 400 | INVALID_TOKEN / invalid_token | Token not found, expired, or revoked |
| 400 | REVOKED | Invite was explicitly revoked |
| 409 | ALREADY_ACCEPTED | Cannot revoke an invite that was already accepted |
| 422 | EMAIL_MISMATCH | Authenticated user’s email does not match the invite email |
| 422 | MISSING_EMAIL | email field missing from request body |
See also
- Workspace Members API - manage the roster directly (requires prior org membership)
- Workspaces API - workspace CRUD
- Error Codes - full error reference