Skip to content
Talk to an Engineer Dashboard

API keys

Issue long-lived, revocable API keys scoped to organizations and users for programmatic access to your APIs

When your customers need to integrate with your APIs — whether for CI/CD pipelines, partner integrations, or internal tooling — they need a straightforward way to authenticate. Scalekit API keys gives you long-lived, revocable bearer credentials that can be issued to your customers for Organizational or User Level access to your APIs.

Unlike JWT-based M2M authentication, API keys do not carry embedded claims and cannot be validated on the client side. The API Keys can be validated via APIs with Scalekit and after server-side validation, the claims are sent when a valid API Key is presented. Revocation of API keys takes effect immediately with no expiry windows or propagation delays. Each key is scoped to an organization and optionally to a specific user, making it easy to build personal access tokens, per-user rate limiting, and audit trails.

In this guide, you’ll learn how to create, validate, list, and revoke API keys using the Scalekit SDK.

API ClientUserYour AppScalekit Request API key Create token (organizationId, userId) API key + tokenId API key Configure API key Request with Authorization header Validate token Organization, user, and role info Process request Response

npm install @scalekit-sdk/node

Initialize the Scalekit client with your environment credentials:

Express.js
2 collapsed lines
import { ScalekitClient } from '@scalekit-sdk/node';
const scalekit = new ScalekitClient(
process.env.SCALEKIT_ENVIRONMENT_URL,
process.env.SCALEKIT_CLIENT_ID,
process.env.SCALEKIT_CLIENT_SECRET
);

To get started, create an API key scoped to an organization. You can optionally scope it to a specific user and attach custom metadata.

Create an API key scoped to an organization. This is the most common pattern for service-to-service integrations where the API key represents access on behalf of an entire organization.

try {
const response = await scalekit.token.createToken(organizationId, {
description: 'CI/CD pipeline token',
});
// Store securely — this value cannot be retrieved again after creation
const opaqueToken = response.token;
// Stable identifier for management operations (format: apit_xxxxx)
const tokenId = response.tokenId;
} catch (error) {
console.error('Failed to create token:', error.message);
}

Scope an API key to a specific user within an organization to enable personal access tokens, per-user audit trails, and user-level rate limiting. You can also attach custom claims as key-value metadata.

try {
const userToken = await scalekit.token.createToken(organizationId, {
userId: 'usr_12345',
customClaims: {
team: 'engineering',
environment: 'production',
},
description: 'Deployment service token',
});
const opaqueToken = userToken.token;
const tokenId = userToken.tokenId;
} catch (error) {
console.error('Failed to create token:', error.message);
}

The response contains three fields:

FieldDescription
tokenThe API key string. Returned only at creation.
token_idAn identifier (format: apit_xxxxx) for referencing the token in management operations.
token_infoMetadata including organization, user, custom claims, and timestamps.

When your API server receives a request with an API key, you’ll want to verify it’s legitimate before processing the request. Pass the key to Scalekit — it validates the key server-side and returns the associated organization, user, and metadata context.

import { ScalekitValidateTokenFailureException } from '@scalekit-sdk/node';
try {
const result = await scalekit.token.validateToken(opaqueToken);
const orgId = result.tokenInfo.organizationId;
const userId = result.tokenInfo.userId;
const claims = result.tokenInfo.customClaims;
} catch (error) {
if (error instanceof ScalekitValidateTokenFailureException) {
// Token is invalid, expired, or revoked
console.error('Token validation failed:', error.message);
}
}

If the API key is invalid, expired, or has been revoked, validation fails with a specific error that you can catch and handle in your code. This makes it easy to reject unauthorized requests in your API middleware.

Beyond the basic organization and user information, the validation response also includes any roles assigned to the user and external identifiers you’ve configured for the organization. These are useful for making authorization decisions without additional database lookups.

