Licensing API v1

Central purchase and license authority. OpenAPI is at /api/v1/openapi.json.

Online entitlements

Hosted apps send a product API key and a user, email, or license key to POST /api/v1/entitlements/check.

Device activations

Activate with a stable install id via POST /api/v1/licenses/activate. Some plans include a base device count; extra devices are a second Stripe subscription item when the product supports them. The server sets the effective limit from customer.subscription.created / updated / deleted webhooks. Never send a client-supplied seat count.

If extras drop below the devices already activated, existing activations stay. New activations are blocked until the customer deactivates devices over the limit. Canceled or past-due subscriptions end entitlement the same way as other base plans.

Desktop + web products

Each product has a stable slug in the catalog (for example sudo-sync for a desktop app). Licenses are WDW1 keys. Perpetual desktop plans may enforce a machine_limit; hosted SaaS plans may leave it unset so entitlement is per account. Subscription plans follow Stripe period and status. Manual or free tiers may have no Stripe price and no checkout.

Desktop: POST /api/v1/licenses/activate, verify, and deactivate with licenseKey and machineId. When a plan has a machine limit, enforce it server-side. Honor onlineVerifyGraceMs (72 hours). Offline verifyLicense uses the Ed25519 public key only. Web: POST /api/v1/entitlements/check with a product API key from pnpm keys:product <your-product-slug>. activate does not block subscription plans. Do not send location.

Offline keys

Desktop and mobile apps can verify an Ed25519-signed token with the embedded public key. The token carries product, plan, customer, expiry or perpetual, and machine limit. Activation and deactivation still hit the API. Embed only the public key in the client; keep the product API key on your server.

import { verifyLicense } from 'whatdoyouwant/licensing'

const payload = await verifyLicense(process.env.LICENSE_KEY, process.env.WDW_PUBLIC_KEY)
if (payload.prd !== 'sudo-sync') throw new Error('wrong product')

SSO

Redirect users to /oauth/authorize?client_id=&redirect_uri=. Exchange the code at /api/v1/oauth/token.

Stripe Tax

Checkout collects the billing address and VAT/GST ID. Product apps do not send location. Pass only the usual productSlug, planId, and optional extra-device fields. The server sets automatic_tax, required billing address, tax ID collection, and the catalog tax_code. Prices are exclusive: tax is added on top.

Export

GET /api/v1/export/:productSlug returns only that product's customers and licenses so a single app can be sold later.