# License Key API Guide The License Key API lets software applications validate and manage license keys issued through Makenot.work. All endpoints are public (no authentication required) and rate-limited. Endpoints are available at both `/api/keys/` and `/api/v1/keys/` paths. New integrations should use the `/api/v1/` prefix. ## Validate and Activate a Key Validates a license key and optionally activates it on a machine. ``` POST https://makenot.work/api/v1/keys/validate Content-Type: application/json { "key": "XXXX-XXXX-XXXX-XXXX", "machine_id": "unique-machine-identifier", "label": "Alice's MacBook Pro" } ``` ### Parameters | Field | Required | Description | |-------|----------|-------------| | `key` | Yes | The license key string | | `machine_id` | Yes | A stable, unique identifier for this machine | | `label` | No | Human-readable name for this activation | ### Response ```json { "valid": true, "activated": true, "license": { "item_id": "550e8400-...", "max_activations": 3, "activation_count": 1, "created_at": "2026-03-13T10:00:00Z" } } ``` ### Error Cases ```json { "valid": false, "error": "invalid_key" } ``` | Error | Meaning | |-------|---------| | `invalid_key` | Key does not exist | | `key_revoked` | Key has been revoked by the creator | | `activation_limit_reached` | All activation slots are in use | ### Idempotent Activation If the same `key` + `machine_id` combination is submitted again, the existing activation is refreshed (timestamp updated) without consuming an additional slot. ## Check Key Status Check whether a key is valid without activating it. ``` GET https://makenot.work/api/v1/keys/{key_code}/status ``` Response: ```json { "valid": true, "license": { "item_id": "550e8400-...", "max_activations": 3, "activation_count": 2, "remaining_activations": 1, "created_at": "2026-03-13T10:00:00Z" } } ``` Use this for periodic license checks without affecting activation state. ## Deactivate a Key Release an activation slot (e.g., when the user uninstalls your software). ``` POST https://makenot.work/api/v1/keys/deactivate Content-Type: application/json { "key": "XXXX-XXXX-XXXX-XXXX", "machine_id": "unique-machine-identifier" } ``` Response: ```json { "success": true, "message": "Activation removed" } ``` ## License Verification (Offline Grace Period) For apps that need to work offline, use the verification endpoint. It validates and activates the key, then returns a signed JWT token valid for 7 days. Your app can verify this token locally without contacting the server. This endpoint requires the project to have license verification enabled (configured by the creator in project settings). ``` POST https://makenot.work/api/v1/license/verify Content-Type: application/json { "key": "XXXX-XXXX-XXXX-XXXX", "machine_fingerprint": "unique-machine-identifier" } ``` ### Response ```json { "valid": true, "token": "eyJhbGciOiJIUzI1NiIs...", "expires_in": 604800 } ``` | Field | Description | |-------|-------------| | `valid` | Whether the key is valid and activated | | `token` | Signed JWT for offline verification (7-day validity) | | `expires_in` | Token lifetime in seconds (604800 = 7 days) | ### JWT Claims The token contains: | Claim | Description | |-------|-------------| | `sub` | License key ID | | `machine` | Machine fingerprint | | `item` | Item ID | | `iat` | Issued at (Unix timestamp) | | `exp` | Expires at (Unix timestamp) | ### Offline Flow 1. Call `/api/v1/license/verify` on app launch (when online) 2. Cache the returned JWT locally 3. On subsequent launches, verify the JWT signature and `exp` claim locally 4. Re-verify with the server when the token nears expiry or when connectivity returns ### Deactivation via Verify API ``` POST https://makenot.work/api/v1/license/deactivate Content-Type: application/json { "key": "XXXX-XXXX-XXXX-XXXX", "machine_fingerprint": "unique-machine-identifier" } ``` | Error | Meaning | |-------|---------| | `invalid_key` | Key does not exist | | `key_revoked` | Key has been revoked by the creator | | `activation_limit_reached` | All activation slots are in use | | `verification_not_enabled` | Project does not have license verification enabled | ## License Text Retrieve the license agreement for an item in plain text: ``` GET https://makenot.work/api/v1/items/{item_id}/license.txt ``` Returns the license text configured by the creator (MIT, Apache 2.0, custom, etc.) as `text/plain`. ## Revocation Creators can revoke keys from their dashboard. A revoked key returns `key_revoked` on all endpoints and cannot be reactivated. ## PWYW Compatibility License keys work with Pay What You Want (PWYW) items. Any customer who completes a purchase receives a key if the creator has keys enabled. ## Machine ID Guidelines The `machine_id` (or `machine_fingerprint` for the verify API) should be: - **Stable**: Same value across app restarts and updates - **Unique**: Different on each machine - **Not personally identifiable**: Avoid using MAC addresses or usernames Good approaches: - Generate a UUID on first launch and store it in the app's data directory - Use a hardware-derived hash (disk serial, motherboard ID) - Use the OS machine ID (e.g., `/etc/machine-id` on Linux) ## Rate Limits License key endpoints are rate-limited per IP. See [API Overview](./api-overview.md) for current limits. ## Integration Pattern Recommended flow for desktop applications: 1. **On first launch**: Prompt for license key, call `/api/v1/keys/validate` with a generated machine ID 2. **On subsequent launches**: Call `/api/v1/license/verify` for offline-capable apps, or `/api/v1/keys/{key_code}/status` for online-only apps 3. **On uninstall**: Call `/api/v1/keys/deactivate` to release the activation slot 4. **On activation failure**: Show the error message and allow the user to enter a different key ## Error Response Format ```json { "error": "descriptive message" } ``` | Status | Meaning | |--------|---------| | 400 | Invalid request body | | 404 | Key not found (for status endpoint) | | 429 | Rate limit exceeded | ## See Also - [Pricing & Monetization](../guide/03-selling.md): License key setup for creators - [API Overview](./api-overview.md): Authentication and rate limits