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 will 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 plus CPU ID on Windows (
wmic),IOPlatformUUIDon macOS, or/etc/machine-idon Linux. Any cross-platform library that derives a stable hash from hardware identifiers also works.
The endpoint
keyandproduct_idare required.hardware_idis optional but recommended if you want anti-sharing.ipis optional. If you do not 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 does not 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 does not match the bound one.LICENSE_IP_BLOCKED— the resolved IP is not 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. - Later 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 is plenty:- An app that validates once on launch hits the limit only if a single customer is brute-forcing.
- Apps that validate periodically, for example every hour for an “online required” check, must 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 does not currently issue signed offline-validation tokens. Every validate call hits the API live. That means a totally-offline machine cannot 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. Let the app run for N hours or N days before requiring a fresh online call. Pick the grace window based on what is 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 does not include a dedicated licenses
service class yet. You can still call the endpoint through the SDK’s typed raw client:
fetch — both work. There is no C#, .NET, Python, or other language-specific
desktop SDK in the repo yet.
Common pitfalls
- HWID that is not stable. Some hardware-id schemes change on OS update or BIOS reset. Test by validating, rebooting, validating again — same HWID? If not, you will burn legitimate buyers.
- Validating on every API call inside the app. Rate limits will hurt you. Validate on launch and every N hours. Cache between validations.
- 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. A network blip is not the same as an invalid license.
On
500/503/connection errors, fall back to your cached grace window. Do not lock the user out.