The update and license API
The endpoints Quikap and QuikapStudio themselves talk 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. - One entitlement covers both products; each product reads its own update channel.
- 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
429with aRetry-Afterheader.
An installation describes itself
Several requests carry a device:
{ "hash": "…43 characters…", "name": "OPS-LAPTOP-7", "platform": "win-x64", "version": "0.1.0", "product": "quikap-studio" }
hash is the SHA-256, in base64url, of the text quikap-device: followed by an identifier that the program makes at
random when it is first run. The identifier itself never leaves the computer, and is not derived from its hardware.
product is quikap or quikap-studio: the two programs on one computer are two installations.
Licenses and entitlements
POST /api/v1/licenses/activate
{ "key": "QK-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-10-09T08:00:00+00:00", "expires": "2026-11-08T08: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 installations. |
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 qk-ent+jwt and the kid of the signing key. Its claims:
| Claim | Meaning |
|---|---|
iss |
https://quikap.exprezoe.com |
aud |
quikap (one audience for the family) |
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. |
Each program checks the signature by itself against the license key compiled into it. The key's public half is published here once the key exists; until then, no release is signed against it and no token is accepted by any build.
| Key | Public key (SubjectPublicKeyInfo, base64) |
|---|---|
quikap-license-2026-10 |
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEY1KoC3Q5ATA0d+QMCt+9eMVleGFAs27OCJbAUFRHKN3Z+zRkJ05vc7O+l3G/pNcPK1EQ1F8mzNSqmow0aqO+XA== |
The update channels
GET /api/v1/updates/{product}/{channel}/{platform}/quikap-update.json
GET /api/v1/updates/{product}/{channel}/{platform}/quikap-update.json.sig
GET /api/v1/updates/{product}/{channel}/{platform}/{installer}
With the bearer token. product is quikap or quikap-studio; 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 for that product on that channel. |
GET /api/v1/releases[?product=…]
With the bearer token. Every release the entitlement includes, of both products or of one, newest first.
Staff sign-in
For people at Exprezoe, in place of a license key.
GET /api/v1/auth/identitysays 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: the program sends it asmax_age).POST /api/v1/auth/identity/challengewith{ "device": { … } }answers{ "nonce": "…", "expires": "…" }.- The program signs in at the issuer in the system browser (authorization code with PKCE), with that nonce.
POST /api/v1/auth/identity/exchangewith{ "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 the programs.
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 products, the site's version and which of the above are switched on.