Overview
TwelveTake Vault is a license key management system for software products. It supports multi-tenant deployment with customizable key formats, machine activation tracking, abuse detection, and a built-in software update system with signed releases.
Base URLs
| Environment | URL |
|---|---|
| Production | https://vault.twelvetake.com |
| Local development | http://localhost:3002 |
Interactive API docs are also available at /api/docs in every installation via Swagger UI.
API Versioning
All public endpoints use the /api/v1/ prefix.
Authentication
The API uses three authentication methods depending on the endpoint type.
API Key (Public Endpoints)
For license validation, management, and product listing. Pass the key in the X-API-Key header.
X-API-Key: your-api-key-here Create keys in the admin dashboard under Settings > API Keys. Keys are scoped:
| Scope | Access |
|---|---|
VALIDATE_ONLY | Activate, validate, deactivate, heartbeat, check |
CREATE_LICENSE | Everything above + create, revoke, renew licenses |
READ_ONLY | Everything above + list products, lookup by order |
License Key (Update Endpoints)
Software update endpoints authenticate with the license key itself via the X-License-Key header. The license must be ACTIVE and belong to the target product.
X-License-Key: TT-ABCD-EFGH-IJKL-MNOP Errors & Response Format
All endpoints return JSON. Successful responses include "success": true. Errors include a structured error object:
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid license key"
}
} Standard HTTP status codes are used: 200 success, 201 created, 207 partial success (bulk), 400 validation error, 401 unauthorized, 403 forbidden, 404 not found, 429 rate limited, 503 service degraded.
Pagination
List endpoints accept page (default 1) and limit (default 50, max 500) query parameters. Paginated responses include:
{
"pagination": {
"page": 1,
"limit": 50,
"total": 142,
"totalPages": 3
}
} Schemas
Enums
| Name | Values |
|---|---|
LicenseStatus | ACTIVE, EXPIRED, SUSPENDED, REVOKED |
LicenseType | PERPETUAL, SUBSCRIPTION, TRIAL, BETA |
LicenseTier | PERSONAL, PROFESSIONAL, BUSINESS, ENTERPRISE |
ProductType | DESKTOP, WEB, MOBILE, PLUGIN, API |
ProductStatus | ACTIVE, DEPRECATED, DISCONTINUED |
ApiKeyScope | VALIDATE_ONLY, CREATE_LICENSE, READ_ONLY |
LicensePublic
Returned by public validation endpoints.
| Field | Type | Description |
|---|---|---|
key | string | License key (e.g., TT-ABCD-EFGH-IJKL-MNOP) |
product | string | Product name |
tier | LicenseTier | License tier |
type | LicenseType | License type |
validUntil | date-time | null | Expiration (null for perpetual) |
activationsUsed | integer | Current activation count |
maxActivations | integer | Maximum allowed activations |
LicenseCreated
Returned by create, revoke, and renew endpoints.
| Field | Type | Description |
|---|---|---|
id | string | License ID |
key | string | License key |
product | string | Product ID |
type | LicenseType | License type |
tier | LicenseTier | License tier |
status | LicenseStatus | Current status |
maxActivations | integer | Maximum activations |
validUntil | date-time | null | Expiration |
createdAt | date-time | Creation timestamp |
Activation
| Field | Type |
|---|---|
machineId | string |
machineName | string |
activatedAt | date-time |
ReleaseInfo
| Field | Type | Description |
|---|---|---|
version | string | Semver version |
channel | string | stable, beta, alpha, rc, nightly |
changelog | string | Release notes |
bundleFilename | string | Download filename |
bundleSize | integer | File size in bytes |
bundleSha256 | string | SHA-256 hash |
signature | string | Ed25519 signature |
isCritical | boolean | Critical update flag |
requiresMigration | boolean | Migration needed |
publishedAt | date-time | null | Publish timestamp |
Health
GET /api/health
Returns service status and database connectivity. No authentication required.
Response 200:
{ "status": "ok", "version": "1.0.0", "timestamp": "2026-03-13T..." } Response 503: Service degraded (database unreachable).
License Validation
Public API for activating, validating, and deactivating license keys. All endpoints require X-API-Key with at least VALIDATE_ONLY scope.
POST /api/v1/license/activate
Activate a license on a machine. Idempotent — if the machine is already activated, returns the existing activation.
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
licenseKey | string (10-100) | Yes | The license key |
machineId | string (8-255) | Yes | Unique hardware fingerprint |
machineName | string (max 255) | No | Human-readable name |
osName | string (max 100) | No | Operating system |
osVersion | string (max 100) | No | OS version |
appVersion | string (max 50) | No | Application version |
Response 200: { success, message, license: LicensePublic, activation: Activation }
Response 400: Max activations reached, expired, suspended, or revoked.
POST /api/v1/license/validate
Check if a license is valid for a specific machine. Read-only — does not create or modify activations. Always returns 200; check the valid field.
Request body: licenseKey (required), machineId (required).
Response 200: { success, valid: boolean, status: LicenseStatus, message, license: LicensePublic, activation: Activation }
POST /api/v1/license/deactivate
Remove a license activation from a machine, freeing the activation slot.
Request body: licenseKey (required), machineId (required).
Response 200: { success, message }
POST /api/v1/license/heartbeat
Periodic check-in from an activated machine. Updates lastSeenAt and returns the next expected heartbeat time. Machines that stop sending heartbeats may be auto-deactivated based on the applicable policy.
Request body: key (required), machineId (required).
Response 200: { success, nextHeartbeatAt: date-time | null }
GET /api/v1/license/check/{key}
Quick status check. Returns only the license status. Does not require a machine ID.
Response 200: { success, status: LicenseStatus }
Response 404: License not found.
License Management
Public API for creating and managing licenses programmatically. Designed for server-to-server integrations (e.g., e-commerce checkout webhooks). Requires X-API-Key with CREATE_LICENSE scope or higher.
POST /api/v1/license/create
Create a new license. Supports idempotency via X-Idempotency-Key header — same key returns the existing license instead of creating a duplicate.
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
productId | string (max 30) | Yes | Product ID |
customerEmail | email (max 255) | Yes | Customer email |
customerName | string (max 255) | No | Customer name |
type | LicenseType | No | Defaults to product default |
tier | LicenseTier | No | License tier |
maxActivations | integer (1-1000) | No | Activation limit |
validUntil | date-time | No | Expiration date |
orderId | string (max 255) | No | External order reference |
metadata | object | No | Custom key-value pairs (max 20 keys) |
Response 201: { success, license: LicenseCreated }
Response 200: Idempotent return — { success, license: LicenseCreated, idempotent: true }
POST /api/v1/license/create-bulk
Create up to 20 licenses in one request. Returns 207 on partial failure. Idempotency key is appended with :index per item.
Request body: { licenses: [{ productId, customerEmail, ... }] } (array of 1-20 items, same fields as create).
Response 201: All created. Response 207: Partial failure (each result includes success/error).
GET /api/v1/license/by-order/{orderId}
Look up all licenses associated with an external order ID. Requires READ_ONLY scope or higher.
Response 200: { success, licenses: LicenseCreated[] }
POST /api/v1/license/revoke
Revoke a license permanently. Provide either licenseId or licenseKey.
Request body: licenseId or licenseKey (one required), reason (optional).
Response 200: { success, license: LicenseCreated }
POST /api/v1/license/renew
Extend a license expiration date. Cannot renew revoked licenses.
Request body: licenseId or licenseKey, validUntil (required, date-time).
Response 200: { success, license: LicenseCreated }
Products
GET /api/v1/products
List all active products. Requires CREATE_LICENSE, READ_ONLY, or FULL_ADMIN scope.
Response 200: { success, products: ProductPublic[] }
Software Updates
Built-in update system for distributing software releases with Ed25519 signed bundles. Clients authenticate with their license key via X-License-Key header.
GET /api/v1/updates/check
Check if an update is available for the licensed product.
Query parameters: current_version (required, semver), channel (optional: stable, beta, alpha, rc, nightly; default stable).
Response 200:
{
"success": true,
"update_available": true,
"version": "1.3.0",
"changelog": "...",
"bundle_size": 52428800,
"sha256": "...",
"signature": "...",
"is_critical": false,
"requires_migration": false,
"published_at": "2026-03-13T...",
"release_id": "..."
} Rate limit: 60 requests per 15 minutes.
GET /api/v1/updates/download
Download a release bundle. Returns binary data (application/octet-stream).
Query parameters: release_id (required), from_version (optional).
Response headers: X-Bundle-SHA256, X-Bundle-Signature.
Rate limit: 10 requests per hour.
GET /api/v1/updates/manifest
Get release metadata without downloading the bundle.
Query parameters: release_id (required).
Response 200: Version, filename, size, sha256, signature, channel, criticality, migration flag, publish date.
GET /api/v1/updates/public-key
Get the Ed25519 public key for verifying release signatures. No license key required.
Query parameters: product (required, product slug), tenant (optional).
Response 200: { public_key: "base64-encoded Ed25519 key" }