API reference
API reference
Base URL: http://your-server:8080. All responses are JSON. Interactive
Swagger UI lives at /docs on your server.
Authentication
Two credentials, two header names:
| Header | Used for | Set where |
|---|---|---|
X-MinMax-Admin-Token |
management endpoints (CRUD, reset, revoke) | your server only |
X-API-Key |
activation / validation endpoints | your client app |
Wrong or missing credentials return 401.
Errors
Errors are JSON: {"detail": "..."} with a status code.
| Code | Meaning |
|---|---|
| 401 | bad or missing auth |
| 404 | unknown product / license / policy (or key doesn't exist) |
| 403 | license revoked, or expired |
| 429 | no seats available on this license |
| 409 | duplicate name / slug |
| 422 | validation error |
/activate deliberately answers 404 for unknown keys — no key sniffing.
Endpoints
GET /healthz
Liveness probe. {"status": "ok", "service": "minmaxkey"}.
GET /
Service info: links to /docs, /admin, /healthz.
Products
POST /v1/products · admin
Create a product. Body: {"name": "PixelForge Pro", "slug": "pixelforge-pro"}.
Returns the product including api_key and public_key.
GET /v1/products · admin
List all products.
GET /v1/products/{id} · admin
One product.
POST /v1/products/{id}/webhook · admin
Set (or clear, with {"url": ""}) the product's webhook endpoint. Returns
{"url": ..., "secret": ...} — the secret is generated once and stays stable.
Keep it server-side.
Policies
POST /v1/products/{id}/policies · admin
Body: {"name": "pro-monthly", "kind": "subscription", "max_seats": 1, "duration_days": 30}.
kind is one of lifetime | subscription | floating | trial. max_seats
defaults to 0 (unlimited). duration_days applies to subscription/trial.
GET /v1/products/{id}/policies · admin
Licenses
POST /v1/products/{id}/licenses · admin
Body: {"policy_id": 1, "customer_email": "[email protected]", "metadata": {...}}.
policy_id must belong to that product. Returns the license including the
human-friendly key.
GET /v1/products/{id}/licenses · admin
All licenses of a product, each with its current activations.
POST /v1/licenses/{id}/revoke · admin
Revokes the license. All future activations and validations fail; existing
offline tokens keep working until the client revalidates (see
Offline tokens for the trade-off).
POST /v1/licenses/{id}/reset · admin
Frees every seat of the license in one go — the customer reinstalled their OS
and their fingerprint changed. The key stays active and can bind a new machine
immediately. Returns {"status": "reset", "freed": N} and fires
license.reset.
POST /v1/licenses/{id}/offline · admin
Returns {"token": "..."} — a signed offline token for a license not bound to
a specific device.
Activation (client-facing, X-API-Key)
POST /v1/products/{id}/activate
Body: {"key": "...", "fingerprint": "<sha256 hex, ≥8 chars>", "name": "My MacBook", "platform": "windows"}.
Returns license, device, seat usage, and offline_token.
POST /v1/products/{id}/deactivate
Body: {"key": "...", "fingerprint": "..."}. Idempotent:
{"freed": true} or {"freed": false}.
GET /v1/products/{id}/validate?key=...&fingerprint=...
Server-side status check for webhooks and payment processors. When a
fingerprint is given and the license is valid, also returns a fresh
offline_token — this is how clients auto-revalidate.
Response shape:
{
"valid": true,
"status": "active",
"license": {"id": 1, "key": "9TMC-...", "status": "active", "expires_at": null, "...": "..."},
"device": {"fingerprint": "9f86d081...", "platform": "windows", "...": "..."},
"seats": {"used": 1, "max": 2},
"offline_token": "eyJ..."
}
When invalid: {"valid": false, "status": "invalid" | "revoked" | "expired"}.