Migration guide

Move from Keygen to Paperkey.

You export your existing licenses from Keygen, transform the JSON to the Paperkey shape, POST them in. The SDK call site in your shipped app stays the same. The dashboard, the webhooks, and the audit log come up populated on the first reload.

A one-off script against your own instance, sized by your license count

Concept mapping

Most concepts map one-to-one. The names differ in three places (Account, Policy, Token).

  • Keygen
    Account
    Paperkey
    Workspace (account)
    Note
    Top-level isolation. One per company.
  • Keygen
    Product
    Paperkey
    Product
    Note
    Same idea. Carries config (cap, fingerprint policy).
  • Keygen
    Policy
    Paperkey
    Product config
    Note
    Inlined into the product. Per-license overrides on the License.
  • Keygen
    License
    Paperkey
    License
    Note
    Same. Arbitrary key formats accepted on import.
  • Keygen
    Machine
    Paperkey
    Activation
    Note
    Same. Hashed fingerprint, slot of cap.
  • Keygen
    Token (license token)
    Paperkey
    Validation token
    Note
    JWT issued by validate. Compatible drop-in shape.
  • Keygen
    Webhook
    Paperkey
    Webhook event
    Note
    HMAC-SHA-256, hex signature, 5s timeout.
  • Keygen
    Entitlement
    Paperkey
    License metadata
    Note
    Stored as JSON on the license. Read by the SDK.

Four steps

1. Export from Keygen

Use the Keygen API to dump every license into a single JSON file. Replace <account-id> and <keygen-api-token> with your values.

curl -X GET 'https://api.keygen.sh/v1/accounts/<account-id>/licenses' \
  -H 'Authorization: Bearer <keygen-api-token>' \
  -H 'Accept: application/vnd.api+json' \
  > keygen-licenses.json

2. Transform to the Paperkey shape

A small Node one-liner. Reads keygen-licenses.json, projects every license into the Paperkey input format, writes paperkey-licenses.json. No external dependencies.

node -e '
  const data = JSON.parse(require("fs").readFileSync("keygen-licenses.json", "utf8"));
  const out = data.data.map(l => ({
    key: l.attributes.key,
    customerEmail: l.relationships?.user?.data?.id ?? null,
    expiresAt: l.attributes.expiry,
    activationCap: l.attributes.maxMachines ?? 1,
    metadata: l.attributes.metadata ?? {},
  }));
  require("fs").writeFileSync(
    "paperkey-licenses.json",
    JSON.stringify(out, null, 2)
  );
'

3. Import into Paperkey

Paperkey mints license keys server-side. To keep the keys your customers already hold, import the exported rows straight into the licenses table of your self-hosted Postgres. Creating them through POST /licenses issues new keys.

jq -c '.[]' paperkey-licenses.json | while read -r row; do
  curl -X POST https://api.example.com/v1/licenses \
    -H "Authorization: Bearer pk_<your-api-key>" \
    -H 'Content-Type: application/json' \
    -d "$row"
done

4. Update the SDK config

In your shipped app, set the SDK baseUrl to your self-hosted instance. The SDK call site stays the same. Customers keep validating with their existing keys.

import { Paperkey } from '@paperkeyhq/sdk';

const pk = new Paperkey({
  apiUrl: 'https://api.example.com',
  productId: 'prod_<your-product-id>',
  publicKey: '<your-paperkey-public-verify-key>',
});

// Same call site as before. The SDK handles cache + offline grace.
const verdict = await pk.validate(licenseKey);

Known limitations

Honest list. We tell you upfront so you do not discover them in production.

  • Machine fingerprints are regenerated on first activation against Paperkey. Existing Keygen machine slots are translated to "available capacity" on the license, not pre-bound to specific machines.
  • Custom Keygen webhooks are not auto-replayed. Re-subscribe in the Paperkey dashboard with the same target URLs; we will resend on every state change going forward.
  • Complex entitlements (per-feature flags) come over as JSON metadata on the license. The SDK exposes them with `license.metadata.<key>`. Refactor your feature gates accordingly.
  • License analytics history (charts, time series) does not migrate. The Paperkey audit log starts on the day of import.

Need a hand?

If you have more than 10k licenses, complex entitlements, or just want eyes on the migration, write to us with your Keygen account size and a contact time. We do walkthroughs in 30 minutes against your real data, free.

Email hello@paperkey.dev

The managed edition is not open yet

Leave your email to hear when it opens. There is no date. The self-hosted edition runs today, under the MIT license.