You ship a desktop application (Windows, macOS, Linux — Electron, .NET, Qt, native, whatever) and you want it to activate against Shoppex license keys. Each customer’s purchase issues a key; your app calls Shoppex to validate it and binds to the user’s hardware so the key can’t be shared. This tutorial walks through the activation flow end to end.Documentation Index
Fetch the complete documentation index at: https://docs.shoppex.io/llms.txt
Use this file to discover all available pages before exploring further.
How Shoppex licenses work
When a buyer purchases a product fulfilled by Licenses, Shoppex issues a license object with:- A unique
license_keystring the buyer sees and enters into your app. - Status:
ACTIVE,SUSPENDED,REVOKED, orEXPIRED. - Optional HWID binding — when the first activation comes in with a hardware ID, that HWID gets bound. Future activations from different hardware fail.
- Optional IP allowlist —
allowed_ipsarray; non-matching IPs are rejected. - Optional max_uses and expires_at — limits on usage count and validity period.
What you’ll need
- A Shoppex API key with the
licenses.readscope. Create one at Settings → Developer → API Keys. - A way to compute a stable hardware fingerprint in your app. Common approaches:
motherboard serial + CPU ID on Windows (
wmic),IOPlatformUUIDon macOS,/etc/machine-idon Linux, or any cross-platform library that derives a stable hash from hardware identifiers.
The endpoint
keyandproduct_idare required.hardware_idis optional but recommended if you want anti-sharing.ipis optional. If you don’t pass it, Shoppex readsx-forwarded-for/x-real-ipfrom the request headers and uses that.
Response
On success, you get the license object back wrapped in Shoppex’s standard envelope:LICENSE_NOT_FOUND— key doesn’t exist in this shop.LICENSE_SUSPENDED— license is suspended (merchant-side action).LICENSE_REVOKED— license was revoked.LICENSE_EXPIRED— pastexpires_at.LICENSE_HWID_MISMATCH— the HWID you passed doesn’t match the bound one.LICENSE_IP_BLOCKED— the resolved IP isn’t inallowed_ips.LICENSE_MAX_USES_REACHED—uses>=max_uses.
HWID binding behavior
The HWID logic is automatic:- First call with a HWID, license
hwid_pending: true— Shoppex binds the HWID to the license and returns success. The license is now locked to that machine. - Subsequent calls with the same HWID — pass through. License returned.
- Call with a different HWID — Shoppex returns
LICENSE_HWID_MISMATCH. The buyer is trying to activate on a second machine.
hardware_id to null puts the license back in hwid_pending: true state. Setting
it to a string binds that HWID directly.
Rate limits
The Dev API uses a token bucket — by default 30 tokens, refilling 10 tokens every 2 seconds. Validate calls count against this. For most apps that’s plenty:- An app that validates once on launch hits the limit only if a single customer is brute-forcing.
- Apps that validate periodically (e.g. every hour for “online required”) should batch and back off on 429.
x-ratelimit-limit, x-ratelimit-remaining,
x-ratelimit-reset. On 429, respect the retry-after header.
A minimal activation flow
Caching: how to handle “the user is offline”
Shoppex doesn’t currently issue signed offline-validation tokens. Every validate call hits the API live. That means a totally-offline machine can’t validate. Most apps handle this by caching successful validations for a grace period: store the last successful validation timestamp locally (signed by your app’s own key to prevent tampering), and let the app run for N hours / N days before requiring a fresh online call. Pick the grace window based on what’s acceptable in your domain — game keys often run with 24-48 hour grace, business software often runs with 30 days.SDK note
The official@shoppexio/sdk for Node/TypeScript doesn’t include a dedicated licenses
service class yet. You can still call the endpoint via the SDK’s typed raw client:
fetch — both work. There’s no C#, .NET, Python, or other
language-specific desktop SDK in the repo yet.
Common pitfalls
- HWID that isn’t stable. Some hardware-id schemes change on OS update or BIOS reset. Test by validating, rebooting, validating again — same HWID? If not, you’ll burn legitimate buyers.
- Validating on every API call inside the app. Rate limits will hurt you. Validate on launch + every N hours; cache between.
- Not handling 429. Even a well-behaved app gets rate-limited if a user manually retries
10x. Honor
retry-after. - No fallback for transient errors. Network blip ≠ invalid license. On
500/503/ connection errors, fall back to your cached grace window, don’t lock the user out.