standards
Workspaces and API keysAccess

Workspaces and API keys

Authenticate every call with one key, and know exactly what that key may read and write.


One Bearer header authenticates everything. The key names its workspace, so no request ever carries a tenant parameter, and the key's roles decide what it may touch. A workspace holds your objects, records, files and people; in the SDK and the API it is called a tenant.

The Authorization header

Send the key as a Bearer token on every request:

curl https://api.standards.new/v1/records/contacts \
  -H "Authorization: Bearer stndrds_your_api_key"

Keys are read from the Authorization header only. A key in a cookie or query string is ignored.

Two refusals cover authentication and authorization:

StatusCodeMeaning
401noneInvalid, expired, or revoked API key. One message for all three cases.
403SCHEMA_FORBIDDENThe key is valid but its roles do not grant the action. The message never names the object or the action.

Creating keys

POST /api-keys creates a key. The caller needs api-keys:create, which the built-in Owner role holds.

curl -X POST https://api.standards.new/v1/api-keys \
  -H "Authorization: Bearer stndrds_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{ "name": "status-page", "roleId": "role-uuid", "expiresAt": "2027-01-01T00:00:00Z" }'

The plaintext secret is returned once, in this response. Store it in a secrets manager. Afterwards the API exposes the key's metadata only (name, prefix, creation date, last use, and the roles it holds).

What a key may do:

  • Without roleId, the key holds the member role. If your own access does not cover member, the request is refused with 400 SCHEMA_VALIDATION_FAILED: pass a narrower roleId.
  • With roleId, the key holds that role and nothing else — one dedicated role per integration. You can bind a role only when your own permissions cover everything it grants; otherwise 403.
  • A personal key also acts within its owner's access: it may do only what both its role and its owner's current roles allow. Demote the owner and the key narrows on its next request; suspend or remove the owner and the key is refused.
  • owner goes on a personal key only. An Owner may give their own personal key the owner role (the app offers it in the personal key dialog), and it still narrows with them. A workspace key acts for nobody, so owner is refused there with 403 SCHEMA_API_KEY_OWNER_ROLE_REFUSED, whoever asks: at creation, and when roles are assigned to the key afterwards.
  • The permissions array in the body is accepted and ignored.

Listing keys

GET /api-keys lists the workspace's keys, and GET /api-keys/:id reads one. Both need api-keys:read. Each key carries a roles array: the roles the key holds, which is what it may do. An abridged entry:

{
  "id": "0b6f…",
  "name": "status-page",
  "prefix": "stndrds_1a2b3c4d_",
  "userProfileId": null,
  "roles": [{ "id": "role-uuid", "name": "status_reader", "label": "Status reader" }]
}
  • A key created with roleId lists that role.
  • A key created without roleId lists member.
  • An empty array means the key holds no role, so it has no permissions.
  • Each entry carries the role's id, name and label only, never its permissions.
  • roles is absent from the creation response.

Each key also reports whom it acts for:

  • authorityActorId — the person the key acts for. Its permissions are the intersection of its own roles and this person's.
  • detachedAt / detachedBy — set on a key that acts with its own roles alone.

Personal and workspace keys

A key is personal or workspace:

  • Personal — pass userProfileId (your own) or personal: true. It acts for you: never more than your own access, re-read on every request. Only you can revoke it. A personal API key may create another personal key for its owner; a workspace key or an agent owns no profile and is refused with 403 SCHEMA_FORBIDDEN.
  • Workspace — no owner. A new workspace key is detached: it acts with its own roles alone, bounded when it is created by what its creator could grant.

Keys created before this release

No existing key was converted. Every key created by a person — directly or through another key — now acts for that person: it keeps what it could do while that person keeps their access, and narrows if they are demoted. A key whose chain does not end on a person (created by an agent, or whose creator no longer exists) and a key whose creator is suspended are refused until an administrator detaches it.

