Licensing
Three levels, picked per product in Pricing and licensing. Most new apps should use Level 2, Sign in with AppsXi: one button, in-app trials and automatic unlock after purchase.
Level 0: no code
AppsXi checks the purchase when the buyer downloads. Each purchase gets a license key in the buyer's library, and refunds and chargebacks revoke it. Good for apps that do not need copy protection.
Level 1: license key API
Each purchase gets a key like AXI-7KQ2M-9WD4T-1HXZ8-C3PNB. Your app validates it once, stores the signed token, and checks the token offline after that.
Product keys
In Developer → your app → Licensing you get two publishable product keys:
pk_test_…works only with sandbox licenses (test purchases and sandbox trials).pk_live_…works only with real licenses.
A product key can only validate; it cannot read buyer data, so it is safe to ship in the app.
Validate
POST https://appsxi.com/api/v1/licenses/validate
Authorization: Bearer pk_live_…
Content-Type: application/json
{ "license_key": "AXI-7KQ2M-9WD4T-1HXZ8-C3PNB", "device_id": "<SHA-256 of a stable machine id>", "device_name": "Studio Mac", "platform": "macos" }Send a hash of the machine id, never the raw id. A valid response:
{
"valid": true,
"status": "active",
"license": "lic_…",
"seats_used": 1,
"seats_max": 2,
"license_type": "personal",
"trial_expires_at": null,
"token": "<EdDSA JWT>",
"updates": { "feed_token": "axf_…", "feed_base": "…", "appcast_url": "…" }
}When valid is false, reason is one of unknown_product_key, unknown_license, seat_limit, trial_used_on_device, device_hash_required, rate_limited, or the license status (revoked after a refund or chargeback, expired after a trial ends).
Offline tokens
token is a compact JWT signed with Ed25519 (alg: EdDSA). Verify it with AppsXi's public key from /api/v1/licenses/public-key (a JWK set; embed it in your app). Claims:
| Claim | Meaning |
|---|---|
sub | Pseudonymous buyer id, stable per app. |
product | Your app. |
license | License id. |
status | active or trial. |
env | live or sandbox. |
seats | { this_device, max }. |
type | The license type: personal, commercial, team or education. |
exp | 30 days for a license; never past a trial's end. |
Validate again when the token is close to exp. If the network is down, keep running until exp.
Helpers
Single-file helpers do the validation, offline check, trial status, checkout link and feed URL for you. Copy the file into your project:
| Language | File | Needs |
|---|---|---|
| TypeScript (Electron, Node 18+) | appxi-license.ts | nothing |
| Swift (macOS 12+) | AppXiLicense.swift | nothing beyond the system frameworks |
| C++ / JUCE | appxi_license.hpp, appxi_license.cpp | OpenSSL; libcurl or your own HTTP function (for example juce::URL) |
| .NET 6+ | AppXiLicense.cs, AppsXi.License.csproj | BouncyCastle.Cryptography |
See the helpers README for what each call returns.
let license = AppXiLicense(productKey: "pk_live_…", publicKeys: ["<x>"], appSlug: "my-app")
_ = try await license.activate(key: enteredKey) // once, online
if let e = license.current() { unlock(e) } // every launch, offline
if license.needsRefresh() { _ = try? await license.refresh() }Seats
Each device_id takes one seat. Buyers free seats from their AppsXi library.
seats_max comes from the license the buyer bought: your Computers per license setting for personal, commercial and education licenses, and the team's number of computers for a team license (see License types). You do not need to do anything for team licenses: the same key simply activates on more computers.
Trials
Set a trial of 3, 7, 14 or 30 days on the app and choose AppsXi as who runs it. People start a trial from the listing and get a trial key; the response has status: "trial" and trial_expires_at, then expired when it ends. One trial per device. When they buy, the same key becomes a full license.
A trial goes on only after AppsXi has seen your app check a license once: issue a test trial key on the Sandbox tab and activate it in a development build. At Level 0 there is no AppsXi trial, because nothing in the app would stop it; apps with their own license server can run their own trial instead (see Using your own license server).
Sandbox
Test purchases in the Square sandbox create sandbox licenses that only pk_test_ keys accept. Use them in development builds.
Your app → Sandbox issues test keys without a purchase (full licenses or trials, any seat count) and lets you:
- move a trial's clock forward a day or a week, expire it now, or restart it;
- convert a trial to a full license, as a purchase does;
- revoke a key, as a refund does, and restore it;
- free all seats;
- send
purchase.createdfor a test key to your license bridge, and replay any past bridge event.
Sandbox trials use their own one-trial-per-device record, so testing never uses up a device's real trial. Sandbox licenses never count toward sales, reviews or rankings.
License types
Besides the personal license, you can sell commercial, team and education licenses with their own prices, set under License types on the Pricing page. The type is a matter of the license terms; AppsXi enforces two things for you:
- Seats: a team license's
seats_maxis the number of computers it covers, at Level 1 and Level 2. - Who can buy education: only buyers with a confirmed school email (
.edu,.edu.<country>,.ac.<country>,.k12.<state>.us). The check runs in the database when the order is priced.
The buyer's library and receipt show the type. Your app can read it too: license_type in the license API and Sign in with AppsXi answers, type in the offline token, and license.type (with the computers as license.seats) in license bridge events. Team licenses need AppsXi keys or the bridge, not your own uploaded keys, because uploaded keys are one per purchase. Details and price rules are in Pricing.
Already have a license system?
Upload a batch of your own keys or single-use coupon codes, or use the license bridge: AppsXi sends each sale to your server, and the key you return appears in the buyer's library. See Using your own license server.