Strimivo

Public API

License API

Issue keys in the admin desk, then call these endpoints from your software to activate seats, validate licenses, and free machines.

BASE  https://license.strimivo.co.uk/api/v1
JSON  Content-Type: application/json

Overview

Every licensed install needs a license key, a product slug (from Admin → Products), and a stable machine_id for that device.

1. Activate

Bind the key to a machine and receive a signed offline lease.

2. Validate

On each launch (or heartbeat), confirm the seat is still allowed.

3. Deactivate

Free a seat when the user uninstalls or moves devices.

FieldRequiredDescription
license_key Yes Key like STRM-XXXXX-XXXXX-XXXXX-XXXXX
product Yes Product slug from admin (e.g. strimivo)
machine_id Activate / deactivate / usually validate Stable unique ID for this install. Changing it consumes a new seat.
machine_name No Human label (hostname)
app_version No Your app version string

Authentication

The public license API does not use API keys or admin logins. Clients send the customer’s license key in the JSON body.

Never call /api/admin/* from shipped software. Admin routes are cookie-session only for humans at /admin/.

Endpoints

GET /api/v1/health

Service health check. No body required.

{
  "ok": true,
  "service": "strimivo-license",
  "runtime": "php",
  "lease_hours": 72,
  "public_url": "https://license.strimivo.co.uk",
  "time": "2026-11-15T17:00:00.000Z"
}
GET /api/v1/public-key

Public material to verify signed offline leases (Ed25519 when available).

POST /api/v1/activate

Bind a license to this machine. Safe to call again on the same machine (idempotent).

Request

{
  "license_key": "STRM-XXXXX-XXXXX-XXXXX-XXXXX",
  "machine_id": "unique-stable-device-id",
  "machine_name": "STEPHEN-PC",
  "app_version": "1.0.0",
  "product": "strimivo"
}

Success

{
  "ok": true,
  "valid": true,
  "message": "License activated.",
  "license": {
    "id": "...",
    "key_tail": "XXXXX",
    "product": "strimivo",
    "product_name": "Strimivo",
    "status": "active",
    "type": "perpetual",
    "features": {},
    "expires_at": null,
    "seats_used": 1,
    "seats_max": 1
  },
  "activation": {
    "id": "...",
    "machine_id": "unique-stable-device-id",
    "machine_name": "STEPHEN-PC"
  },
  "lease": {
    "token": "v1....",
    "expires_at": "2026-11-18T17:00:00.000Z",
    "offline_ok": true
  }
}
POST /api/v1/validate

Check that the license is still allowed. If the product uses seat limits (seats_max > 0), this machine must already be activated.

Request

{
  "license_key": "STRM-XXXXX-XXXXX-XXXXX-XXXXX",
  "machine_id": "unique-stable-device-id",
  "product": "strimivo",
  "app_version": "1.0.0"
}

Success shape matches activate (valid: true + refreshed lease).

POST /api/v1/heartbeat

Same body and behaviour as validate. Call periodically while the app is open.

POST /api/v1/deactivate

Release this machine’s seat so another device can activate.

Request

{
  "license_key": "STRM-XXXXX-XXXXX-XXXXX-XXXXX",
  "machine_id": "unique-stable-device-id",
  "product": "strimivo"
}

Success

{
  "ok": true,
  "valid": false,
  "deactivated": true,
  "message": "Machine deactivated.",
  "license": { "...": "..." }
}

Errors

Failures return JSON with ok: false and usually valid: false.

{
  "ok": false,
  "valid": false,
  "error": "seat_limit",
  "message": "All seats are in use (1 of 1). ..."
}
errorMeaningWhat to show the user
invalid_requestMissing/bad fieldsFix the key / required fields
machine_requiredNeed machine_idApp bug — generate machine id
license_not_foundUnknown keyInvalid license key
license_revokedAdmin revokedContact support
license_suspendedTemporarily blockedContact support
license_expiredPast expiryRenew license
product_mismatchWrong product slugKey is for another product
seat_limitNo free seatsDeactivate another PC
not_activatedValidate before activateCall activate first

Recommended app flow

  1. Generate and store a stable machine_id on first install.
  2. Ask for the license key → POST /activate.
  3. If valid: true, unlock the app and store the key + lease.
  4. On every launch → POST /validate (or trust lease until lease.expires_at offline).
  5. While running → POST /heartbeat every few hours.
  6. Settings → “Deactivate this device” → POST /deactivate.

Online checks see revoke/suspend immediately. An offline lease can keep working until lease.expires_at (default 72 hours).

Examples

curl

curl -s -X POST https://license.strimivo.co.uk/api/v1/activate \
  -H "Content-Type: application/json" \
  -d '{
    "license_key": "STRM-XXXXX-XXXXX-XXXXX-XXXXX",
    "machine_id": "dev-1",
    "product": "strimivo",
    "app_version": "1.0.0"
  }'

JavaScript

const BASE = "https://license.strimivo.co.uk/api/v1";

async function activate(key, machineId, product = "strimivo") {
  const res = await fetch(`${BASE}/activate`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      license_key: key,
      machine_id: machineId,
      product,
      app_version: "1.0.0",
    }),
  });
  return res.json(); // check result.valid
}

Prompt for your coding AI

Integrate online licensing using this docs page and:
https://license.strimivo.co.uk/docs/INTEGRATION.md

Base API: https://license.strimivo.co.uk/api/v1
Product slug: strimivo

Implement: stable machine_id, activate on first use, validate on launch,
heartbeat while running, deactivate in settings. Handle every error code.
Never call /api/admin from the shipped app.