Veefore docs

API keys

Programmatic access for your own services and CI — scoped to a role, optionally to a workspace, and shown exactly once.

An API key authenticates a service or a pipeline, not a person and not an agent. Agents authenticate with a key pair at the gateway; people authenticate with a session. API keys are for your deployment tooling, your CI, and your own backend services talking to the control plane.

#Create one

bash
curl -X POST https://veefore.example.com/v1/api-keys \
  -H "Authorization: Bearer $EXISTING_KEY" \
  -H 'content-type: application/json' \
  -d '{
    "name": "ci-policy-gate",
    "role_id": "role_01JAV…",
    "workspace_id": "wsp_01JAV…",
    "expires_in_days": 90
  }'
json
{
  "data": {
    "api_key": { "id": "apk_01JAV…", "name": "ci-policy-gate", "created_at": "…" },
    "token": "aos_sk_…",
    "note": "Store this key now. It cannot be retrieved again."
  }
}

`token` is shown exactly once

Only a hash is stored. There is no endpoint that returns it again, and there never will be. Capture it from this response or create a new key.

Note the field name: the secret is token, not secret. api_key is the metadata record.

namestringrequired

What this key is for. It appears in the audit trail on every action the key takes, so make it specific — ci-policy-gate beats ci.

role_idstringrequired

The role whose permissions the key carries. GET /v1/roles to list them.

workspace_idstringoptional

Scope the key to one workspace. Do this unless the key genuinely needs organization-wide reach.

expires_in_daysnumberoptional

1 to 730. Set it. A key with no expiry is a key that outlives the reason it was created.

#Use one

bash
curl -s https://veefore.example.com/v1/agents \
  -H "Authorization: Bearer aos_sk_…" \
  -H 'x-veefore-workspace: wsp_01JAV…' \
  -H 'x-veefore-environment: env_01JAV…'

Same header as a session token. The scope headers are optional — omit them and you get the organization-wide view, subject to the key's permissions and its workspace scope.

#Pick the smallest role

An API key's permissions are its role's permissions. The roles and their contents are in Permissions and roles.

Key purpose Role
CI policy gate DEVELOPER, or a custom role with policy:read + policy:simulate
Deployment pipeline Custom: policy:read, policy:activate, environment:set_mode
Monitoring and dashboards VIEWER
Audit export to a SIEM Custom: audit:read, audit:export
Agent provisioning service Custom: agent:create, agent:read, tool:read

Never `OWNER`

OWNER holds every permission including organization:delete. A key in a CI environment variable is a key that is in your CI provider's storage, in build logs if anyone echoes the environment, and in whatever a compromised build step can read.

Create a custom role with exactly the permissions the key needs. It takes two minutes and it bounds the damage.

Two further protections apply automatically and are worth knowing about:

High-impact permissions cannot come from a wildcard. agent:revoke, policy:activate, security:emergency_action and the rest must appear on the role explicitly.

Step-up permissions are unavailable to API keys. Step-up requires a human re-authenticating, and an API key has no human. A key holding a step-up permission still cannot perform the operation — which means a CI key cannot activate a policy on its own. That is deliberate: activation is a decision, not a build step.

#Store it properly

Where Verdict
Your CI provider's encrypted secret store Correct
Your cloud provider's secret manager Correct
An environment variable injected at runtime Acceptable
A file in the repository Never
A .env that is gitignored Risky — easy to commit by accident
Checked into a Dockerfile or image layer Never

npm run quality:audit fails the build on a committed secret, which catches some of this. Do not rely on it as the control.

#Rotate and revoke

bash
curl -s https://veefore.example.com/v1/api-keys \
  -H "Authorization: Bearer $EXISTING_KEY"

curl -X POST https://veefore.example.com/v1/api-keys/apk_01ABC/revoke \
  -H "Authorization: Bearer $EXISTING_KEY"

Revocation is immediate. To rotate, create the new key, deploy it, then revoke the old one — in that order, so there is no window where nothing works.

Rotate on a schedule and immediately on any suspicion. Set expires_in_days so expiry happens even when nobody remembers.

#Everything is attributed

Every action an API key takes is recorded against that key, so the audit trail distinguishes "the CI gate read the policy" from "a developer read the policy". principal.type is API_KEY, which is also a policy field:

json
{ "kind": "condition", "id": "c-principal", "field": "principal.type", "operator": "eq", "value": "API_KEY" }

Useful for rules that should apply to automation but not to people, or the reverse.

Identity, authority, policy and enforcement for autonomous AI agents.