# A versioned API with scoped keys instead of shared secrets

> A SaaS starter API with versioned routes, hashed keys, read/write/admin scopes, expiry, revocation, usage logs, discovery, validation, and rate limits.

Source: https://saascode.ai/inside/api-versioned-rest-api-keys · Published: 2026-08-21 · Section: fabric

---
A SaaS starter with a versioned REST API needs more than an `/api` folder. External callers need stable routes, credentials that can be revoked, scopes that mean something, usage records, and a path for product-specific operations. The base includes that infrastructure under `/api/v1`.

API keys belong to organizations. A key has a name, a stored hash, a display prefix, a scope, an optional expiry, an active state, last-used data, and a request count. The scopes form a simple hierarchy: read, write, and admin.

The full key is generated from cryptographically random bytes and returned once. Storage receives a SHA-256 hash and a short display prefix, not the bearer secret. Validation hashes the presented key, looks it up, checks its active and expiry states, and then enforces the required scope.

Keys also carry a live or test prefix derived from the product configuration. The validator accepts the current product prefix and a legacy prefix for compatibility. That makes environments visible to a human without weakening the hash-based lookup.

That is the right default for a buyer. A leaked database row does not reveal the key that clients send. A read-only integration does not need an admin credential. A key can be revoked without changing the credentials used by every other integration.

## What the versioned surface contains

The baseline includes `/api/v1/me`, which returns the calling organization and key context. It also includes a Connect surface for higher-level business operations. A manifest endpoint lists registered operations, their scope, schemas, steps, and dispatched events. A dynamic route executes an operation by slug.

Four Connect operations ship in the base: subscribe an organization, cancel a subscription, invite a user, and promote a user's role. Product-specific operations can register beside them without adding another dispatcher. The same registry can also expose those workflows through the product's MCP server.

Each Connect call follows one path: validate the bearer key, check the organization feature, apply the per-key limiter, resolve the operation, compare scopes, parse JSON, validate the operation's schema, and execute. The response uses a consistent envelope for success and error details.

There is also a versioned device endpoint for mobile push registration. That route uses a signed-in user session rather than an API key, which is an important distinction. “Versioned” describes the public path contract; each route still declares the authentication model appropriate to its caller.

## Keys are operable from the admin

The admin key route lists keys for the current organization with pagination, filters, and sortable usage fields. It can create a key with one of the three scopes and an optional expiry. It can revoke an existing key by making it inactive.

The database stores a log row for API-key requests. Validation updates last-used and request-count data and records the endpoint, method, and source address. The request counter uses an atomic database function so concurrent calls do not overwrite one another.

Database permissions add another guard. User-session writes to API key rows were revoked, and the remaining tenant insert/update policy is capped at read scope. Privileged key creation stays behind the server route. That closes the path where an ordinary organization member could create an admin-scoped key directly through the database API.

## Rate limiting has two layers

Every `/api/*` request passes through a coarse per-IP limiter in the application proxy. Authentication and setup paths use a stricter tier; other API paths use a broader tier. With Redis configured, the limiter is shared. On a single production process without Redis, it falls back to an in-memory counter.

Key-authenticated v1 and MCP routes add a per-key sliding window of 100 requests per minute when Redis credentials are present. A rejected call returns 429 plus remaining, reset, and retry headers.

This layered design separates broad abuse control from credential-specific quotas. It also has a real deployment caveat, described below.

## The honest limit

The base is not an automatically generated CRUD API for every table a product will ever add. It ships identity for the calling key, four business workflows, device registration, and the registry where product operations belong. A new product entity still needs a deliberate route or Connect operation with its own schema and authorization.

The per-key limiter is conditional on Redis. Without those credentials, that specific function returns without limiting. The coarse per-IP layer still has an in-memory production fallback, but that fallback is process-local. On a multi-instance or serverless deployment, each instance keeps its own counters. Errors fail open so a limiter outage does not take the product down. Buyers who need consistent distributed quotas should configure Redis.

The manifest is discovery, not a complete generated OpenAPI contract. Its current schema descriptor exposes a minimal Zod type shape. SDK generation that needs full field-level JSON Schema will need a richer converter or a hand-authored contract.

## Why the API starts in every product

API access tends to arrive as a customer request after the web product works. If there is no credential model at that point, the fastest patch is often one shared secret with too much power and no audit trail.

Starting with organization-owned, scoped keys changes that. The owner can issue one credential per integration, see usage, expire it, and revoke it. Product workflows get one versioned dispatcher and one validation path. The same operation can later serve an SDK or an agent without bypassing the product's business guard.

The base does not claim to know every endpoint a product needs. It supplies the part that is expensive to retrofit safely: identity, scope, lifecycle, logging, rate control, and a stable extension point.