To make an integration key independent of the person who created it, detach it: POST /api-keys/:id/detach, standards keys detach <id> --yes, or Detach from creator in the API keys settings. Detaching needs api-keys:create, applies to workspace keys only, and is refused with 403 when the key holds a role wider than your own access. It is recorded in the activity log.

Permissions

A permission grants a set of actions on one target, within one of two scopes. On the wire:

{ "scope": "object", "target": "contacts", "actions": ["read", "update"] }

In prose, this documentation writes resource:action, so architect:update means the update action on the architect system resource.

  • object scope is business data. The target is an object name, or * for every object.
  • system scope is the platform itself. The target is one of the catalog's resources, or * for all of them. Only owner can be given *: saving it on any other role is refused, and no other role holds it. There is no catch-all resource outside the catalog: an unlisted target, or an action a resource does not define, is refused with 400 SCHEMA_PERMISSION_INVALID_GRANT.

Actions

ActionGrants
readFetching data.
createCreating records or resources.
updateModifying records or resources.
deleteSoft-deleting and restoring.
manageAdministrative operations, such as purging records for good.

Actions are independent, not tiers: manage does not imply read, and delete does not imply update. * in a stored actions list means all five actions, manage included.

System resources

The full list of resources, their capability groupings, incremental levels and defaults lives on its own page: Roles and permissions. architect:update syncs a schema source and architect:delete removes one; people:read unlocks the user kind of the MCP search and read tools and the GET /actors directory.

Record routes check an object-scope permission on the object they touch: read to list, search and get, create to create, update to update and attach documents, delete to delete and restore, manage to purge (object:* only). Every other route checks the matching action on its own system resource.

Built-in roles

Every workspace starts with three roles you will meet:

RoleObject accessSystem access
ownerAll actions on all objects, including purge.All actions on all resources.
memberread, create, update, delete on all objects — never purge.read on people, architect, agents, settings, audit, forms; read, create, update, delete on connectors, files and documents.
agent_defaultread, create, update, delete on all objects.read, create, update on documents and files; read on env-vars.

See Roles and permissions for the full catalog, the object grant's purge switch, and how to restrict someone below member with a custom role.

A Member cannot change the schema, mint keys or administer people. Those stay with Owner unless a custom role grants them. Built-in roles cannot be edited or deleted.

Only an actor that already holds Owner can grant the Owner role. Any other role can be assigned, to a key or a person, only by an actor whose own permissions cover everything it grants; otherwise the assignment is refused with 403. Custom roles, and their assignment to keys and people, are managed from the People area of the app.

Expiry and revocation

  • Expiry: expiresAt is optional. An expired key is refused like an invalid one.
  • Revocation: DELETE /api-keys/:id, or standards keys revoke <id> --yes. The next request with that key gets a 401.
  • The last owner is protected: revoking the only usable Owner credential the workspace has left is refused with 400 SCHEMA_VALIDATION_FAILED, Cannot revoke the last owner role. Assign another owner first. An Owner key that acts for a person does not count as an owner of its own, since it can do nothing that person cannot: only a detached key keeps a workspace owned once its last human Owner is gone.
  • Auditing: each key records when it was last used, so stale keys are easy to spot.

Keys are server-side secrets

Never commit a key or ship it in browser code. The SDK refuses to run with an API key in a browser. Rotate a key you suspect has leaked: create the replacement, switch, then revoke the old one.

Signed-in sessions

A person signed in to the app calls the API with a short-lived session access token instead of a key. Signing out, including sign out everywhere, revokes the session's refresh token at once: no new access token can be obtained. An access token already issued keeps reaching the API until it expires, about 1 hour on standards.new: the API checks each token's signature locally, without a call to the auth server, so it cannot see a sign-out before then. A dedicated instance can narrow that window to 30 seconds.

Next steps

  • Errors: what a 401, a 403 and every other refusal looks like on each door.
  • SDK: createStandards({ baseUrl, apiKey }) and the server-only rule.
  • MCP OAuth: sign an agent in with scopes instead of a pasted key.
  • CLI: standards login stores a key per instance profile.
  • Book a demo