Quickstart
Ten minutes, curl, and (for the offline half) Python. Nothing to install.
What you need
Section titled “What you need”| Value | Where to get it |
|---|---|
| API base URL | https://api.waveformanager.com for every merchant. |
| A license key | Place a test order, or have the merchant issue a not-for-resale (NFR) key from the Waveform admin. Keys look like ACME-7F3K-9QPD-2MXR. |
| Product ID (offline products) | The product’s internal ID, shown on the Integration page of the Waveform admin. It is a short opaque string, never a store SKU or a Shopify GID. |
| Public key (offline products) | Also on the Integration page. It is public and safe to ship inside your app. |
Online: validate a license
Section titled “Online: validate a license”An online product asks the API on each use. One request:
curl -sX POST https://api.waveformanager.com/v1/validate \ -H 'content-type: application/json' \ -d '{"key":"ACME-7F3K-9QPD-2MXR","instanceId":"sess_123"}'A valid, active license answers 200:
{ "ok": true, "status": "active", "mode": "online", "features": ["pro"], "expiresAt": 1824014964, "sessions": { "used": 1, "max": 2 }, "usage": { "used": 0, "limit": 3, "period": "2026-10" }}Send the same instanceId again and the session is refreshed rather than counted twice. A different instanceId
beyond the product’s session cap gets 409 with {"error":"session-limit"}. An unknown key is 404 invalid-key; a
revoked or expired one is 403. Full detail in the Online integration guide.
Offline: activate, then verify locally
Section titled “Offline: activate, then verify locally”An offline product activates once, receives a signed token, and from then on verifies it without the network.
-
Compute this machine’s fingerprint. Any stable machine ID works for a test; a real app uses the OS machine ID (see Core concepts).
Terminal window RAW_ID="my-test-machine-1" # a real app: MachineGuid / IOPlatformUUID / machine-idAPP_SALT="my-app-fp-salt-v1" # a constant unique to your product, never changed after releaseFP=$(printf %s "$RAW_ID" | tr 'A-Z' 'a-z' \| openssl dgst -sha256 -hmac "$APP_SALT" -binary \| openssl base64 -A | tr '+/' '-_' | tr -d '=')echo "$FP" -
Activate the key on this device.
Terminal window curl -sX POST https://api.waveformanager.com/v1/activate \-H 'content-type: application/json' \-d "{\"key\":\"ACME-7F3K-9QPD-2MXR\",\"fingerprint\":\"$FP\",\"deviceName\":\"Studio Mac\",\"productId\":\"<product-id>\"}" \> activation.jsonThe response carries the signed
token, the server’spublicKey, and the device record:{"token": "<payload>.<signature>","publicKey": "VXdxEEnp3JGgxVOXgDlo1BwIEu_UuluzUK5e-sIG9IQ","device": { "id": "id_2", "name": "Studio Mac", "createdAt": 1792478978, "lastSeenAt": 1792478978 },"reactivated": false,"rebound": false} -
Verify the token locally. This is what your app does on every launch, with no network. In production you embed the public key from the Integration page rather than trusting the one in the response; the response copy is for this walkthrough.
import json, base64, hmac, timefrom cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKeydef b64d(s): return base64.urlsafe_b64decode(s + "=" * (-len(s) % 4))act = json.load(open("activation.json"))token, public_key = act["token"], act["publicKey"]fingerprint = "<the value of $FP>"payload_b64, sig_b64 = token.split(".")# 1. signature over the exact payload bytes (raises InvalidSignature if forged)Ed25519PublicKey.from_public_bytes(b64d(public_key)).verify(b64d(sig_b64), b64d(payload_b64))# 2. only now trust the claimsclaims = json.loads(b64d(payload_b64))assert claims["alg"] == "ed25519" and claims["v"] == 1assert hmac.compare_digest(claims["fp"], fingerprint) # bound to this machineassert time.time() <= claims["exp"] + claims["grace"] * 86400 # inside lease + graceprint("licensed, features:", claims["feat"])
Where next
Section titled “Where next”- Online product: Online integration covers sessions, usage metering and failure handling.
- Offline product: Offline integration covers the full gate, heartbeats, and device management.
- Audio plugin: the JUCE activation kit does all of this, with a ready-made activation UI.