try {
const result = await scalekit.token.validateToken(opaqueToken);
// Roles assigned to the user
const roles = result.tokenInfo.roles;
// External identifiers for mapping to your system
const externalOrgId = result.tokenInfo.organizationExternalId;
const externalUserId = result.tokenInfo.userExternalId;
} catch (error) {
if (error instanceof ScalekitValidateTokenFailureException) {
console.error('Token validation failed:', error.message);
}
}

If you attached custom claims when creating the API key, they come back in every validation response. This is a convenient way to make fine-grained authorization decisions — like restricting access by team or environment — without hitting your database.

try {
const result = await scalekit.token.validateToken(opaqueToken);
const team = result.tokenInfo.customClaims?.team;
const environment = result.tokenInfo.customClaims?.environment;
// Use metadata for authorization
if (environment !== 'production') {
return res.status(403).json({ error: 'Production access required' });
}
} catch (error) {
if (error instanceof ScalekitValidateTokenFailureException) {
console.error('Token validation failed:', error.message);
}
}

You can retrieve all active API keys for an organization at any time. The response supports pagination for large result sets, and you can filter by user to find keys scoped to a specific person.

// List tokens for an organization
const response = await scalekit.token.listTokens(organizationId, {
pageSize: 10,
});
for (const token of response.tokens) {
console.log(token.tokenId, token.description);
}
// Paginate through results
if (response.nextPageToken) {
const nextPage = await scalekit.token.listTokens(organizationId, {
pageSize: 10,
pageToken: response.nextPageToken,
});
}
// Filter tokens by user
const userTokens = await scalekit.token.listTokens(organizationId, {
userId: 'usr_12345',
});

The response includes totalCount for the total number of matching tokens and nextPageToken / prevPageToken cursors for navigating pages.

When you need to revoke an API key — for example, when an employee leaves or you suspect credentials have been compromised — you can invalidate it through Scalekit. Revocation takes effect instantly: the very next validation request for that key will fail.

This operation is idempotent, so calling invalidate on an already-revoked key succeeds without error.

// Invalidate by API key string
await scalekit.token.invalidateToken(opaqueToken);
// Or invalidate by token_id (useful when you store tokenId for lifecycle management)
await scalekit.token.invalidateToken(tokenId);

Now let’s put it all together. The most common pattern is to add API key validation as middleware in your API server. Extract the Bearer token from the Authorization header, validate it through Scalekit, and use the returned context for authorization decisions.

Express.js
import { ScalekitValidateTokenFailureException } from '@scalekit-sdk/node';
async function authenticateToken(req, res, next) {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) {
// Reject requests without credentials to prevent unauthorized access
return res.status(401).json({ error: 'Missing authorization token' });
}
try {
// Server-side validation — Scalekit checks token status in real time
const result = await scalekit.token.validateToken(token);
// Attach token context to the request for downstream handlers
req.tokenInfo = result.tokenInfo;
next();
} catch (error) {
if (error instanceof ScalekitValidateTokenFailureException) {
// Revoked, expired, or malformed tokens are rejected immediately
return res.status(401).json({ error: 'Invalid or expired token' });
}
throw error;
}
}
// Apply to protected routes
app.get('/api/resources', authenticateToken, (req, res) => {
const orgId = req.tokenInfo.organizationId;
// Serve resources scoped to this organization
});

Here are a few tips to help you get the most out of API keys in production:

  • Store API keys securely: Treat API keys like passwords. Store them in encrypted secrets managers or environment variables. Never log keys, commit them to version control, or expose them in client-side code.
  • Set expiry for time-limited access: Use the expiry parameter for keys that should automatically become invalid after a set period. This limits the blast radius if a key is compromised.
  • Use custom claims for context: Attach metadata like team, environment, or service as custom claims. Your API middleware can use these claims for fine-grained authorization without additional database lookups.
  • Rotate keys safely: To rotate an API key, create a new key, update the consuming service to use the new key, verify the new key works, then invalidate the old key. This avoids downtime during rotation.

You now have everything you need to issue, validate, and manage API keys in your application.