Migrating from Other Systems

Admin Features

Migrating from Other Systems

Already running licensing on Keygen, Cryptolens, LicenseSpring, or something you built yourself? You can bring your existing licenses into PermitCore without reissuing new keys to a single customer. Licenses → Import reads a CSV export from your old system and creates matching licenses here — using the exact same key strings your customers already have.

Why the key string doesn't need to change

PermitCore never validates a license by comparing its format — it validates by hash. When a customer's software calls POST /api/v1/validate or /activate, the server computes a SHA-256 hash of whatever string was sent and looks it up. That means any string works as a license key, not just PermitCore's own PERMIT-XXXX-XXXX-XXXX-XXXX format — importing simply registers your existing key strings under that same lookup, unchanged.

What import does not do: it does not redirect copies of your software that are already installed on customer machines away from your old vendor's API. Those installs have your old vendor's validate/activate URL built into the release they're running — only a new release, built with a PermitCore SDK, actually stops calling the old vendor. Plan this as a phased rollout: import your license data now, ship the release that switches over, and let already-installed copies fall off the old vendor naturally as customers update — not as an instant cutover.

How it works

  1. Export your licenses from your current system as a CSV (most vendors offer this from their dashboard or API).
  2. Go to Licenses → Import, pick your source system from the dropdown (or "Generic CSV" for anything else), and upload the file or paste its contents.
  3. Map columns — PermitCore guesses the mapping based on the source system you picked; review and correct anything before continuing.
  4. Preview every row before anything is sent — rows with a missing key, an unrecognized product name, or an invalid value are flagged and skipped, without blocking the rest of the file.
  5. Optionally set a passphrase so you can decrypt/reveal these keys again later from the admin panel.
  6. Review the results — each row shows whether it was created, already existed (skipped, not an error), or failed, with a downloadable CSV of anything that didn't import cleanly.

The passphrase is optional for imports

Manually-created licenses always need a passphrase because PermitCore generates the key itself and needs a way to show it to you once. Imported licenses are different — you already have the plaintext key from your old system's export, so encryption here is purely for being able to view the key again later from the admin panel.

If you skip the passphrase, the license still works completely normally for validation and activation (only the key's hash matters for that) — you just won't be able to reveal the plaintext again afterward. This is the same trade-off PermitCore already applies to Store-purchased licenses claimed through a customer's own account.

Carrying over activations already in use

A license that's already been activated on, say, 3 of its 5 seats on your old system needs that carried over — otherwise it would import at 0 activations and let 2 extra devices activate for free before you're even aware. Map a column with the number of seats already used to "Activations already used" in the wizard, and it seeds the license's activation count directly instead of starting from zero.

Field mapping by source system

Field names below are the ones each vendor's own API/export documentation uses at the time of writing — a vendor changing their export format won't automatically update the wizard's auto-guess, but you can always remap columns manually.

Keygen.sh

Keygen fieldPermitCore fieldNotes
keyLicense KeyUsed as-is, no reformatting.
policy / productProductMatched against your PermitCore product names — a Keygen "policy" is roughly a product's license rules.
maxMachinesMax ActivationsKeygen's per-license machine limit.
usesActivations already usedKeygen's own usage counter.
expiryExpires AtISO 8601 timestamp.
(owner relationship — no direct field on the license)Customer EmailKeygen licenses reference a separate user resource; most CSV exports add their own flattened email column.

Cryptolens

Cryptolens fieldPermitCore fieldNotes
keyLicense KeyUsed as-is.
productId / product nameProductMatched against your PermitCore product names.
maxNoOfMachinesMax ActivationsCryptolens's activation limit.
activatedMachinesActivations already usedA list in Cryptolens's API — use its count/length as a plain number in your CSV.
expiresExpires At
customerCustomer Email

LicenseSpring

LicenseSpring fieldPermitCore fieldNotes
license_keyLicense KeyUsed as-is.
productProductMatched against your PermitCore product names.
max_activations (or max_consumptions for metered licenses)Max ActivationsLicenseSpring splits activation-based and consumption-based licenses — map whichever your license type uses.
times_activatedActivations already used
validity_periodExpires At
customer.emailCustomer EmailNested under a customer object in LicenseSpring's API — most CSV exports flatten it to its own column.

Generic CSV / home-grown systems

Any CSV with a column of license key strings works — the wizard doesn't care about your column names, only how you map them. At minimum you need a key column; a product, max activations, activations-used, expiry, and customer email column are all optional and can be left unmapped (a default product and max-activations value apply instead).

What's not carried over yet

Tags, IP/country allowlists, and custom fields aren't part of the import wizard today — they're genuinely not sent, not silently dropped. If your old system's licenses used any of these, add them afterward from each license's Edit screen (or via PUT /api/licenses/{"{id}"} if you're scripting the import).

Permissions

Importing requires the same Create / Edit / Revoke license permission as manually creating a license — if you use custom roles, grant that permission to any role that should be able to import.

API

The wizard is a thin client over POST /api/licenses/import — if you'd rather script a migration directly, hash each existing key with SHA-256 client-side (never send a plaintext key to the server) and POST an array of rows, up to 500 per request:

POST /api/licenses/import
Authorization: Bearer <token>
Content-Type: application/json

[
  {
    "productId": "...",
    "encryptedKey": "",
    "iv": "",
    "plaintextKeyHash": "<sha256 hex of the existing key>",
    "maxActivations": 5,
    "initialActivations": 3,
    "expiresAt": "2027-01-01T00:00:00Z",
    "customerEmail": "[email protected]"
  }
]

Leaving encryptedKey/iv empty (as above) skips the reveal-later capability, matching the wizard's "Skip passphrase" option. Each row reports back one of Created, Duplicate (already imported — not an error), ValidationFailed, or Failed — a bad or duplicate row never blocks the rest of the batch.