Concepts

How the license model works

This is the mental model behind Simple License Server. If you understand these concepts, the endpoint behavior is straightforward.

System flow in plain language

  1. You define a slug (policy template) for a product or plan.
  2. Your trusted backend calls POST /generate with that slug.
  3. The server creates a license key with expiration and seat limits resolved at that moment.
  4. The client app activates the license with a fingerprint, consuming a seat.
  5. The client periodically validates the same license+fingerprint pair.
  6. Seats can be released with POST /deactivate; full access can be blocked with POST /revoke.

What is a slug?

A slug is a policy anchor for new licenses. It does not represent a customer; it represents how licenses should behave when they are created.

  • Defines max_activations (seat limit).
  • Defines expiration policy (forever, duration, or fixed_date).
  • Is resolved at generation time, then stored on the license record.
  • Changing a slug later affects future licenses, not already-issued ones.

By default, the server creates a default slug with one seat and no expiration.

Want a deeper view of naming rules and policy snapshots? See Slug concepts.

How seats work

Seats are tracked as active activation rows per fingerprint. A seat is in use when that fingerprint has an activation with no deactivated_at value.

  • /activate with a new fingerprint creates a seat if the license still has capacity.
  • Calling /activate again for an already-active fingerprint does not consume another seat.
  • /deactivate releases that fingerprint seat immediately.
  • Revocation is different: /revoke blocks the whole license, not one seat.

License API vs Management API

  • License API: /generate, /revoke, /activate,/validate, /deactivate.
  • Management API: /management/slugs, /management/api-keys, and /management/webhooks routes for operator tooling and frontend-backed workflows.
  • Never call key-protected routes directly from browser code.
  • For payment webhooks, use provider event id as Idempotency-Key on /generate.

Outbound webhooks and idempotency

Outbound webhooks are asynchronous delivery jobs. Endpoint configuration lives in Management API, and delivery is triggered by License API events.

  • Delivery retries can happen, so receivers should dedupe using Idempotency-Key: sls-webhook-<delivery_id>.
  • Additional headers include X-Webhook-Delivery-Id and X-Webhook-Event.
  • Payload shape includes top-level id, type, attempt, occurred_at, and a data object with event-specific fields.
  • Current event types: license.generated, license.activated, license.deactivated, license.validated, license.validation_failed, license.revoked.

For full contract details (headers, envelope, and event payload schemas), see Webhook concepts.

Glossary

ConceptMeaning
SlugPolicy template for newly generated licenses. Controls max activations and expiration policy for licenses created under it.
LicenseIssued key tied to a slug. Stores status, metadata, created time, and resolved expires_at.
SeatOne active fingerprint binding for a license. Seats are represented by active rows in activations.
FingerprintDevice/app-instance identifier sent by your client to activate, validate, and deactivate.
License APILicense lifecycle endpoints. /generate and /revoke require a generated server API key; runtime routes are called by your app.
Management APIOperator/internal endpoints under /management/* that require a management bootstrap key.
Runtime APIClient-facing endpoints (/activate, /validate, /deactivate) used by your shipped app.
Idempotency-KeyHeader used on /generate to make webhook retries safe and avoid duplicate license issuance.
RevocationLicense-level action that sets status to revoked and blocks future activate/validate calls.