← Back to blog

July 10, 2026 · 8 min read

Licensing API Recipes: Copy-Paste Code for 4 Common Jobs

You do not need a licensing framework to add licensing. You need four requests. Below are copy-paste recipes for the jobs almost every app needs — validate a key before unlocking, gate a paid feature on an entitlement, activate a device to enforce seat limits, and release a seat on uninstall. Each is a single HTTPS call, shown as a plain HTTP request so it is language-agnostic: send the method, path, X-API-Key header, and JSON body from whatever your app is written in. Run every call server-side with your secret key — the client is untrusted.

Recipe 1 — Validate a license key

The gate. Ask the API whether a key is good right now, and unlock only on a clear yes.

POST /api/v1/integration/licenses/{licenseKey}/validate
X-API-Key: <your-api-key>          # server-side only, never in shipped code

-> 200 OK
{
  "data": {
    "valid": true,
    "license": { "status": "active", "entitlements": { "plan": "pro" } }
  }
}

Read data.valid and unlock only when it is true. Gotcha: valid: false is not an error — an expired license returns 200 with valid: false, while a revoked one returns403. Fail closed on anything that is not valid: true, but tell a bad key apart from an unreachable API: on a network failure, fall back to your last good result or a short grace window rather than locking out a paying customer.

Recipe 2 — Gate a paid feature on an entitlement

Once the key is valid, the same response carries what the customer is allowed to do. Entitlements are a flexible key-value object — gate on the flag your feature needs, not on a hard-coded plan name or customer id.

// from the validate response above
entitlements = data.license.entitlements   // e.g. { "plan": "pro", "sso": true, "export": true }

if (!entitlements.sso)    hideSsoSettings()
if (!entitlements.export) disableCsvExport()

Because the gate reads entitlements as data, you can change what a plan includes — or grandfather one customer — without shipping a new build. That split between "is the key valid?" and "what does it allow?" is the difference between a key and an entitlement.

Recipe 3 — Activate a device (enforce seat limits)

If a plan includes device or seat limits, record an activation when a customer installs. The service counts activations against the license and refuses once the cap is reached.

POST /api/v1/integration/activations
X-API-Key: <your-api-key>

{
  "license_key": "XXXX-XXXX-XXXX-XXXX",
  "instance_id": "b1c9e7a2-...-persisted-per-install-uuid",
  "device_fingerprint": "sha256-hash-of-device-info"   // optional
}

-> 201 Created
{ "data": { "activation": { "instance_id": "b1c9e7a2-...", "status": "active" } } }

Gotcha: the instance_id must be a stable, unguessable value your app generates once per install and persists (a random UUID) — never a shared or guessable string, or "3 seats" means nothing. The call is idempotent on license_key + instance_id, so a retry returns the existing activation instead of burning a second seat, and a license already at its limit returns an error you can surface as "seat limit reached — upgrade or free a seat."

Recipe 4 — Release a seat on uninstall

The other half of Recipe 3. When a customer uninstalls or signs out of a device, release the seat so it can be reassigned. Identify it by the same license_key + instance_id you activated with — no activation id to track.

POST /api/v1/integration/activations/deactivate
X-API-Key: <your-api-key>

{ "license_key": "XXXX-XXXX-XXXX-XXXX", "instance_id": "b1c9e7a2-..." }

-> 200 OK
{ "data": { "activation": { "status": "revoked", "deactivated_at": "2026-07-10T..." } } }

Gotcha: call this on every uninstall and sign-out — it is idempotent, so releasing an already-released seat still returns 200. It is also non-destructive: the record is kept as revoked for your audit trail, while the seat frees immediately.

Put them together

That is a working licensing loop: validate to gate access, read entitlements to gate features, activate to enforce seats, deactivate to release them. Everything else — revoking a leaked key, verifying offline, renewals — is the same shape: one authenticated request. The full request and response reference is in the license-key API guide, and the copy-paste quickstart (curl, PHP, JavaScript) runs the whole flow end to end in a few minutes. If you want the design thinking around where each call goes, see how to add licensing to your app; if you are moving off a home-grown system, see migrating from DIY licensing.

Frequently asked questions

How do I validate a license key with an API?

Send a POST request to the licensing service’s validate endpoint for that key, authenticated with your secret API key, and read the boolean it returns. With ValidonX that is POST /api/v1/integration/licenses/{licenseKey}/validate with an X-API-Key header; the response’s data.valid is true when the key exists, is active, and has not expired or been revoked. Do the call server-side and treat data.valid as the gate — never decide access purely in client code.

How do I gate a paid feature behind a plan?

Read the license’s entitlements from the validate response and check the one your feature needs. Entitlements are a flexible key-value object — for example {"plan": "pro"} or {"seats": 5, "export": true} — so gating a feature is checking data.license.entitlements for the plan or flag it requires, rather than hard-coding customer IDs. Keeping the gate driven by entitlement data means you can change what a plan includes without shipping a new build.

How do I enforce a seat or device limit?

Record an activation when a customer installs or signs in on a device, and the service counts it against the license’s seat limit. With ValidonX you POST to /api/v1/integration/activations with the license_key and a stable, unguessable instance_id your app generates once per install; the call is idempotent on license_key + instance_id, so retrying never double-counts a seat, and it returns an error when the license is already at its activation limit.

How do I release a seat when a customer uninstalls?

Deactivate the activation you created, identified by the same license_key + instance_id you activated with. With ValidonX that is POST /api/v1/integration/activations/deactivate; it frees the seat so it can be reassigned, is non-destructive (the record is kept as revoked for audit), and is idempotent — safe to call on every uninstall even if a previous attempt already succeeded.

Do I need an SDK to call a licensing API?

No. Every recipe here is a single HTTPS request with a JSON body and an API-key header, so any language that can make an HTTP call can do it — no SDK or framework required. An SDK is a convenience wrapper over these same endpoints; the API is the contract. That is what makes adding licensing a few requests rather than a dependency you have to adopt.

Want to run these against a real key? Start free — issue a key and make your first validate call in minutes, no credit card. Or read the licensing-as-a-service overview first.

We use essential cookies for authentication and session management. Privacy Policy