Building a License Key System for SaaS
License key validation seems simple until you need activation tracking, entitlement enforcement, usage metering, and multi-tenancy. Here's how we designed ValidonX's licensing engine.
The Core Loop
Every licensing system boils down to a simple loop: issue → validate → activate → enforce. The complexity lies in making each step reliable, fast, and observable at scale.
1. Issue: License Key Generation
License keys follow the format VALIDONX-XXXX-XXXX-XXXX-XXXX. They're stored in the tenant's isolated database and linked to a product. Keys can be perpetual or time-limited with configurable expiration.
2. Validate: Single API Call
POST /v1/integration/licenses/{key}/validate
X-API-Key: VX-your-api-key
→ { "valid": true, "license": { "status": "active", "product": "my-app" } }Validation checks key existence, status (active/revoked/expired), expiration date, and product association. The response includes all metadata your application needs to make an allow/deny decision.
3. Activate: Device Binding
Activations bind a license to a specific device or environment using a fingerprint. The activation endpoint is idempotent — calling it twice with the same key + fingerprint returns the existing activation rather than creating a duplicate.
Each license has a configurable maximum number of activations (default: 10). When the limit is reached, new activations are rejected with ACTIVATION.LIMIT_EXCEEDED.
4. Enforce: Entitlements & Usage
Beyond simple valid/invalid checks, ValidonX supports feature entitlements ("does this license include the premium module?") and usage metering ("how many API calls has this tenant made this month?"). Entitlements are seeded from the subscription plan and can be overridden per-tenant.
API Key Security
API keys are never stored in plaintext. We hash them with HMAC-SHA256 using the application key as the secret. This provides fast, constant-time lookups while ensuring that even if the database is compromised, raw API keys cannot be recovered.
We support three key types: secret (full access, server-side only),public (read-only, safe for client-side), and ephemeral(auto-expiring, for temporary access).
Lessons Learned
- Idempotency is essential — network retries are inevitable, so every write endpoint must be safe to call twice
- Audit everything — every license validation, activation, and entitlement check is logged with a request ID for tracing
- Rate limit early — without per-tenant rate limits, one customer's integration bug can saturate the platform
- Plan for scale — database-per-tenant gives us clean scaling boundaries from day one