Permissions & Roles
Overview
Keyban authenticates two kinds of identity, with two separate permission systems:
- Member roles govern human users in the Admin Console — Owner, Admin, Editor, Viewer.
- API key permissions govern programmatic access — every call carrying an
X-Api-Keyheader.
The two are orthogonal: a key issued by an Owner is not automatically Owner-equivalent.
Concepts
A small vocabulary set, used consistently throughout this page and the API.
| Term | Meaning |
|---|---|
| Role | Named bundle of permissions assigned to a member of an organization (Owner, Admin, Editor, Viewer). One role per member per organization. |
| Permission | A (resource, action) tuple — e.g. (dpp, create). Permissions are not assigned individually to humans; they're assigned through a role. |
| Resource | A surface of the platform that can be acted upon: organization, member, invitation, team, application, dpp, loyalty, billing, settings, agentWallet, apiKey. |
| Action | What a permission grants on a resource: read, create, update, delete. |
| API key permission | The same (resource, action) model, attached to an individual API key rather than to a role. Independent from the issuer's role. |
| Super-admin | A platform-level flag set on a Keyban-team user account, granting cross-organization access. Not a role within an organization, not exposed to partners. |
Identity
Member
Human user, signed into the Admin Console.
API key
Machine identity, sent in the X-Api-Key header.
Authorization
Role
Owner, Admin, Editor, or Viewer — bundle of permissions.
Permissions map
resource → actions[] attached directly to the key.
Resource
dpp
Digital Product Passports.
loyalty
Programmes, accounts, points.
application · billing · …
The other 9 resources defined in access-control.ts.
Member roles
Roles at a glance
| Role | Best for | Summary |
|---|---|---|
| Owner | Organization creator, CTO | Full control over the organization, including deletion. |
| Admin | Team leads, project managers | Manages members, settings, and all modules — except organization deletion. |
| Editor | Developers, content managers | Creates and edits products and campaigns; reads settings. |
| Viewer | Stakeholders, external reviewers | Read-only access to products and campaigns. |
The only Owner-exclusive action is organization.delete. Editor and Viewer differ on a single axis: write access to products and campaigns.
What each role can do
The tables below use the user-facing labels (« DPP products », « Loyalty campaigns », « Applications »). The corresponding API resources are dpp, loyalty, and application.
Products & Campaigns
| Action | Owner | Admin | Editor | Viewer |
|---|---|---|---|---|
| View DPP products | ||||
| Create DPP products | ||||
| Edit DPP products | ||||
| Delete DPP products | ||||
| View Loyalty campaigns | ||||
| Create Loyalty campaigns | ||||
| Edit Loyalty campaigns | ||||
| Delete Loyalty campaigns |
Applications
| Action | Owner | Admin | Editor | Viewer |
|---|---|---|---|---|
| View applications | ||||
| Create applications | ||||
| Edit applications | ||||
| Delete applications |
Team management
| Action | Owner | Admin | Editor | Viewer |
|---|---|---|---|---|
| View members | ||||
| Invite or remove members | ||||
| Change member roles | ||||
| Create or manage teams |
Organization & Billing
| Action | Owner | Admin | Editor | Viewer |
|---|---|---|---|---|
| View settings | ||||
| Edit organization settings | ||||
| Delete organization | ||||
| View and manage billing |
Manage members
To invite or change member roles, you need the Owner or Admin role yourself.
Invite a member
- Go to Organization → Members in the Admin Console.
- Click Invite Member.
- Enter the member's email and pick a role (Admin, Editor, or Viewer).
- The invitee receives a magic-link email; the invitation expires after a set window.
Change a member's role
- Go to Organization → Members, find the member.
- Click the role dropdown next to their name and pick the new role.
- The change takes effect immediately on their next page load. No data is lost — the member simply gains or loses access to certain actions. In-progress writes that exceed the new role are rejected by the API.
Typical role assignments
| Team member | Recommended role | Why |
|---|---|---|
| Company founder / CTO | Owner | Needs full control including billing and org deletion. |
| Project manager | Admin | Manages the team and settings, but shouldn't delete the org. |
| Developer / Designer | Editor | Creates and updates products and campaigns daily. |
| Client / External auditor | Viewer | Needs to see data without modifying anything. |
API key permissions
API keys carry their own permission map, independent from the role of the person who created them.
Permission shape
{
"<resource>": ["<action>", "<action>", ...],
...
}
Available resources and actions
The resources are the same as for member roles (defined in auth/access-control.ts):
organization, member, invitation, team, application, dpp, loyalty, billing, settings, agentWallet, apiKey.
Each resource accepts the same set of actions: read, create, update, delete.
Common scope recipes
| Use case | Permissions |
|---|---|
| Read-only audit script | { "dpp": ["read"], "loyalty": ["read"] } |
| Catalogue migration (one-shot) | { "dpp": ["create", "update", "read"] } |
| Loyalty backend with mint | { "loyalty": ["read", "update"] } |
| Shopify integration | { "dpp": ["create", "update", "read"] } |
Lifecycle
API keys are created, scoped, rotated, and revoked entirely from the Organization → API keys screen of the Admin Console. The full secret is shown once at creation time and cannot be re-displayed. There is no public REST surface for the key lifecycle today — the underlying authentication plugin (@better-auth/api-key) is integrated, but its routes are not part of Keyban's documented API.
How permissions are enforced
The Admin Console hides actions the current member's role doesn't grant — there are no greyed-out buttons. The API performs the same check on every request.
Two parallel checks, OR semantics
When an API call lands on the backend, the access-control gate accepts the request if either of the two paths grants the permission:
- The session's user is a member with a role that includes
(resource, action)for the calling organization, or - The
X-Api-Keyheader carries a permission map that includes(resource, action).
A failure on both paths returns 403 Forbidden with a structured JSON body — see How to handle Keyban API errors.
Because the gate is OR, a member with the Viewer role who also holds an API key carrying dpp: ["create"] can create passports through that key. Treat API keys as first-class identities — least-privilege scopes matter even for accounts whose human role is read-only.
Best practices
Least privilege
Pick the minimum role for each member, and the minimum scope for each API key. A read-only dashboard shouldn't carry create. A one-shot migration shouldn't carry delete.
One key per integration
Bind one key to one purpose — shopify-prod, migration-2026-q2, bi-dashboard. The blast radius on a leak is then limited to a single integration, and audit log entries identify the offender immediately.
Rotate proactively
Run the rotation pattern (create-deploy-revoke) at a known cadence, even when no incident pushed you to. Aim for once per quarter on long-lived integrations; immediately on any suspicion (committed to a public repo, shared in chat, exposed in a packet capture).
Set an expiry on temporary keys
For migrations, audits, demos: set expiresAt to the smallest reasonable window. Keyban does not currently send a reminder before expiry — set a calendar event of your own.
Audit the lifecycle
Every member role change and every API key creation/revocation is recorded by the Admin Console. Review the log periodically and after every team change.
Debug a 403
Run through this checklist before opening a support ticket. Most 403 responses fall into one of these four buckets.
-
Identify your identity
- If the call was made through the Admin Console, your active member role applies. Check it under Organization → Members → [your row].
- If the call was made with an
X-Api-Key, only the key'spermissionsapply. The session role of the key's creator is not considered.
-
Map the endpoint to a
(resource, action)pairPOST /v1/dpp/passports→(dpp, create).PATCH /v1/dpp/passports/:id→(dpp, update).POST /v1/loyalty/account/:id/mint→(loyalty, update).DELETE /v1/dpp/passports/:id→(dpp, delete).- When in doubt, consult
/backend-openapi— every operation lists its required permission.
-
Compare
- For a member role, look up the table in Member roles → What each role can do.
- For an API key, fetch the key in Organization → API keys and read its
permissionsmap. - If the required pair is absent, that's the cause. Either elevate the role (asking an Owner/Admin) or re-issue the key with a wider scope.
-
Escalate with context
- Include the request
instance(the path), the response body'sstatus, and your member id (or key prefix). - Reach out to your Keyban contact with this information.
- Include the request
What's not exposed today
To set expectations honestly:
- No public REST surface for API key lifecycle. Creation, listing, rotation, and revocation happen through the Admin Console only.
- No quota or per-key rate-limit fields surfaced through the UI. The
AuthApiKeyentity carriesrateLimitMax/refillIntervalcolumns but Keyban currently configures rate limits at the platform level. - No expiry alerts. Expired keys silently start failing — set a reminder when you create one with
expiresAt. - No attribute-based access control (ABAC). Permissions today are role-and-resource based; finer-grained rules (« only edit DPP products you created yourself ») are not modelled. Cross-team isolation must be done by splitting into multiple organizations.