Documentation

Licenses

Complete documentation for LoginSign license management: variants, code lifecycle, developer APIs, assignment, user redemption, inventory sync, marketplace deals, and status-based licensing.

1. What licenses do in LoginSign

  • Map your commercial plans (for example Free, Pro, Team, Enterprise) to configurable license variants.
  • Allow users to redeem codes after they connect your app, or send marketplace buyers through /activate/:appId so LoginSign creates the connection first.
  • Assign a license or unused code from a connected user in the portal (one-time per code).
  • Enable backend-driven status sync that grants/revokes licenses by globalId.
  • Let your backend pull the full code inventory with Basic Auth.
  • Publish on AppSumo and other deal marketplaces via the hosted connector and partner APIs. See Marketplace licenses.
  • Provide searchable, auditable code inventory with owner and state metadata.

2. Core model

Variant

A variant defines a plan type (name and optional description) for one application. Validation is set when you generate, upload, or assign codes.

Code

A code belongs to one variant and can be redeemed once per redemption lifecycle. Marketplace keys (AppSumo UUIDs or imported deal codes) are stored as the same code row, plus optional marketplace metadata. See Marketplace licenses.

Datastore (PostgreSQL / Prisma)

Internally each code row belongs to exactly one variant. After redemption, LoginSign persists which AppConnection holds the entitlement via the foreign key linkedConnectionId. Developer APIs return a derived owner object instead of exposing the raw key. Rows also store source (MANUAL, MARKETPLACE, STATUS_SYNC) and optional marketplace fields (marketplaceLicenseKey, tier, parent key). If you maintain database dumps or external reporting, run prisma migrate deploy so historical renames stay aligned with prisma/migrations/.

State

UNUSED   Code exists but was not redeemed yet
ACTIVE   Code is linked to an app connection
EXPIRED  Code reached expiry (non-lifetime) or was expired by flow logic

Validation

How long the code stays valid after it is activated. Unused codes have no running timer. When a code is assigned or redeemed, activatedAt is set and the validation period starts from that moment: one month, one year, or lifetime.

MONTH | YEAR | LIFETIME

3. Console workflow (recommended)

  1. Create one or more variants in /console/licenses.
  2. Generate codes in bulk or upload predefined codes via CSV.
  3. Assign from a user in /console/users, or let the user redeem a code.
  4. A code can be given only once. After activatedAt is set, it cannot be assigned again.
  5. Monitor code states and owners in the license table.
  6. Pull the full inventory from your backend via License Sync.
  7. For AppSumo and other deal marketplaces, use the hosted connector and partner write APIs in Marketplace licenses.

4. Variant APIs (owner session)

GET    /api/developer/applications/:appId/licenses/variants
POST   /api/developer/applications/:appId/licenses/variants
PATCH  /api/developer/applications/:appId/licenses/variants/:variantId
DELETE /api/developer/applications/:appId/licenses/variants/:variantId

Create variant example

{
  "name": "Premium",
  "description": "Premium access",
  "active": true
}

Delete with replacement behavior

  • If no codes are linked: delete returns 204.
  • If linked codes exist and no replacement is sent: returns 409 with replacement options.
  • Send replacementVariantId to migrate linked codes and delete safely.

5. Code APIs (owner session)

POST   /api/developer/applications/:appId/licenses/codes/generate
POST   /api/developer/applications/:appId/licenses/codes/upload
GET    /api/developer/applications/:appId/licenses/codes?state=all|used|unused&q=...
DELETE /api/developer/applications/:appId/licenses/codes/:codeId

Generate codes example

{
  "quantity": 500,
  "variantId": "var_123",
  "duration": "MONTH"
}

Quantity is clamped server-side to 1..50000. duration is the validation period that starts when the code is activated.

Upload codes example

{
  "variantId": "var_123",
  "duration": "YEAR",
  "codes": [
    "ABC1-DEF2-GHI3",
    { "code": "JKL4-MNO5-PQR6", "variantId": "var_999", "duration": "LIFETIME" }
  ]
}
  • Max upload size: 5000 rows per request.
  • Invalid rows and duplicates are skipped.
  • Response includes createdCount and skippedCount.

6. Assign from a user (owner session)

In the portal, open a connected user and use Assign. The same API assigns either an unused code or a variant plus validation. If no unused matching code exists, LoginSign creates one already active for that user.

POST /api/developer/applications/:appId/users/:connectionId/licenses
{ "codeId": "lic_123" }

{ "variantId": "var_123", "duration": "MONTH" }
  • The user must be an active, unblocked connection.
  • A used code (state not unused, or activatedAt already set) returns 409.
  • Validation starts at assignment: activatedAt and expiresAt are written then.

7. User redemption APIs (user session)

GET  /api/user/licenses/subscriptions
POST /api/user/licenses/redeem

Redeem request

{
  "code": "ABC1-DEF2-GHI3"
}

Redemption rules

  • Code must exist and not be expired.
  • Code already redeemed by another user returns conflict.
  • The user must already have an active app connection for that application. Marketplace buyers should use /activate/:appId instead — that flow creates the connection, then redeems. See Marketplace licenses.
  • Activation persists linkedConnectionId, activatedAt, and calculated expiresAt.

Typical errors

  • 404: invalid code.
  • 400: expired code or missing connection.
  • 409: already used or race condition during concurrent redeem.

8. Partner write APIs and marketplace (Basic Auth)

The same Basic Auth credentials used for inventory pull can generate, import, look up, redeem, and revoke codes, and ingest marketplace events. Hosted AppSumo URLs and the activation page live on Marketplace licenses.

POST /api/applications/:appId/licenses/codes/generate
POST /api/applications/:appId/licenses/codes/import
GET  /api/applications/:appId/licenses/:code
POST /api/applications/:appId/licenses/:code/redeem
POST /api/applications/:appId/licenses/:code/revoke
POST /api/applications/:appId/marketplace/events
GET  /activate/:appId?code=

Successful redeem, assign, upgrade, and revoke also POST license_activated / license_updated / license_deactivated to your app webhookUrl.

9. Pull the full code inventory (Basic Auth)

Your backend can sync every license code in one request. No portal session is required.

GET /api/applications/:appId/licenses
GET /api/applications/:appId/licenses?state=all|used|unused

Response: { codes, total, summary }. Full contract: License Sync.

10. Status sync integration with licenses

Your backend can grant/revoke license-backed status directly:

PATCH /api/applications/:appId/users/:globalId/status
  • Uses Basic Auth (client_id:client_secret).
  • Can auto-create variant if variantName does not yet exist.
  • duration is validation after activation (defaults to LIFETIME).
  • revoke: true expires active redeemed licenses for that user.

Status PATCH updates one user. It does not replace the inventory pull. Details: Status.

11. Filtering, search, and reporting

  • Filter codes via state=all|used|unused.
  • Search by code, marketplace key, owner display name, owner email, and globalId.
  • Listing response includes summary counters: total, used, unused.

12. Operational best practices

  • Create clear variant naming conventions before large code generation.
  • Use CSV exports regularly for backup/audit trails.
  • When deleting variants, always verify replacement mapping first.
  • Treat generated and uploaded codes as secrets until redeemed.
  • Monitor spikes in redeem failures to detect integration issues.
  • For AppSumo and other deal sites, map tiers before go-live and search by marketplace key when support files a ticket.

13. Related docs