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. When a configured root_api_key is presented to /api/v1/admin/*, the trusted upstream is authorized as ROOT.For /api/v1/admin/*, trusted mode permits requests with no explicit identity headers, and also permits target identity headers when they match the account/user in the URL. These requests are treated as ROOT after the deployment's root_api_key is verified. For ordinary trusted-mode data APIs, role and identity still come from X-OpenViking-Account + X-OpenViking-User.
| 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 agents (deprecated, returns empty list) | 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 indexesov --sudo admin migrate - Legacy agent/session migration and cleanupov --sudo task status/list - Query root/system background tasks, such as migration tasks--sudo only works with the commands above - 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 |
Notes:
trusted mode, user_key is omitted from the responsepeer_id.HTTP 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"
}'
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"
}'
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..."
},
"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:
user/ and resources/; sessions live under user/)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 to assign. ROOT and same-account ADMIN may register "user" or "admin". "root" must be assigned through the dedicated role-change endpoint. |
Notes:
trusted mode, user_key is omitted from the response"root" role cannot be minted through user registrationHTTP 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
}
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
}
Migrate 0.3.x legacy viking://agent/... / viking://session/... data into the 0.4.0 user / peer namespace, or clean up old namespaces after migration has been verified. This endpoint is ROOT-only and runs as a background task.
Processing Flow:
action=migrate, run preflight checks for account registry, session owner metadata, and other prerequisitesMigration does not automatically call reindex. If retrieval after migration is not as expected, users should manually reindex the new paths.
Code Entry Points:
openviking/server/routers/admin.py:migrate_legacy_data - HTTP routeopenviking/service/legacy_migration.py:LegacyDataMigration - Migration implementationHTTP API
POST /api/v1/admin/migrate
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| action | str | No | migrate | migrate runs migration; cleanup removes old namespaces |
Migration result fields
| Field | Description |
|---|---|
| migrated.files / migrated.directories | Number of files and directories copied |
| migrated.vector_records | Number of existing vector records copied |
| migrated.skipped_vector_records | Number of old records skipped because they had no vector payload |
| migrated.operations | Operation counts grouped by migration category |
| skipped / warnings / created_users | Skipped items, warnings, and users created automatically |
Cleanup result fields
| Field | Description |
|---|---|
| cleanup.directories | Number of legacy directories deleted |
| cleanup.vector_records | Number of old vector records deleted |
| cleanup.targets | Legacy scopes that were cleaned |
| skipped / warnings | Skipped items and warnings |
Run migration
curl -X POST http://localhost:1933/api/v1/admin/migrate \
-H "Content-Type: application/json" \
-H "X-API-Key: <root-key>" \
-d '{"action": "migrate"}'
Clean old namespaces
curl -X POST http://localhost:1933/api/v1/admin/migrate \
-H "Content-Type: application/json" \
-H "X-API-Key: <root-key>" \
-d '{"action": "cleanup"}'
CLI
ov --sudo admin migrate --output json
ov --sudo admin migrate --cleanup --output json
Response Example
{
"task_id": "legacy_migration_..."
}
# 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>"