REST API: Admin
ScaiKey-driven user/group sync, super_admin role management, tenant admin views.
All endpoints under /api/v1/admin. Most require super_admin (role is the ScaiFlow-local admin role, not a ScaiKey role); a few /tenant/* endpoints accept tenant_admin too.
GET /v1/admin/users#
List every user mirrored from ScaiKey.
Super admin only.
Response:
{
"users": [
{
"id": "usr_xxx",
"tenant_id": "tnt_acme",
"email": "alice@acme.example",
"display_name": "Alice Johnson",
"status": "ACTIVE",
"admin_role": "tenant_admin", // null | tenant_admin | super_admin
"admin_role_source": "manual", // null | manual | group | bootstrap
"deleted_at": null,
"last_synced_at": "2026-04-29..."
}
]
}
POST /v1/admin/sync#
Trigger an immediate sync from ScaiKey. Same code path as the hourly background sync.
Super admin only.
Response:
{
"skipped_reason": null,
"users_method": "effective_users", // or "member_walk" (fallback path)
"users_seen": 42,
"users_added": 3,
"users_undeleted": 0,
"users_soft_deleted": 1,
"groups_seen": 7,
"groups_added": 0,
"groups_undeleted": 0,
"groups_soft_deleted": 0,
"memberships_seen": 86,
"memberships_added": 5,
"memberships_soft_deleted": 2,
"super_admin_count": 2,
"super_admins_group_promoted": 0,
"super_admins_group_demoted": 0,
"duration_seconds": 1.8
}
skipped_reason is set when sync was skipped (e.g. credentials_missing — SCAIKEY_BASE_URL etc. not configured).
users_method indicates which path mirrored users: effective_users (preferred, platform-tier ScaiKey credentials) or member_walk (tenant-tier fallback).
POST /v1/admin/users/{user_id}/role#
Grant an admin role to a user.
Body:
{ "role": "tenant_admin" } // or "super_admin"
Super admin can grant either role to anyone. Tenant admin can grant tenant_admin to users in their own tenant only.
204 No Content.
The grant is recorded with admin_role_source = "manual" — survives reconciliation (the group-derived sync won't demote a manual grant). Use the matching DELETE to revoke.
DELETE /v1/admin/users/{user_id}/role#
Revoke an admin role. Same permission rules as grant.
204 No Content.
Refuses to demote the last super_admin (would lock out platform access). Set another super_admin first.
GET /v1/admin/tenant/users#
All users in the calling tenant. Accepts tenant_admin (sees own tenant only) or super_admin (sees specified or own tenant).
Query:
tenant_id— (super_admin only) view a different tenant's users.
Response: same shape as /v1/admin/users but scoped.
GET /v1/admin/tenant/flows#
All flows in the calling tenant, regardless of per-flow ACLs (tenant admins have implicit admin access on every flow in their tenant).
Response:
[
{
"id": "flow_xxx",
"name": "Customer Support",
"owner_id": "usr_alice",
"owner_email": "alice@acme.example",
"version": "1.0.2",
"created_at": "2026-04-01...",
"updated_at": "2026-04-29..."
}
]
Used by the tenant admin UI to surface every flow in the tenant, even ones the admin doesn't have an explicit ACL on.