docs/en/api/08-admin.md
The Admin API manages accounts and users in a multi-tenant environment. It covers workspace (account) creation/deletion, user registration/removal, role changes, and API key regeneration.
This API is available in both api_key and trusted deployments:
api_key mode, the effective role is always derived from the presented API key.trusted mode, ordinary requests still do not use user-key registration, but a trusted gateway may call Admin API using a registered user with appropriate role (role is looked up from user registry).In trusted mode, role is determined by looking up X-OpenViking-Account + X-OpenViking-User from the user registry. If the user doesn't exist, role defaults to USER.
For /api/v1/admin/*, trusted mode also permits requests with no explicit identity headers; those requests are treated as ROOT and are intended for trusted upstreams authenticated by the deployment's root_api_key.
| Role | Description |
|---|---|
| ROOT | System administrator with full access |
| ADMIN | Workspace administrator, manages users within their account |
| USER | Regular user |
| Operation | ROOT | ADMIN | USER |
|---|---|---|---|
| Create/delete workspace | Y | N | N |
| List workspaces | Y | N | N |
| Register/remove users | Y | Y (own account) | N |
| List agent namespaces | Y | Y (own account) | N |
| Regenerate user key | Y | Y (own account) | N |
| Change user role | Y | N | N |
--sudo OptionWhen using the ov CLI to perform admin operations requiring ROOT privileges, you can use the --sudo option. This option uses the root_api_key from your ~/.openviking/ovcli.conf instead of the regular api_key.
Configure root_api_key in ~/.openviking/ovcli.conf:
{
"url": "http://localhost:1933",
"api_key": "alice-user-key",
"root_api_key": "your-root-api-key",
...
}
--sudoov --sudo admin - Account and user managementov --sudo system - System utility commandsov --sudo reindex - Rebuild indexes--sudo only works with admin commands - using it with regular data commands will errorroot_api_key configured to use --sudoCreate a new workspace with its first admin user.
Processing Flow:
Code Entry Points:
openviking/server/routers/admin.py:create_account - HTTP routeopenviking/server/api_keys/new.py:APIKeyManager.create_account - Core implementationopenviking_cli/client/sync_http.py:SyncHTTPClient.admin_create_account - Python SDKParameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| account_id | str | Yes | - | Workspace ID |
| admin_user_id | str | Yes | - | First admin user ID |
| isolate_user_scope_by_agent | bool | No | false | Further isolate user scope by agent |
| isolate_agent_scope_by_user | bool | No | false | Further isolate agent scope by user |
Notes:
trusted mode, user_key is omitted from the responseisolate_user_scope_by_agent and isolate_agent_scope_by_user are only available via HTTP API, not in Python SDK or CLIHTTP API
POST /api/v1/admin/accounts
curl -X POST http://localhost:1933/api/v1/admin/accounts \
-H "Content-Type: application/json" \
-H "X-API-Key: <root-key>" \
-d '{
"account_id": "acme",
"admin_user_id": "alice",
"isolate_user_scope_by_agent": true,
"isolate_agent_scope_by_user": false
}'
Trusted mode (registered gateway user)
# First, register the gateway admin user in api_key mode
curl -X POST http://localhost:1933/api/v1/admin/accounts \
-H "Content-Type: application/json" \
-H "X-API-Key: <root-key>" \
-d '{
"account_id": "platform",
"admin_user_id": "gateway-admin"
}'
# Then promote it to root for cross-account admin operations
curl -X PUT http://localhost:1933/api/v1/admin/accounts/platform/users/gateway-admin/role \
-H "Content-Type: application/json" \
-H "X-API-Key: <root-key>" \
-d '{"role": "root"}'
# Then use in trusted mode
curl -X POST http://localhost:1933/api/v1/admin/accounts \
-H "Content-Type: application/json" \
-H "X-API-Key: <root-key>" \
-H "X-OpenViking-Account: platform" \
-H "X-OpenViking-User: gateway-admin" \
-d '{
"account_id": "acme",
"admin_user_id": "alice",
"isolate_user_scope_by_agent": true,
"isolate_agent_scope_by_user": false
}'
Trusted mode (root fallback without identity headers)
curl -X POST http://localhost:1933/api/v1/admin/accounts \
-H "Content-Type: application/json" \
-H "X-API-Key: <root-key>" \
-d '{
"account_id": "acme",
"admin_user_id": "alice"
}'
Python SDK
import openviking as ov
client = ov.SyncHTTPClient(api_key="<root-key>")
client.initialize()
result = client.admin_create_account("acme", "alice")
print(f"Account created: {result['account_id']}")
print(f"Admin user: {result['admin_user_id']}")
print(f"User key: {result.get('user_key', '(not exposed in trusted mode)')}")
CLI
# Requires ROOT privileges, use --sudo
ov --sudo admin create-account acme --admin alice
Response Example
{
"status": "ok",
"result": {
"account_id": "acme",
"admin_user_id": "alice",
"user_key": "7f3a9c1e...",
"isolate_user_scope_by_agent": true,
"isolate_agent_scope_by_user": false
},
"time": 0.1
}
In trusted mode, the same response omits user_key.
List all workspaces (ROOT only).
Processing Flow:
Code Entry Points:
openviking/server/routers/admin.py:list_accounts - HTTP routeopenviking/server/api_keys/new.py:APIKeyManager.get_accounts - Core implementationopenviking_cli/client/sync_http.py:SyncHTTPClient.admin_list_accounts - Python SDKNo parameters.
HTTP API
GET /api/v1/admin/accounts
curl -X GET http://localhost:1933/api/v1/admin/accounts \
-H "X-API-Key: <root-key>"
Python SDK
import openviking as ov
client = ov.SyncHTTPClient(api_key="<root-key>")
client.initialize()
accounts = client.admin_list_accounts()
for account in accounts:
print(f"Account: {account['account_id']}, created: {account['created_at']}, users: {account['user_count']}")
CLI
# Requires ROOT privileges, use --sudo
ov --sudo admin list-accounts
Response Example
{
"status": "ok",
"result": [
{"account_id": "default", "created_at": "2026-02-12T10:00:00Z", "user_count": 1},
{"account_id": "acme", "created_at": "2026-02-13T08:00:00Z", "user_count": 2}
],
"time": 0.1
}
Delete a workspace and all associated users and data (ROOT only).
Processing Flow:
Code Entry Points:
openviking/server/routers/admin.py:delete_account - HTTP routeopenviking/server/api_keys/new.py:APIKeyManager.delete_account - Core implementationopenviking_cli/client/sync_http.py:SyncHTTPClient.admin_delete_account - Python SDKParameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| account_id | str | Yes | - | Workspace ID to delete |
Notes:
HTTP API
DELETE /api/v1/admin/accounts/{account_id}
curl -X DELETE http://localhost:1933/api/v1/admin/accounts/acme \
-H "X-API-Key: <root-key>"
Python SDK
import openviking as ov
client = ov.SyncHTTPClient(api_key="<root-key>")
client.initialize()
result = client.admin_delete_account("acme")
print(f"Account deleted: {result['deleted']}")
CLI
# Requires ROOT privileges, use --sudo
ov --sudo admin delete-account acme
Response Example
{
"status": "ok",
"result": {
"deleted": true
},
"time": 0.1
}
Register a new user in a workspace.
Processing Flow:
Code Entry Points:
openviking/server/routers/admin.py:register_user - HTTP routeopenviking/server/api_keys/new.py:APIKeyManager.register_user - Core implementationopenviking_cli/client/sync_http.py:SyncHTTPClient.admin_register_user - Python SDKParameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| account_id | str | Yes | - | Workspace ID |
| user_id | str | Yes | - | User ID |
| role | str | No | "user" | Role: "admin" or "user" |
Notes:
trusted mode, user_key is omitted from the responseHTTP API
POST /api/v1/admin/accounts/{account_id}/users
curl -X POST http://localhost:1933/api/v1/admin/accounts/acme/users \
-H "Content-Type: application/json" \
-H "X-API-Key: <root-or-admin-key>" \
-d '{
"user_id": "bob",
"role": "user"
}'
Python SDK
import openviking as ov
client = ov.SyncHTTPClient(api_key="<root-or-admin-key>")
client.initialize()
result = client.admin_register_user("acme", "bob", role="user")
print(f"User registered: {result['user_id']}")
print(f"User key: {result.get('user_key', '(not exposed in trusted mode)')}")
CLI
# Either ROOT or account ADMIN can execute
# If using regular user's api_key who is an ADMIN of acme:
ov admin register-user acme bob --role user
# If using root_api_key (--sudo):
ov --sudo admin register-user acme bob --role user
Response Example
{
"status": "ok",
"result": {
"account_id": "acme",
"user_id": "bob",
"user_key": "d91f5b2a..."
},
"time": 0.1
}
List all users in a workspace.
Processing Flow:
Code Entry Points:
openviking/server/routers/admin.py:list_users - HTTP routeopenviking/server/api_keys/new.py:APIKeyManager.get_users - Core implementationopenviking_cli/client/sync_http.py:SyncHTTPClient.admin_list_users - Python SDKParameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| account_id | str | Yes | - | Workspace ID |
| limit | int | No | 100 | Maximum number of users to return |
| name | str | No | null | Filter by user ID (prefix match) |
| role | str | No | null | Filter by role |
Notes:
trusted mode, user_key is omitted from the responseHTTP API
GET /api/v1/admin/accounts/{account_id}/users
# List all users
curl -X GET http://localhost:1933/api/v1/admin/accounts/acme/users \
-H "X-API-Key: <root-or-admin-key>"
# With filters
curl -X GET "http://localhost:1933/api/v1/admin/accounts/acme/users?role=admin&limit=50" \
-H "X-API-Key: <root-or-admin-key>"
Python SDK
import openviking as ov
client = ov.SyncHTTPClient(api_key="<root-or-admin-key>")
client.initialize()
users = client.admin_list_users("acme")
for user in users:
print(f"User: {user['user_id']}, role: {user['role']}")
CLI
# Either ROOT or account ADMIN can execute
# If using regular user's api_key who is an ADMIN of acme:
ov admin list-users acme
# If using root_api_key (--sudo):
ov --sudo admin list-users acme
Response Example
{
"status": "ok",
"result": [
{"user_id": "alice", "role": "admin"},
{"user_id": "bob", "role": "user"}
],
"time": 0.1
}
List agent namespaces that exist under a workspace. This is an admin discovery API; it does not change normal viking://agent/... filesystem semantics.
Processing Flow:
viking://agent namespace rootCode Entry Points:
openviking/server/routers/admin.py:list_agents - HTTP routecrates/ov_cli/src/client.rs:HttpClient.admin_list_agents - CLI HTTP clientcrates/ov_cli/src/commands/admin.rs:list_agents - CLI commandParameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| account_id | str | Yes | - | Workspace ID |
Notes:
default agent namespace.HTTP API
GET /api/v1/admin/accounts/{account_id}/agents
curl -X GET http://localhost:1933/api/v1/admin/accounts/acme/agents \
-H "X-API-Key: <root-or-admin-key>"
CLI
# Either ROOT or account ADMIN can execute
# If using regular user's api_key who is an ADMIN of acme:
ov admin list-agents acme
# If using root_api_key (--sudo):
ov --sudo admin list-agents acme
Response Example
{
"status": "ok",
"result": [
{"agent_id": "default", "uri": "viking://agent/default"},
{"agent_id": "openclaw", "uri": "viking://agent/openclaw"}
],
"time": 0.1
}
Remove a user from a workspace. The user's API key is deleted immediately.
Processing Flow:
Code Entry Points:
openviking/server/routers/admin.py:remove_user - HTTP routeopenviking/server/api_keys/new.py:APIKeyManager.remove_user - Core implementationopenviking_cli/client/sync_http.py:SyncHTTPClient.admin_remove_user - Python SDKParameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| account_id | str | Yes | - | Workspace ID |
| user_id | str | Yes | - | User ID to remove |
Notes:
HTTP API
DELETE /api/v1/admin/accounts/{account_id}/users/{user_id}
curl -X DELETE http://localhost:1933/api/v1/admin/accounts/acme/users/bob \
-H "X-API-Key: <root-or-admin-key>"
Python SDK
import openviking as ov
client = ov.SyncHTTPClient(api_key="<root-or-admin-key>")
client.initialize()
result = client.admin_remove_user("acme", "bob")
print(f"User deleted: {result['deleted']}")
CLI
# Either ROOT or account ADMIN can execute
# If using regular user's api_key who is an ADMIN of acme:
ov admin remove-user acme bob
# If using root_api_key (--sudo):
ov --sudo admin remove-user acme bob
Response Example
{
"status": "ok",
"result": {
"deleted": true
},
"time": 0.1
}
Change a user's role (ROOT only).
Processing Flow:
Code Entry Points:
openviking/server/routers/admin.py:set_user_role - HTTP routeopenviking/server/api_keys/new.py:APIKeyManager.set_role - Core implementationopenviking_cli/client/sync_http.py:SyncHTTPClient.admin_set_role - Python SDKParameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| account_id | str | Yes | - | Workspace ID |
| user_id | str | Yes | - | User ID |
| role | str | Yes | - | New role: "admin", "user", or "root" |
Notes:
HTTP API
PUT /api/v1/admin/accounts/{account_id}/users/{user_id}/role
curl -X PUT http://localhost:1933/api/v1/admin/accounts/acme/users/bob/role \
-H "Content-Type: application/json" \
-H "X-API-Key: <root-key>" \
-d '{"role": "admin"}'
Python SDK
import openviking as ov
client = ov.SyncHTTPClient(api_key="<root-key>")
client.initialize()
result = client.admin_set_role("acme", "bob", "admin")
print(f"User: {result['user_id']}, new role: {result['role']}")
CLI
# Requires ROOT privileges, use --sudo
ov --sudo admin set-role acme bob admin
Response Example
{
"status": "ok",
"result": {
"account_id": "acme",
"user_id": "bob",
"role": "admin"
},
"time": 0.1
}
Regenerate a user's API key. The old key is immediately invalidated.
Processing Flow:
Code Entry Points:
openviking/server/routers/admin.py:regenerate_key - HTTP routeopenviking/server/api_keys/new.py:APIKeyManager.regenerate_key - Core implementationopenviking_cli/client/sync_http.py:SyncHTTPClient.admin_regenerate_key - Python SDKParameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| account_id | str | Yes | - | Workspace ID |
| user_id | str | Yes | - | User ID |
Notes:
HTTP API
POST /api/v1/admin/accounts/{account_id}/users/{user_id}/key
curl -X POST http://localhost:1933/api/v1/admin/accounts/acme/users/bob/key \
-H "Content-Type: application/json" \
-H "X-API-Key: <root-or-admin-key>"
Python SDK
import openviking as ov
client = ov.SyncHTTPClient(api_key="<root-or-admin-key>")
client.initialize()
result = client.admin_regenerate_key("acme", "bob")
print(f"New user key: {result['user_key']}")
CLI
# Either ROOT or account ADMIN can execute
# If using regular user's api_key who is an ADMIN of acme:
ov admin regenerate-key acme bob
# If using root_api_key (--sudo):
ov --sudo admin regenerate-key acme bob
Response Example
{
"status": "ok",
"result": {
"user_key": "e82d4e0f..."
},
"time": 0.1
}
# Step 1: ROOT creates workspace with alice as first admin (requires --sudo)
ov --sudo admin create-account acme --admin alice
# Returns alice's user_key
# Step 2: alice (admin) registers regular user bob
# Configure api_key in config file to alice's user_key, no --sudo needed
ov admin register-user acme bob --role user
# Returns bob's user_key
# Step 3: List all users in the account
ov admin list-users acme
# Step 4: ROOT promotes bob to admin (requires --sudo)
ov --sudo admin set-role acme bob admin
# Step 5: bob lost their key, regenerate (old key immediately invalidated)
# alice as admin can do this, no --sudo needed
ov admin regenerate-key acme bob
# Step 6: Remove user
ov admin remove-user acme bob
# Step 7: Delete entire workspace (requires --sudo)
ov --sudo admin delete-account acme
# Step 1: Create workspace
curl -X POST http://localhost:1933/api/v1/admin/accounts \
-H "Content-Type: application/json" \
-H "X-API-Key: <root-key>" \
-d '{"account_id": "acme", "admin_user_id": "alice"}'
# Step 2: Register user (using alice's admin key)
curl -X POST http://localhost:1933/api/v1/admin/accounts/acme/users \
-H "Content-Type: application/json" \
-H "X-API-Key: <alice-key>" \
-d '{"user_id": "bob", "role": "user"}'
# Step 3: List users
curl -X GET http://localhost:1933/api/v1/admin/accounts/acme/users \
-H "X-API-Key: <alice-key>"
# Step 4: Change role (requires ROOT key)
curl -X PUT http://localhost:1933/api/v1/admin/accounts/acme/users/bob/role \
-H "Content-Type: application/json" \
-H "X-API-Key: <root-key>" \
-d '{"role": "admin"}'
# Step 5: Regenerate key
curl -X POST http://localhost:1933/api/v1/admin/accounts/acme/users/bob/key \
-H "Content-Type: application/json" \
-H "X-API-Key: <alice-key>"
# Step 6: Remove user
curl -X DELETE http://localhost:1933/api/v1/admin/accounts/acme/users/bob \
-H "X-API-Key: <alice-key>"
# Step 7: Delete workspace
curl -X DELETE http://localhost:1933/api/v1/admin/accounts/acme \
-H "X-API-Key: <root-key>"