1. Customer purchases on Permisoft
Checkout runs on our platform via POST /api/checkout (not a publisher API). We issue a unique licence key and email the buyer when the order is PAID.
Permisoft Licensing is the system of record for perpetual licence keys on the marketplace — like a store entitlement layer, without FlexNet, SafeNet, or monthly license-server fees. Your software never mints keys; Permisoft issues them on every purchase and customers activate through our public API.
Checkout runs on our platform via POST /api/checkout (not a publisher API). We issue a unique licence key and email the buyer when the order is PAID.
Integrate @permisoft/client or call POST /api/keys/activate with the key and a stable machine ID. The key binds to one machine.
Use ps_live_… for production and ps_test_… for sandbox. Validate keys (POST /api/v1/publisher/keys/validate), pull stats (GET /api/v1/publisher/stats), and create test purchases (POST /api/v1/publisher/sandbox/purchase).
Internal runbook for testing your own product: see docs/VENDOR_ONBOARDING.md in the repo.
Every paid order creates exactly one key in Permisoft. Keys are uppercase, segmented, and include a checksum. Your publisher prefix is assigned at approval (from your studio slug).
Example: MYAPP-A3B7-K9D2-M4F8-42
Audience: Desktop app / installer on the customer machine
Auth: None (rate limited by IP)
POST https://permisoft.app/api/keys/activate
Content-Type: application/json
{
"key": "MYAPP-A3B7-K9D2-M4F8-42",
"machineId": "stable-hardware-id-from-your-app",
"machineLabel": "Paul's MacBook (optional)",
"productSlug": "myapp-myproduct (optional, for extra validation)"
}Success
{
"valid": true,
"message": "Activation successful",
"scope": "PRODUCT",
"productSlug": "myapp-myproduct",
"activationToken": "eyJ...",
"tokenExpiresAt": "2026-06-01T00:00:00.000Z",
"publisherSlug": "myapp"
}Error codes
Use @permisoft/client activateOnline(apiBaseUrl, { key, machineId }) — caches JWT for offline grace.
Audience: Your backend, license server, or support tooling
Auth: Authorization: Bearer ps_live_… or ps_test_…
POST https://permisoft.app/api/v1/publisher/keys/validate
Authorization: Bearer ps_live_xxxxxxxx
Content-Type: application/json
{ "key": "MYAPP-A3B7-K9D2-M4F8-42" }Responses
{ "valid": false, "reason": "invalid_format" }{ "valid": false, "reason": "not_found" }{
"valid": true,
"status": "UNUSED" | "ACTIVATED" | "REVOKED",
"product": { "id": "...", "slug": "...", "name": "..." },
"activated": true,
"activatedAt": "2026-05-20T12:00:00.000Z",
"keyMasked": "MYAPP-****-****-****-42",
"sandbox": false
}Auth: Authorization: Bearer ps_live_… or ps_test_… (scope stats:read)
GET https://permisoft.app/api/v1/publisher/stats?from=2026-05-01&to=2026-05-31&productId=optional-cuid
{
"publisherSlug": "myapp",
"sandbox": false,
"platformFeePercent": 5,
"totals": {
"unitsSold": 12,
"grossCents": 48000,
"platformFeeCents": 2400,
"netPublisherCents": 45600,
"pendingPayoutCents": 45600
},
"keysIssuedByProduct": [{ "productId": "...", "count": 12 }],
"sales": [{ "orderId": "...", "grossCents": 4000, "platformFeeCents": 200, ... }]
}Auth: Authorization: Bearer ps_test_… (scope stats:read)
POST https://permisoft.app/api/v1/publisher/sandbox/purchase
Authorization: Bearer ps_test_xxxxxxxx
Content-Type: application/json
{ "productId": "cuid-of-your-product", "holderName": "QA tester" }{
"sandbox": true,
"orderId": "...",
"licenseKey": "MYAPP-....",
"message": "Sandbox purchase created..."
}