Skip to content

API keys

Audience

PLANA staff, integration partners, customers issuing per-workspace API keys for their own automations.

PLANA uses two kinds of API keys:

KindPrefixIssued byUsed for
Workspace API keypa_live_…Tenant admin in BOS → Settings → API keysCustomer automations against their own workspace
Service API keyX-API-Key (no prefix)PLANA platformService-to-service calls inside the cluster

Workspace API keys (pa_live_…)

These are the keys customers create to call PLANA's API from their own scripts. Issued from the BOS UI; bcrypt-hashed in the database; never visible after creation.

Lifecycle

  1. Tenant admin opens BOS → Settings → API keys → "Create key"
  2. Names the key (e.g. "Nightly stock sync") and selects scopes
  3. BOS calls POST /api/portal/apikeys on pulse-account-api
  4. The API generates a token: prefix pa_live_ + 32 random bytes base32-encoded (so the token is pa_live_AB7K2… — readable in logs for the prefix, but the body itself is high-entropy)
  5. The API stores only the bcrypt hash of the token in the api_key table on pulse_account
  6. The plaintext is shown to the user once, in a copy-to-clipboard field
  7. Subsequent calls authenticate with the key in the Authorization header: Authorization: Bearer pa_live_…

What the key can do

A workspace API key is scoped to one workspace. The platform identifies which workspace from the key prefix (looked up in api_key) and authorises calls accordingly.

ScopeWhat it grants
dashboard:readKPIs, cashflow, alerts
agents:invokePOST to /api/agents/:id/chat
banking:readPSD2 account and transaction read
webhooks:writeReceive workspace webhooks
*Everything — discouraged but available

A key without an explicit scope set defaults to dashboard:read, the lowest-risk grant.

What the key CANNOT do

  • Write to Odoo records directly (no XML-RPC bridge)
  • Access another workspace's data (the workspace binding is enforced at the API layer)
  • Modify the workspace's own configuration (e.g. team membership)

For those operations the user must log in interactively. API keys are for automation, not for delegating administrative actions.

Revocation

The tenant admin can revoke a key from the same Settings → API keys page: "Revoke" deletes the row in api_key and the key stops working immediately. There is no time-window or grace period.

Audit

Every API call authenticated with a workspace key is logged with the key prefix (not the full token) and the workspace ID. The log is in the PLANA:executions:{workspace} Redis log (visible in BOS → Execution log) and mirrored to Loki for 90-day retention.

Storage

WhereWhat
Database (pulse_account.api_key)bcrypt(token), name, created_at, last_used_at, revoked_at, workspace_id, scopes
Never anywhere elseThe plaintext token

If a customer loses their token, the only option is to revoke and re-issue. We cannot recover it.

Service API keys (X-API-Key)

These are the keys PLANA services use to call each other. They are configured per-service in SOPS and never rotated by a UI.

Inventory

Calling serviceTarget serviceHeader keySOPS path
pulse-account-apipulse-bankingX-API-Keypulse_account.banking_api_key
pulse-account-apiai-agentsX-API-Keypulse_account.agents_api_key
pulse-account-apipulse-eventsX-API-Keypulse_events.publisher_key
ai-agentspulse-data (Neural Business Network)X-API-Keyai_agents.pulse_data_key
pulse-notifications(callers)X-API-Keypulse_notifications.api_key
pulse-billing (Stripe webhook receiver)pulse-eventsX-API-Keypulse_events.publisher_key
saas-orchestrator (historical)pulse-eventsX-API-Keypulse_events.publisher_key

The orchestrator entry is historical — the service was deleted in May 2026. The key would only be re-used if we re-add a similar workload.

Rotation

Service keys rotate on a deliberate schedule, not automatically:

  1. Generate a new key (32 random bytes hex-encoded)
  2. Update SOPS at the path above
  3. Update the consumer service's deployment to pick up the new secret (Flux reconciles, the deployment rolls)
  4. Verify the consumer is using the new key (logs)
  5. Update the provider service to revoke the old key
  6. (Optional) Validate by sending a request with the old key and expecting 401

We rotate service keys after any incident that might have exposed them, and on an annual cadence otherwise. The cadence is tracked in the security calendar.

Storage

WhereWhat
SOPS-encrypted YAML (infra/secrets/plana-pulse.enc.yaml)Plaintext key
Vaultwarden mirrorSecondary copy (per the
secrets storage policy)
Kubernetes Secret in the namespace of the consuming serviceMounted into the pod as env var

The plaintext never appears in git in cleartext; only the SOPS-encrypted form is committed.

Authentication layering

API calls are authenticated in layers:

  1. Layer 1 — Network: NetworkPolicies allow only the right caller pods to reach the target service Service. A leaked key alone cannot reach a service that's blocked at the network level.
  2. Layer 2 — API key: The target service checks X-API-Key (for service-to-service) or Authorization: Bearer pa_live_… (for workspace keys).
  3. Layer 3 — Authorization: For workspace keys, the service further checks the workspace binding and the scope.

A leaked workspace key alone (without network access) is still bounded to one workspace; a leaked service key reaches only services its caller's NetworkPolicy already allows.

Customer-facing best practices

When a customer asks how to use their API key safely:

  • Store the key in a secrets manager (HashiCorp Vault, AWS Secrets Manager, doppler, 1Password CLI), not in source code
  • Use scope-limited keys — issue one key per automation, scoped to the minimum it needs
  • Rotate annually — revoke the old key and issue a new one yearly
  • Monitor BOS → Execution log — every API call shows up there with the key prefix; if you see calls you don't recognise, revoke

These are documented for customers at BOS → Settings → API keys.

Where to read more

© PLANA Digital Ltd.