API Reference
The raw HTTP contract. Use this if you're integrating from a language without a ready-made SDK, or want to see exactly what the SDKs do under the hood.
Base URL: loading…
Conventions
- All requests and responses are JSON,
Content-Type: application/json. - The public license endpoints (
/api/v1/*) always return HTTP 200 for any request the server understood — the outcome (including failures like an expired key) is in the JSON body. Non-2xx is reserved for transport-level problems:400validation error,429rate limited,500unexpected error. - The admin API (
/api/admin/*) uses conventional REST status codes and requires an authenticated session cookie (see below).
GET /api/v1/public-key
No auth, no rate limit beyond the general limiter. Returns the Ed25519 public key used to sign every activate/verify response.
{ "publicKey": "base64-spki-der...", "algorithm": "ed25519" }
POST /api/v1/activate
Binds a key to a device on first use. Idempotent if called again with the same hwid. Rate limited: 10 requests / 10 min per IP, and 20 / hour per key.
Request
{
"product": "your-product-slug",
"key": "7K2M9-QX3R8-2VBN5-8JH4C-T",
"hwid": "sha256-hex-of-a-machine-id" // see Security Model page
}
POST /api/v1/verify
Checks whether a previously-activated key is still valid on this device. Does not activate — a key that has never been activated returns reason: "not_activated" (SDKs handle this by calling /activate automatically). Rate limited: 30 requests / min per IP, and 120 / hour per key.
Request
{
"product": "your-product-slug",
"key": "7K2M9-QX3R8-2VBN5-8JH4C-T",
"hwid": "sha256-hex-of-a-machine-id"
}
Response (both endpoints)
A signed envelope. See the Security Model page for exactly how to verify signature against payload.
{
"payload": {
"valid": true,
"reason": null,
"product_slug": "your-product-slug",
"key_id": 42,
"hwid_bound": true,
"status": "active",
"expires_at": "2027-01-01T00:00:00.000Z",
"issued_at": "2026-09-04T12:00:00.000Z",
"offline_grace_until": "2026-09-07T12:00:00.000Z"
},
"signature": "base64..."
}
reason values
| Value | Meaning |
|---|---|
null | Success — valid is true. |
not_found | No such key for that product (or the key belongs to a different product). |
malformed_key | The key string fails its checksum — almost always a typo. Never hits the database. |
revoked | An admin revoked this key. |
expired | Past its computed or fixed expiry date. |
hwid_mismatch | This key is already bound to a different device. |
not_activated | /verify only — this key has never been activated on any device yet. |
Admin API
Powers the dashboard at /admin/. You can also script it — e.g. to auto-generate a key from your own checkout/fulfillment webhook. It's cookie-session authenticated, so from a script: log in once, keep the cookie, reuse it.
# log in and save the session cookie
curl -c cookies.txt -X POST .../api/admin/auth/login \
-H 'content-type: application/json' \
-d '{"username":"admin","password":"..."}'
# generate 1 key for product id 1, expiring 30 days after activation
curl -b cookies.txt -X POST .../api/admin/keys \
-H 'content-type: application/json' \
-d '{"productId":1,"count":1,"durationDays":30,"customerEmail":"buyer@example.com"}'
Full admin routes: /api/admin/auth/*, /api/admin/products, /api/admin/keys, /api/admin/stats — the request/response shapes mirror what the dashboard sends; open your browser's network tab on /admin/ for exact examples.