Documentation Reference Updated

The update and license API

The endpoints PluggedDesk itself talks to, for people who run a mirror, automate a deployment or want to know exactly what is sent.

Conventions

  • The API is under /api/v1, over HTTPS only. Every answer is JSON, except the installer itself.
  • An installation proves its entitlement with Authorization: Bearer <entitlement>. Cookies are not read by the API.
  • A refusal has this shape, with a status code that matches:
{ "ok": false, "error": "seat_limit", "message": "All 3 seat(s) of this license are in use (…).", "ref": "9f2c41aa" }

error is stable and meant for programs. message is meant for people. ref, when present, is the reference of the line in the site's audit trail.

  • Requests are limited per network address. Over the limit, the answer is 429 with a Retry-After header.

An installation describes itself

Several requests carry a device:

{ "hash": "…43 characters…", "name": "OPS-LAPTOP-7", "platform": "win-x64", "version": "0.5.0" }

hash is the SHA-256, in base64url, of the text pluggeddesk-device: followed by an identifier that PluggedDesk makes at random when it is first run. The identifier itself never leaves the computer, and is not derived from its hardware.

Licenses and entitlements

POST /api/v1/licenses/activate

{ "key": "PD-XXXXX-XXXXX-XXXXX-XXXXX-XXXXX", "device": { … } }

Activates the installation: a new seat, or the seat the installation already has. Answers:

{
  "ok": true,
  "token": "eyJhbGciOiJFUzI1NiIs…",
  "entitlement": {
    "id": "act_…", "kind": "license", "licensee": "…", "organization": "…",
    "plan": "early-access", "planTitle": "Early access", "channels": ["stable"],
    "issued": "2026-09-29T08:00:00+00:00", "expires": "2026-10-29T08:00:00+00:00",
    "seats": { "used": 1, "total": 3 }
  }
}
error Status Meaning
license_unknown 403 The key is not known, or is mistyped.
license_revoked 403
license_expired 403
seat_limit 409 Every seat is in use. The message names the computers.
licensing_unavailable 503 The site has no signing key configured.

GET /api/v1/entitlements/current

With the bearer token. Says what the entitlement is today, from the site's records.

POST /api/v1/entitlements/refresh

With the bearer token, and { "device": { … } }. Answers like activate, with a new token. An expired token cannot be refreshed: activate again.

POST /api/v1/entitlements/deactivate

With the bearer token. Gives the seat back. The token stops working at once.

error Status Meaning
entitlement_expired 401 Activate again.
entitlement_invalid 401 Not a token this site signed, or not for this site.
entitlement_revoked 401 The installation was deactivated.

The entitlement token

A JWS in compact form, signed with ES256. Its header has typ pd-ent+jwt and the kid of the signing key. Its claims:

Claim Meaning
iss https://pluggeddesk.exprezoe.com
aud pluggeddesk
sub The activation.
jti This token.
iat, nbf, exp Issued, not valid before, expires.
kind license or staff
lic, name, org, plan The license, the licensee, the organisation, the plan.
chn The release channels.
dev The installation's hash.

PluggedDesk checks the signature by itself against this key, which is compiled into it:

Key Public key (SubjectPublicKeyInfo, base64)
pluggeddesk-license-2026-09 MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEyrz3xvrDYdbLn+DjQnc/yZ7od1pWDuzVZOTn8CDlRKb/D/G9HczymXXvEB5Tnt3ofINjCvPizE2zfxGsiK0tag==

The update channel

GET /api/v1/updates/{channel}/{platform}/pluggeddesk-update.json
GET /api/v1/updates/{channel}/{platform}/pluggeddesk-update.json.sig
GET /api/v1/updates/{channel}/{platform}/{installer}

With the bearer token. channel is stable or preview; platform is win-x64.

The manifest is served byte for byte as it was signed. It names its installer by file name only, so the installer is read from beside the manifest, and a mirror can serve a release as it is. Both small files carry an ETag; the installer supports ranges, so an interrupted download can be resumed.

error Status Meaning
channel_not_allowed 403 The entitlement does not include that channel.
no_release 404 Nothing has been released on that channel.

GET /api/v1/releases

With the bearer token. Every release the entitlement includes, newest first.

Staff sign-in

For people at Exprezoe, in place of a license key.

  1. GET /api/v1/auth/identity says whether staff sign-in is on, names the issuer and the client to sign in with, and says how recent the sign-in at the issuer must be (maxAge, in seconds: PluggedDesk sends it as max_age).
  2. POST /api/v1/auth/identity/challenge with { "device": { … } } answers { "nonce": "…", "expires": "…" }.
  3. PluggedDesk signs in at the issuer in the system browser (authorization code with PKCE), with that nonce.
  4. POST /api/v1/auth/identity/exchange with { "id_token": "…", "device": { … } } answers like activate.

The site accepts an ID token only when its nonce is a challenge the site issued to that installation and has not seen used. It then checks the signature against the issuer's published keys (RS256 only), the issuer, the audience, the lifetime, that the token is fresh, that the sign-in at the issuer is no older than maxAge (auth_time), that a second factor was used, and that the person is in a group that may use PluggedDesk.

When the issuer has changed its key, the first token signed with the new one is checked against the keys the issuer publishes at that moment. A token that names a key the issuer does not publish costs the site at most one read of the issuer's keys in thirty seconds.

error Status Meaning
identity_disabled 503 Staff sign-in is not switched on.
challenge_unknown 401 The sign-in was not started with this site, or took longer than ten minutes.
token_replayed 409 That sign-in was already used.
token_invalid 401 The ID token could not be verified.
stale_authentication 401 The sign-in at the issuer is older than maxAge, or the token does not say when it was made.
mfa_required 403 No second factor was used.
not_in_required_group 403
issuer_unavailable 503 The issuer's keys could not be read.

What the site is

GET /api/v1/health answers { "ok": true } when the site and its records are reachable. GET /api/v1/meta names the site's version and which of the above are switched on.