API Reference
MARDUK Kit ships with a license and entitlement API at https://mardukkit-api.brandon-c64.workers.dev/api/validate. The public website at https://mardukkit-ai.pages.dev sends buyers through Stripe and the thank-you page, then the kit talks to this Worker when a buyer activates a license key or add-on code.
Base URL
https://mardukkit-api.brandon-c64.workers.dev/apiAuthentication
The validate endpoint is public, the license key itself acts as the secret. There is no separate API token. CORS is enabled (Access-Control-Allow-Origin: *).
POST /api/validate
Validates a license key. Returns whether the key is currently active, the tier it grants, and (optionally) an expiration timestamp.
Request
POST /api/validate HTTP/1.1
Host: mardukkit-api.brandon-c64.workers.dev
Content-Type: application/json
{
"key": "JK-1234-5678-ABCD",
"machineId": "a1b2c3d4e5f6...",
"version": "1.0.0"
}Body fields
| Field | Type | Required | Description |
|---|---|---|---|
key | string | yes | The buyer's license key. Format depends on your backend (LemonSqueezy, Polar, Gumroad, KV, or static). |
machineId | string | recommended | SHA-256 hash slice of hostname-username-platform-arch. Used by some backends (LemonSqueezy) to bind a key to a machine. |
version | string | optional | Kit version the buyer is installing. Logged for analytics, not validated. |
Response (200 OK, valid key)
{
"valid": true,
"tier": "starter",
"expiresAt": "2026-12-31T23:59:59Z"
}Response (200 OK, rejected)
{
"valid": false,
"message": "License rejected by LemonSqueezy"
}Note: rejected keys still return HTTP 200 with valid: false. The kit's validate.js handles this. We use 200 (not 401/403) so middleware doesn't accidentally retry or strip the body.
Response fields
| Field | Type | When present |
|---|---|---|
valid | boolean | always |
tier | string | when valid (e.g. starter, pro, enterprise) |
expiresAt | ISO 8601 string | when valid + provider supports expiration |
message | string | when invalid (human-readable reason) |
GET /api/health
Liveness probe. Returns {"ok": true} with HTTP 200. No auth.
curl https://mardukkit-api.brandon-c64.workers.dev/api/health
{"ok": true}Examples
curl
curl -X POST https://mardukkit-api.brandon-c64.workers.dev/api/validate \
-H "Content-Type: application/json" \
-d '{
"key": "JK-1234-5678-ABCD",
"machineId": "a1b2c3d4e5f6",
"version": "1.0.0"
}'JavaScript (Node.js / browser)
const res = await fetch("https://mardukkit-api.brandon-c64.workers.dev/api/validate", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
key: process.env.MARDUK_KIT_KEY,
machineId: hashId(),
version: pkg.version,
}),
});
const data = await res.json();
if (!data.valid) throw new Error(data.message);
console.log("activated tier:", data.tier);Python
import requests, hashlib, os, platform
machine_id = hashlib.sha256(
f"{os.uname().nodename}-{os.getlogin()}-{platform.system()}-{platform.machine()}".encode()
).hexdigest()[:16]
r = requests.post("https://mardukkit-api.brandon-c64.workers.dev/api/validate", json={
"key": "JK-1234-5678-ABCD",
"machineId": machine_id,
"version": "1.0.0",
})
data = r.json()
if not data["valid"]:
raise SystemExit(data["message"])
print("activated tier:", data["tier"])Entitlement endpoints
The shipped kit uses these endpoints after purchase. Starter activates the base kit only. Pro and Enterprise include both paid add-ons. Individual MARDUK- codes unlock the add-on named by the code, while bundle unlocks both.
| Endpoint | Purpose | Buyer-facing caller |
|---|---|---|
POST /api/stripe-success | Thank-you page mints an LK- license or draws a MARDUK- add-on code. | MARDUK website after Stripe checkout |
POST /api/activate-license | Binds an LK- license to one machine and returns tier/add-ons. | Kit dashboard Settings → Add-ons |
POST /api/redeem | Claims a single-use add-on code and binds it to one machine. | Kit dashboard Settings → Add-ons |
GET /api/entitlements?key=...&machine_id=... | Refreshes the entitlement list for a license that is already activated on that machine. | Local addon-gate sidecar |
/api/entitlements intentionally requires machine_id. If a license key has not been activated on the requesting machine, the Worker returns 403 and the kit keeps paid modules locked.
Backends
The validate endpoint can route to one of five backends, configured in the worker/wrangler.toml via the LICENSE_BACKEND env var:
| Backend | What it does | Config |
|---|---|---|
static | Match against comma-separated STATIC_KEYS env var | wrangler secret put STATIC_KEYS |
lemonsqueezy | Proxy to api.lemonsqueezy.com/v1/licenses/validate | No extra config |
polar | Proxy to api.polar.sh/v1/customer-portal/license-keys/validate | POLAR_ORG_ID required |
gumroad | Proxy to api.gumroad.com/v2/licenses/verify | GUMROAD_PRODUCT required |
kv | Cloudflare KV lookup (manual key insertion) | Bind VALID_KEYS_KV namespace |
Grace mode
The kit's validate.js implements grace mode: if the validate endpoint returns 5xx or is unreachable, the install proceeds with a 7-day provisional license. Re-validation runs in the background on subsequent launches.
This means: your buyer's install never fails because of provider downtime. They get full access for up to 7 days while you fix the upstream issue.
Rate limits
Cloudflare Workers free tier: 100,000 requests / day. The kit calls validate once per fresh install, well under any realistic limit. For very-high-volume products, upgrade to Workers Paid ($5/mo for 10M requests).
CORS
All responses include Access-Control-Allow-Origin: * and full method/header allowlists. The endpoint accepts OPTIONS preflight requests automatically.
Errors
| Status | When |
|---|---|
| 200 | Normal, check valid in body |
| 400 | Missing or malformed JSON body |
| 403 | Entitlement refresh for a machine not bound to that license |
| 404 | Wrong path, missing license, or missing redeem code |
| 405 | Wrong method (validate is POST-only) |
| 500 | Backend config issue (e.g. POLAR_ORG_ID unset for polar backend) |