Access Control
VectorAxis organises everything into organizations and workspaces, and controls who can do what with roles. People and applications act through platform API keys (va_…), and each key’s scope and role decide exactly which features and actions it can reach. This page covers the model end-to-end: the hierarchy, the roles, how to create a key for each role, the full permission matrix, and the developer API for every resource.
An organization is the billing & identity boundary. Inside it, workspaces are isolated environments (think dev and prod, or one per team or customer), and every resource lives in a workspace.
Organization
├─ Workspace A (dev)
└─ Workspace B (prod)
├─ Virtual keys
├─ Prompts
├─ Routing configs
└─ Guardrails| Key | What it is |
|---|---|
| va_… platform API key | Your identity & permissions. Authenticates to the management and inference APIs and decides what you may do. Carries a scope + role. This page. |
| vk-… virtual key | A safe stand-in for a provider credential, used as the api_key on inference requests. Holds no management permissions. See Virtual Keys. |
When you create a platform key (or invite a person) you choose a scope (how wide it reaches) and a role (how much it can do within that reach).
| Scope | Reaches | Carries a… |
|---|---|---|
| org | The whole organization — every workspace and all org-level settings. | an organization role (owner / admin / member) |
| workspace | A single workspace and the resources inside it. | a workspace role (admin / member) |
| Role | In plain terms |
|---|---|
| owner org | Everything an admin can do, plus money & lifecycle: subscriptions, credits, and deleting the organization. |
| admin org | Runs the org: manage workspaces, members, and platform keys; full control of every workspace’s resources. |
| member org | Read-only at the org level. Has no access to a workspace unless separately added to it. |
| admin workspace | Full control of one workspace: create & manage its keys, prompts, configs, guardrails, and members. |
| member workspace | Can view a workspace’s resources and make inference calls, but can’t create or change them. |
Open API Keys and choose Create key. You’ll set:
- Name — a label so you can recognise the key later (e.g. “CI pipeline”, “Eng workspace admin”).
- Scope — org for an organization-wide key, or workspace to confine it to one workspace.
- Role — admin or member. For an org-scoped key that level applies across every workspace; for a workspace key it applies within that one workspace. This is what grants the permissions in the matrix below. (A key never carries the org owner role — owner is a person-only membership role for billing & lifecycle.)
- Expiry (optional) — a date after which the key stops working.
What each role can do
full = create, edit, and delete · view = read & use only · — = no access.
These act on resources that live in a workspace. Org owners and admins have admin here in every workspace automatically.
| Feature / action | Workspace admin | Workspace member |
|---|---|---|
| Virtual keys — create / edit / delete | full | — |
| Virtual keys — view & use | full | view |
| Prompts — create / edit / delete / publish | full | — |
| Prompts — view & run | full | view |
| Routing configs — create / edit / delete | full | — |
| Routing configs — view | full | view |
| Guardrails — create / edit / delete / enable | full | — |
| Guardrails — view | full | view |
| Workspace members — invite / change role / remove | full | — |
| Workspace members — view | full | view |
| Logs & analytics — view | full | view |
| Make inference calls (chat, prompts) | full | full |
| Feature / action | Org owner | Org admin | Org member |
|---|---|---|---|
| Workspaces — create | full | full | — |
| Workspaces — view (list) | full | full | view |
| Org members — invite / change role / remove | full | full | — |
| Org members — view | full | full | view |
| Platform API keys — create / revoke | full | full | — |
| Platform API keys — view (list) | full | full | view |
| Logs — export | full | full | — |
| Billing & subscription — view | full | full | view |
| Credits — buy / withdraw / auto-recharge | full | — | — |
| Subscription — change plan / checkout / portal | full | — | — |
| Delete the organization | full | — | — |
Developer API reference
Authenticate every call with Authorization: Bearer va_…. Management endpoints live under /v1. Each resource lists its endpoints with the role required, then the create payload and response. Required fields are marked *. For field-by-field detail, see each resource’s own page: virtual keys, routing configs, guardrails.
| Method | Path | Who can call it |
|---|---|---|
| POST | /v1/virtual-keys | Workspace admin |
| GET | /v1/virtual-keys | Workspace member |
| GET | /v1/virtual-keys/{slug} | Workspace member |
| PUT | /v1/virtual-keys/{slug} | Workspace admin |
| DELETE | /v1/virtual-keys/{slug} | Workspace admin |
| POST | /v1/virtual-keys/{slug}/disable | Workspace admin |
| POST | /v1/virtual-keys/{slug}/enable | Workspace admin |
Create payload * = required:
{
"name": "OpenAI Production", // *
"organization": "acme", // *
"workspace_id": "<workspace-uuid>",
"credential_mode": "BYOK", // BYOK | PLATFORM
"credentials": [
{ "provider": "OPENAI", "key": "sk-..." }
],
"model_allowlist": ["gpt-4o", "gpt-4o-mini"],
"rate_limit_rpm": 100,
"credit_limit_usd": 50.0,
"credit_reset_policy": "MONTHLY",
"expires_at": "2026-12-31T00:00:00Z"
}Create response — the slug is what you use as an api key:
{ "slug": "vk-1a2b3c4d5e6f7a8b", "key_hint": "...abcd" }| Method | Path | Who can call it |
|---|---|---|
| POST | /v1/configs | Workspace admin |
| GET | /v1/configs | Workspace member |
| GET | /v1/configs/{slug} | Workspace member |
| PUT | /v1/configs/{slug} | Workspace admin |
| DELETE | /v1/configs/{slug} | Workspace admin |
| POST | /v1/configs/validate | Any member |
| GET | /v1/configs/{slug}/audit-log | Workspace member |
| GET | /v1/configs/{slug}/analytics | Workspace member |
{
"name": "Premium router", // *
"organization": "acme", // *
"workspace_id": "<workspace-uuid>",
"strategy_mode": "FALLBACK", // * SINGLE | LOADBALANCE | FALLBACK | CONDITIONAL
"on_status_codes": [429, 500, 502, 503, 504],
"targets": [ // *
{ "name": "primary", "virtual_key_slug": "vk-1a2b3c4d5e6f7a8b" },
{ "name": "backup", "virtual_key_slug": "vk-c7d4e1f2a3b95a8b" }
]
}Response is the full config detail, including its slug. Reference it on requests with x-config.
| Method | Path | Who can call it |
|---|---|---|
| POST | /v1/prompts | Workspace admin |
| GET | /v1/prompts | Workspace member |
| GET | /v1/prompts/{slug} | Workspace member |
| PUT | /v1/prompts/{slug} | Workspace admin |
| DELETE | /v1/prompts/{slug} | Workspace admin |
| POST | /v1/prompts/{slug}/versions | Workspace admin |
| GET | /v1/prompts/{slug}/versions | Workspace member |
| POST | /v1/prompts/{slug}/versions/{n}/publish | Workspace admin |
Create a prompt (the container), then add a version with the actual messages:
// POST /v1/prompts
{ "name": "Summarize Article", "description": "Summarizes an article",
"organization": "acme", "workspace_id": "<workspace-uuid>" } // name & organization required
// POST /v1/prompts/{slug}/versions
{ "messages": [
{ "role": "system", "content": "You summarize articles." },
{ "role": "user", "content": "Summarize: {{article}}" }
],
"model": "gpt-4o-mini", "temperature": 0.3 }Run a published prompt at POST /v1/prompts/{slug}/completions (authenticated by a virtual key or routing config, like any inference call).
| Method | Path | Who can call it |
|---|---|---|
| POST | /v1/guardrails | Workspace admin |
| GET | /v1/guardrails | Workspace member |
| GET | /v1/guardrails/{slug} | Workspace member |
| PUT | /v1/guardrails/{slug} | Workspace admin |
| DELETE | /v1/guardrails/{slug} | Workspace admin |
| POST | /v1/guardrails/{slug}/enable | Workspace admin |
| POST | /v1/guardrails/{slug}/disable | Workspace admin |
{
"name": "Safety policy", // *
"organization": "acme", // *
"workspace_id": "<workspace-uuid>",
"enabled": true,
"checks": [ // * one or more checks
{ "type": "PII_DETECTOR", "target": "BOTH", "action": "REDACT" },
{ "type": "banned_words", "target": "INPUT", "action": "BLOCK",
"config": { "words": ["foo", "bar"] } }
]
}Bind guardrails to the virtual key so every request made with it is checked without the caller sending anything, or apply one per request with x-guardrail: <slug> (which adds to the key’s, never replaces them). See the guardrails page for every validator and its config.
| Method | Path | Who can call it |
|---|---|---|
| POST | /v1/workspaces | Org admin |
| GET | /v1/workspaces | Org member |
| GET | /v1/workspaces/{slug} | Workspace member |
| PUT | /v1/workspaces/{slug} | Workspace admin |
| DELETE | /v1/workspaces/{slug} | Workspace admin |
{ "name": "Engineering", "organization": "acme", // name & organization required
"slug": "eng", "description": "Eng team workspace" }{ "id": "<uuid>", "organization": "acme", "slug": "eng",
"name": "Engineering", "created_at": "...", "updated_at": "..." }| Method | Path | Who can call it |
|---|---|---|
| GET | /v1/organizations/{org}/members | Org member |
| POST | /v1/organizations/{org}/members | Org admin |
| PUT | /v1/organizations/{org}/members/{userId} | Org admin |
| DELETE | /v1/organizations/{org}/members/{userId} | Org admin |
| GET | /v1/workspaces/{slug}/members | Workspace member |
| POST | /v1/workspaces/{slug}/members | Workspace admin |
| PUT | /v1/workspaces/{slug}/members/{userId} | Workspace admin |
| DELETE | /v1/workspaces/{slug}/members/{userId} | Workspace admin |
Invite to the org (an org member needs a workspace to land in):
// POST /v1/organizations/{org}/members
{ "email": "[email protected]", "role": "member", // email & role required
"workspace_id": "<workspace-uuid>", "workspace_role": "member" }
// POST /v1/workspaces/{slug}/members
{ "email": "[email protected]", "role": "admin" } // email & role requiredYou can invite by email before the person has ever signed in — the membership reconciles to their account on first login.
| Method | Path | Who can call it |
|---|---|---|
| POST | /v1/api-keys | Org admin, or workspace admin (workspace-scoped) |
| GET | /v1/api-keys | Org member |
| DELETE | /v1/api-keys/{keyId} | Org admin, or that workspace’s admin |
{
"name": "CI pipeline", // *
"organization": "acme", // *
"scope": "workspace", // * org | workspace
"role": "admin", // * admin | member
"workspace_id": "<workspace-uuid>", // required for workspace scope
"expires_at": "2026-12-31T00:00:00Z"
}Create response — raw_key is shown only once:
{ "key": { "id": "<uuid>", "name": "CI pipeline", "scope": "workspace",
"role": "admin", "key_hint": "...wxyz", "status": "ACTIVE" },
"raw_key": "va_DA3saInUNRHzl19hHC9p42..." }| Method | Path | Who can call it |
|---|---|---|
| GET | /v1/billing/subscription | Org member |
| GET | /v1/billing/credits | Org member |
| GET | /v1/billing/credits/transactions | Org member |
| GET | /v1/billing/entitlements | Org member |
| POST | /v1/billing/credits/purchase | Org owner |
| POST | /v1/billing/credits/withdraw | Org owner |
| PUT | /v1/billing/credits/auto-recharge | Org owner |
| POST | /v1/billing/checkout | Org owner |
| POST | /v1/billing/subscription/change | Org owner |
| POST | /v1/billing/portal | Org owner |
// POST /v1/billing/credits/purchase (and /withdraw)
{ "amount_usd": 100.0 } // amount required| Method | Path | Who can call it |
|---|---|---|
| GET | /v1/logs | Member |
| GET | /v1/logs/{id} | Workspace member |
| GET | /v1/analytics/summary | Member |
| GET | /v1/analytics/timeseries | Member |
| GET | /v1/analytics/breakdown | Member |
| POST | /v1/logs/export | Org admin |
| GET | /v1/logs/export/{jobId}/download | Org admin |
Scope a read to one workspace with ?workspace_id=…. Org members must scope to a workspace they belong to; org admins can read across the whole org. More on the logs page.