
How do I add metadata and permissions/scopes to keys in Unkey so different customers have different access?
Controlling what each customer can access is one of the main reasons to use an API key platform like Unkey. By combining key metadata with role-based permissions or scopes, you can give different customers tailored access while keeping your API surface simple and secure.
This guide explains the concepts, design patterns, and practical steps for adding metadata and permissions/scopes to Unkey keys so each customer has different access.
Key concepts: metadata, roles, and scopes in Unkey
Before designing your model, it helps to separate three concerns:
- Key – A secret token a client sends with each request. In Unkey, a key can be verified via the API or SDK and tracked in real-time analytics.
- Metadata – Arbitrary structured data attached to a key (for example:
customerId,plan,features,environment). Metadata doesn’t enforce access by itself but is used by your API or auth layer to make decisions. - Permissions / scopes – Fine-grained access rules that define what a key is allowed to do (for example:
read:invoices,write:users,admin). In Unkey, these are typically modeled via role-based access control (RBAC) or a permission list you associate with a key or its owner.
Unkey’s platform is:
- API-first / UI-first – You can manage keys, metadata, and access via the REST API or the dashboard.
- Role-based – It supports granular access privileges with either role-based or permission-based control, and permission changes are propagated globally in seconds.
Your job is to combine these building blocks into a clear, maintainable access model.
Designing an access model for different customers
Different customers usually need different combinations of:
- Which resources they can access (e.g., only their own data)
- Which operations they can perform (read-only vs full write)
- Which product features or environments they have (free vs paid, sandbox vs production)
A practical approach is to:
-
Use metadata for “who and what”
customerId– which tenant the key belongs toaccountType/plan– free, pro, enterpriseenvironment–sandbox,productionfeatureFlags– e.g.,{ "betaFeatureX": true }
-
Use roles/scopes for “what they can do”
role–read_only,standard,adminscopes– granular capabilities likeread:users,write:users,read:billing, etc.
Your API then:
- Reads the key’s metadata and attached role/permissions after verification.
- Enforces authorization based on both – for example:
- “Only access resources where
resource.customerId === key.metadata.customerId” - “Only call write operations if
scopesincludeswrite:<resource>” - “Enable premium endpoints only if
plan === 'pro'orfeatureFlags.superSearch === true”
- “Only access resources where
Attaching metadata to keys in Unkey
Although the exact API details evolve, the pattern is:
- Create a key with metadata (via REST or SDK)
- Verify the key on each request
- Read the metadata from the verification result
- Authorize based on that metadata
Example metadata to attach per customer
Common fields you might include:
{
"customerId": "cust_1234",
"plan": "pro",
"environment": "production",
"features": {
"advancedReporting": true,
"betaFlags": ["new-dashboard"]
},
"orgId": "org_5678"
}
You can vary this payload per customer so that keys are “self-describing” and carry all the information you need for access control and analytics.
Using Unkey’s RBAC for permissions/scopes
Unkey supports role-based access control with:
- Roles – collections of permissions, such as
viewer,editor,admin. - Permissions – granular privileges, like:
users.readusers.writebilling.readprojects.manage
Because permission changes are propagated globally in seconds, you can:
- Promote/demote customers (e.g., upgrade from basic to pro) without rotating keys.
- Turn features on/off at the role level and have all associated keys see the change nearly instantly.
A typical setup for customer-specific access
-
Define logical roles
For example:
customer_read_onlyusers.read,projects.read
customer_standard- All of the above plus
projects.write,webhooks.manage
- All of the above plus
customer_enterprise- All standard permissions plus
billing.read,audit.logs.read
- All standard permissions plus
-
Map roles to plans
free→customer_read_onlypro→customer_standardenterprise→customer_enterprise
-
Assign roles to keys or API consumers
When creating or updating a key, associate the appropriate role (or set of permissions) based on the customer’s plan and feature set.
-
Update roles centrally as your product evolves
If you add a new feature requiring
search.advanced:- Add the new permission to
customer_enterprise - All enterprise customers get access without changing existing keys
- Add the new permission to
End-to-end flow: per-customer scopes with metadata
1. Create a key per customer with metadata
When a customer signs up or purchases a plan:
- Generate a key using Unkey’s API
- Attach metadata and assign appropriate roles/scopes
Example metadata strategy:
{
"customerId": "cust_1234",
"plan": "pro",
"role": "customer_standard",
"environment": "production"
}
2. Store the key securely on the customer side
Customers use their Unkey-generated secret (e.g., sk_1234abcdef) in requests to your API.
3. Verify the key with Unkey on each request
Using the TypeScript SDK example from the docs:
import { Unkey } from "@unkey/api";
const unkey = new Unkey({
rootKey: process.env["UNKEY_ROOT_KEY"] ?? "",
});
const result = await unkey.keys.verifyKey({
key: "sk_1234abcdef",
});
if (result.error) {
// handle network or verification error
}
if (!result.valid) {
// reject unauthorized request
}
// result now contains data such as key metadata, roles, scopes (depending on your setup)
// use these to authorize the request
4. Use metadata and scopes inside your API
Inside your request handler:
- Read the
customerIdfrom key metadata. - Read role/scopes and compare against the requested operation.
- Enforce tenant isolation and feature access:
Pseudo-code:
// 1. Load resource requested
const resource = await db.getProject(req.params.projectId);
// 2. Enforce tenant boundary
if (resource.customerId !== keyMetadata.customerId) {
return res.status(403).json({ error: "Forbidden" });
}
// 3. Enforce scopes/permissions
if (!scopes.includes("projects.write")) {
return res.status(403).json({ error: "Insufficient permissions" });
}
// 4. Proceed with action
// ...
This pattern ensures that:
- A key for one customer cannot access another customer’s data.
- Even within a single customer, keys are limited by what scopes/roles you’ve assigned.
Modeling different access for different types of customers
Below are concrete patterns you can apply depending on your SaaS model.
1. Free vs paid vs enterprise plans
Metadata
plan: "free" | "pro" | "enterprise"planExpiresAt: ISO8601 timestamp
Roles/scopes
role: "customer_free",customer_pro,customer_enterprisescopes:- Free →
read:basic - Pro →
read:basic,write:basic,read:advanced - Enterprise → All scopes + admin or custom scopes
- Free →
Enforcement
- Use
planmetadata for billing logic (e.g., limit usage or show warnings). - Use
role/scopesto control which endpoints are accessible.
2. Environment-based keys (sandbox vs production)
Metadata
environment: "sandbox" | "production"
Rules
- Sandbox keys may:
- Only reach a test database
- Be rate-limited differently
- Have restricted scopes (e.g., no access to live billing)
Enforce via:
- Routing (sandbox vs production services)
- Security policies that check
environment === 'production'before allowing sensitive actions
3. Organization-level vs user-level keys
You may want keys at different levels:
- Org-level keys – used by backend integrations or admin dashboards
- User-level keys – personal API keys
Metadata
orgId:"org_ABC"userId:"user_123"(optional for user-level keys)keyType:"org"or"user"
Permissions strategy
- Org keys may have:
- Wider scopes but used from trusted environments
- User keys:
- Scopes tied to individual user permissions (for example, team member roles)
Safely evolving metadata and permission models
As your product grows, you’ll likely change how you structure metadata and scopes. With Unkey, you can do this with minimal disruption:
-
Use metadata as a versioned contract
Add a field like
metaVersion: 1to keys, and in your API, branch logic by metadata version. This lets you migrate gradually. -
Add new fields instead of renaming/removing
Rather than changing
plantosubscriptionTier, introducesubscriptionTierand keepplanuntil all keys are migrated. -
Use roles as stable abstractions
Treat roles as the public contract; you can rewire underlying permissions without changing which role is attached to each key.
-
Leverage rapid global propagation
Because permission changes propagate globally in seconds, you can:
- Roll out new permissions quickly
- Revert misconfigurations without waiting for long cache TTLs
Monitoring and debugging access with Unkey
Unkey provides Realtime Analytics so you can:
- See key usage over the last 30 days
- Identify which keys are being rate-limited or exceeding usage
- Track which customers are using which features/endpoints
This is especially useful when you:
- Launch new scopes and need to confirm they’re used as expected
- Debug “I’m getting 403” reports by checking which key and metadata were sent
- Optimize plans by seeing how different tiers use your API
You can access analytics:
- Via the dashboard for an at-a-glance view
- Via the API to build your own monitoring or billing system
Putting it all together: checklist for per-customer access
-
Define your model
- Decide on metadata fields:
customerId,plan,environment, etc. - Define roles and permissions that match your product surface.
- Decide on metadata fields:
-
Implement key creation
- On customer signup/plan change, create or update a key in Unkey.
- Attach metadata and assign the correct role/scopes.
-
Integrate key verification
- Use Unkey’s SDK or REST API to verify keys on every incoming request.
- Handle invalid/expired keys early in the request pipeline.
-
Build authorization logic
- Enforce tenant boundaries using
customerId. - Enforce feature access with roles/scopes and plan metadata.
- Respect environments (sandbox vs production) where relevant.
- Enforce tenant boundaries using
-
Monitor and adjust
- Use Unkey’s realtime analytics to understand usage and spot misconfigurations.
- Refine roles and metadata as your plans and features evolve.
By combining rich key metadata with Unkey’s role-based access control and fast global propagation of permission changes, you can give each customer precisely the access they need—no more, no less—while keeping your implementation straightforward and maintainable.