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/api

Authentication

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

FieldTypeRequiredDescription
keystringyesThe buyer's license key. Format depends on your backend (LemonSqueezy, Polar, Gumroad, KV, or static).
machineIdstringrecommendedSHA-256 hash slice of hostname-username-platform-arch. Used by some backends (LemonSqueezy) to bind a key to a machine.
versionstringoptionalKit 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

FieldTypeWhen present
validbooleanalways
tierstringwhen valid (e.g. starter, pro, enterprise)
expiresAtISO 8601 stringwhen valid + provider supports expiration
messagestringwhen 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.

EndpointPurposeBuyer-facing caller
POST /api/stripe-successThank-you page mints an LK- license or draws a MARDUK- add-on code.MARDUK website after Stripe checkout
POST /api/activate-licenseBinds an LK- license to one machine and returns tier/add-ons.Kit dashboard Settings → Add-ons
POST /api/redeemClaims 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:

BackendWhat it doesConfig
staticMatch against comma-separated STATIC_KEYS env varwrangler secret put STATIC_KEYS
lemonsqueezyProxy to api.lemonsqueezy.com/v1/licenses/validateNo extra config
polarProxy to api.polar.sh/v1/customer-portal/license-keys/validatePOLAR_ORG_ID required
gumroadProxy to api.gumroad.com/v2/licenses/verifyGUMROAD_PRODUCT required
kvCloudflare 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

StatusWhen
200Normal, check valid in body
400Missing or malformed JSON body
403Entitlement refresh for a machine not bound to that license
404Wrong path, missing license, or missing redeem code
405Wrong method (validate is POST-only)
500Backend config issue (e.g. POLAR_ORG_ID unset for polar backend)