Licences

A licence is a one-of-a-kind key issued at purchase for type: license products. Each licence has a maximum activation count (maxActivations); your software calls POST /licenses/:key/activate from the user's device on first launch and burns one activation. Storlaunch's licence storage is canonical in Fulkruma — these endpoints proxy through to Fulkruma so you have a single auth surface.

The activation and validation endpoints are unauthenticated by design: your distributed software can hit them with just the licence key. The list and revoke endpoints are merchant-scoped and need an sk_* key.

Endpoints

Method Path Auth Purpose
GET /v1/storefront/licenses sk_* List all licences for this workspace
GET /v1/storefront/licenses/validate?key=… none Validate a licence key (read-only)
GET /v1/storefront/licenses/:key sk_* Retrieve one licence by key
POST /v1/storefront/licenses/:key/activate none Activate a licence on an installation
POST /v1/storefront/licenses/:key/deactivate none Free an activation slot
DELETE /v1/storefront/licenses/:key sk_* Revoke a licence

Licence storage lives in Fulkruma. Storlaunch keeps no local License table — reads and writes round-trip to Fulkruma using your scoped account context. There's a single network hop on every operation; cache validate-success responses for a few minutes on your end if you're going to call from a hot loop.

List licences

GET /v1/storefront/licenses

Returns every licence ever issued in the workspace, sorted newest first. No pagination cursor at the moment — the list endpoint returns the full set in one shot.

Response200 OK

{
  "data": [
    {
      "id": "lic_01HXAB7K3M9N2P5QRS8TVWXY3Z",
      "key": "STORLAUNCH-AB12-CD34-EF56-GH78",
      "productId": "prod_01HXxxxxxxxxxxxxxxxxxxxxxx",
      "customerId": "cus_01HXxxxxxxxxxxxxxxxxxxxxxx",
      "checkoutSessionId": "cs_01HXxxxxxxxxxxxxxxxxxxxxxx",
      "activations": 1,
      "maxActivations": 3,
      "status": "active",
      "expiresAt": null,
      "createdAt": "2026-05-13T10:42:00Z"
    }
  ],
  "error": null,
  "meta": { "requestId": "req_01HX...", "timestamp": "2026-05-13T10:42:00Z" }
}

Validate a licence

GET /v1/storefront/licenses/validate?key=…&productId=…

Unauthenticated read. Returns whether the key is valid and (optionally) bound to the right product. Never throws on a bad key — you get valid: false and an empty body.

Use this from your software's startup check loop: it's free, public, and safe to call repeatedly.

Query parameters

Param Type Required Description
key string yes The licence key. Case-sensitive.
productId string no Bind the validation to a specific product. valid is false if the key exists but is for another product.

Response200 OK

{
  "data": {
    "valid": true,
    "key": "STORLAUNCH-AB12-CD34-EF56-GH78",
    "productId": "prod_01HX...",
    "activations": 1,
    "maxActivations": 3,
    "expiresAt": null
  },
  "error": null,
  "meta": { "requestId": "req_01HX...", "timestamp": "2026-05-13T10:42:00Z" }
}

When valid is false, the rest of the fields may be null.

Retrieve a licence

GET /v1/storefront/licenses/:key

Merchant-authenticated read by key. Returns the full licence object.

Status error.code When
404 RESOURCE_NOT_FOUND Key doesn't exist in this workspace.

Activate a licence

POST /v1/storefront/licenses/:key/activate

Burns one activation slot. Idempotent on instanceId — calling with the same instanceId twice doesn't double-count, and the response includes alreadyActive: true on the repeat.

Path parameters

Param Type Description
key string The licence key.

Request body

Field Type Required Description
instanceId string yes A stable, device-or-install-unique ID. UUID, machine ID, anything — just keep it consistent across that install's restarts so re-activation doesn't burn slots.

Response200 OK

{
  "data": {
    "activated": true,
    "key": "STORLAUNCH-AB12-CD34-EF56-GH78",
    "activations": 2,
    "maxActivations": 3,
    "instanceId": "device-7f3a91b2",
    "alreadyActive": false
  },
  "error": null,
  "meta": { "requestId": "req_01HX...", "timestamp": "2026-05-13T10:42:00Z" }
}

Errors specific to this endpoint

Status error.code When
404 RESOURCE_NOT_FOUND Invalid or revoked licence key.
422 SUBSCRIPTION_INACTIVE Key has expired (expiresAt in the past).
422 ACTIVATION_LIMIT_REACHED All activation slots are in use. The user needs to deactivate one or buy more seats.

Deactivate a licence

POST /v1/storefront/licenses/:key/deactivate

Frees one activation slot. Idempotent on instanceId — releasing twice is fine; you get alreadyDeactivated: true.

Request body

Field Type Required Description
instanceId string yes The same install ID used in activate.

Response200 OK

{
  "data": {
    "deactivated": true,
    "key": "STORLAUNCH-AB12-CD34-EF56-GH78",
    "activations": 1,
    "instanceId": "device-7f3a91b2",
    "alreadyDeactivated": false
  },
  "error": null,
  "meta": { "requestId": "req_01HX...", "timestamp": "2026-05-13T10:42:00Z" }
}

Revoke a licence

DELETE /v1/storefront/licenses/:key

Permanently invalidates the licence. Merchant-only. Use sparingly — revoking is for chargebacks, fraud, and abuse only. For a refund, deactivate and let the licence expire naturally so audit trails stay clean.

Response204 No Content.

// Node
await client.licenses.revoke('STORLAUNCH-AB12-CD34-EF56-GH78');

The licence object

Field Type Nullable Description
id string no Fulkruma's internal ID. Prefixed lic_.
key string no The user-visible key. Format depends on the licence template; usually STORLAUNCH-XXXX-XXXX-XXXX-XXXX. Always unique.
productId string no The product this licence is for.
customerId string yes The buyer (when known).
checkoutSessionId string yes The checkout session that issued it.
activations integer no Count of currently-active installs.
maxActivations integer no Cap (from the product config at issue time).
status enum no active, expired, revoked.
expiresAt string (ISO 8601 UTC) yes Hard expiry, if set.
createdAt string (ISO 8601 UTC) no Issue time.

Activation pattern

Your software's startup flow typically looks like:

  1. Read the cached key and instanceId from local storage.
  2. If instanceId is empty, generate one and persist it.
  3. Call GET /validate?key=….
  4. If valid: true and activations >= 1, proceed.
  5. If valid: true but activations == 0, call POST /:key/activate.
  6. If valid: false, fall back to the licence-entry UI.

Cache the validate result locally for 5–15 minutes so you're not hammering the API every cold start. Revalidate on long uptimes (every few hours) to pick up revocations.

Events

Licence lifecycle events fire from Fulkruma directly, not Storlaunch — subscribe to them on your Fulkruma webhook endpoint, not Storlaunch's. The Storlaunch outbox stays catalogue-focused.

You'll see product.purchased (Storlaunch event) at the moment a licence is issued; treat that as the "new licence" trigger if you want a single subscription surface.

Next

  • Products — flip licenseEnabled: true to issue licences on purchase.
  • Deliveries — the licence key is included in the delivery record.
  • Public storefront — how buyers receive their key.