Keysat Docs
Get started · Integrate the SDK

Integrate the SDK.

Wire Keysat licenses into your software in under an afternoon. The verifier is pure-function, offline, and ships in five lines. What you do with the result — refuse to start without a license, unlock specific features, just show a "supporter" badge — is your call. The SDK is the primitive; the business model is yours.

Prerequisites

Before you start, you should have:

Pick an SDK

Three official SDKs ship today. They are wire-compatible — a license issued by your Keysat verifies identically in any of them.

# npm
npm install @keysat/licensing-client

# pnpm
pnpm add @keysat/licensing-client

If your language isn’t covered, see Wire format. The format is small and porting takes about an afternoon.

Step 1 — Embed your public key

In the admin UI, open Overview and copy the issuer public key from the "Embed your public key" card. (Or fetch it from GET /v1/issuer/public-key.) Paste it into your application’s source code as a compile-time constant.

const ISSUER_PEM = `-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEAmz7q8r4t1v…h3k2pXq9wL
-----END PUBLIC KEY-----`;

Embed it. Don’t fetch it. The whole point of offline verification is that your software can’t be tricked by a network-level attacker. If you fetch the public key at runtime, you’re back to trusting a server.

Step 2 — Verify a license at startup

Read the user’s license key from wherever you store it (a file in their data directory, the OS keychain, an env var) and verify it on application start.

import { Verifier, PublicKey } from '@keysat/licensing-client';

const verifier = new Verifier(
  PublicKey.fromPem(ISSUER_PEM)
);

const result = verifier.verify(licenseKeyFromUser);

// Now decide what to do with the result, based on YOUR business model.
// One-time purchase to use the app at all? Refuse to start unless valid.
// Free + paid features? Check entitlements per feature.
// Supporter badge only? Just render differently when valid.
if (result.valid) {
  app.licensed = true;
  app.entitlements = result.entitlements;
}

The verifier returns a result object with the following fields:

FieldTypeMeaning
validboolSignature checked, expiry not exceeded.
product_idstringThe product slug this license was issued for.
policy_slugstringWhich policy was active at issue time.
license_idstringUUID of the license; useful for support tickets.
issued_atDateUTC timestamp.
expires_atDate | nullnull for perpetual.
is_trialboolSet by the policy at issue time.
seatsintMax machines (0 = unlimited).
entitlementsSet<string>Feature flags baked into the signed payload.

Step 3 — Handle errors gracefully

Verification can fail for benign reasons (the user hasn’t pasted a license yet) or hostile ones (someone tampered with a license file). Distinguish them in your UX:

try {
  const result = verifier.verify(licenseKey);
  if (result.valid) grantAccess(result);
  else showRenewalPrompt(result.expires_at);
} catch (e) {
  if (e instanceof SignatureError) showTamperWarning();
  else if (e instanceof FormatError) showInputError();
  else showGenericError(e);
}

Renewals & revocation

Keysat licenses are signed at issue time and do not phone home. If a license is revoked in the admin UI, the existing key continues to verify in your app — that’s the trade-off for offline.

If you need revocation, ship a thin online check that runs on a cadence (e.g. once a week) against your Keysat’s revocation feed:

// Optional. Run on a cadence, ignore network errors.
async function checkRevocation(licenseId: string) {
  const r = await fetch(`https://your-keysat.example/v1/licenses/${licenseId}/status`);
  if (r.ok) {
    const j = await r.json();
    if (j.status === 'revoked') disableApp();
  }
}

You decide the policy. Many indie developers ship no revocation at all. Once a key is sold, it stays valid — refunds happen offline via BTCPay. That’s perfectly reasonable.

Admin API

The admin UI is a thin shell over a small JSON API. Bearer-auth all requests with your admin API key.

MethodPathUse
GET/v1/productsList products (public).
POST/v1/admin/productsCreate a product.
POST/v1/admin/policiesCreate a policy.
POST/v1/admin/discount-codesCreate a discount or comp code.
GET/v1/admin/licenses/searchFind licenses by email, npub, or invoice.
POST/v1/admin/licenses/<id>/revokeRevoke a license.
POST/v1/admin/webhook-endpointsRegister an outbound webhook.
GET/v1/admin/auditRead audit log.
POST/v1/redeemRedeem a free-license code (public).

Full schemas for each endpoint live in Wire format & API reference.