Choosing a Licensing Model

When first starting out, choosing a licensing model can be a bit daunting. There are so many choices, how do you choose which one is best for your business? A quick Google search is likely to confuse you more than educate you, thanks to a plethora of enterprise jargon and buzzwords. Today, we'll take an in-depth look at licensing from a business perspective so that you can make an educated decision on what type of model is best for you and your business.

Licensing terminology

Below is a non-exhaustive reference for some of the terminology used in this guide and throughout the licensing space. Feel free to refer back to this throughout the guide, but we'll try to keep things as straight forward as possible, regardless. If you're a developer implementing Keygen, this list will also provide you with a good overview of all of our resources.

  1. Users are a particular resource within Keygen that represents a user of your software, allowing them to authenticate with Keygen's API and manage the licenses and machines which are associated with their user resource.
  2. Licenses are a particular resource within Keygen that, in their simplest form, represent a permission i.e. you give the license owner permission to do x within your software, whether x is to use your software as whole, or only a particular feature your software offers.
  3. Policies are resources within Keygen that define how individual licenses should behave, and what rules those licenses must follow. A license is always an implementation of a policy i.e. you cannot create a license without first having a policy that defines its behavior.
  4. Machines are a device or node that is associated with a license within Keygen. When creating a machine and associating it with a license, you must specify a unique string called a fingerprint which the machine is identified by.
  5. A fingerprint is a unique string that identifies a particular machine. There are many ways to create a fingerprint, but most use a combination of device-specific components such as MAC address, HDD serial number, OS, etc., but it could also simply be a domain name (e.g. when licensing a WordPress plugin), or even a file you store locally on their machine containing the unique string.

Licensing goals

Before we go any further, we should take a step back and think about why we want to implement licensing and what we want to accomplish through licensing. Only then can we begin to decide what licensing model will best accomplish those goals.

  • I want to limit access of my software to only licensed users so that I can make sure all of my users are paying customers.
  • I want to offer timed licenses to each of my users so that they must renew their license at the end of the expiry.
  • I want to offer a limited trial version of my software to users so that users can try out my software before purchasing.
  • I want to offer in-app purchases (or a "freemium" model) so that users can use a "basic" version of my software before purchasing "pro" features.
  • I want to limit access of my software to a single machine so that customers have to purchase a new license for each additional machine.
  • I want to limit access of my software to only x number of machines so that a license cannot be used on unlimited machines.
  • I want to offer a limited supply of "special" licenses so that I can run a promotional campaign with my audience.
  • I want to license individual features of my software to users so that I can upsell premium features.
  • I want to require periodic "check-ins" for licenses to remain valid so that I can offer offline support but still require a periodic internet connection.
  • I want each license to have a usage limit so that I can offer plans based on usage of a particular feature.

Licensing models

You may create resources directly using Keygen's API, or you can use your account dashboard to do so instead. The policies below use default attributes where possible, so be sure to review the Policy resource's default attributes.

Table of Contents

  1. I want to limit access of my software to only licensed users
  2. I want to offer timed licenses to each of my users
  3. I want to offer a limited trial version of my software to unlicensed users
  4. I want to offer a premium version of my free software
  5. I want to limit access of my software to a single machine
  6. I want to limit access of my software to only x number of machines
  7. I want to offer a limited supply of "special" licenses
  8. I want to license individual features of my software to users
  9. I want to require periodic "check-in" for licenses to remain valid
  10. I want each license to have a usage limit

I want to limit access of my software to only licensed users

Keeping things simple. Below is a policy which implements the bare-minimum to implement a licensing system for your software. It will be valid on an unlimited number of machines.

Here's what that Policy would look like (with everything else left to defaults):

{
  data: {
    type: "policies",
    attributes: {
      name: "Pro License"
    }
  }
}

Licenses that implement this policy will never expire and will be valid on an unlimited number of machines. The only way for a license implementing this policy to fail validation is for it to be suspended or revoked.


I want to offer timed licenses to each of my users

To offer licenses which have an expiration date, we'll need to create a policy similar to the above one, but one which also has a value for the duration attribute.

And here's what that would look like (everything else left to defaults),

{
  data: {
    type: "policies",
    attributes: {
      name: "Pro License",
      duration: 1209600 // 2 weeks
    }
  }
}

The duration attribute is how long, in seconds, the license is valid for from the license's creation timestamp, e.g. a license is created at epoch 1503520001, so its expiration will be 1503520001 + 1209600, which is 09/06/2017 at 8:26pm UTC.

When a license's expiration gets close, we will fire off an license.expiring-soon webhook event so that you can notify your customer and even automate renewing the license server-side.


I want to offer a limited trial version of my software to unlicensed users

This is similar to the above implementation, but instead of only having a single policy, we'll have 2. The first one will be our "full" policy, while the second will be our "trial" policy.

Here's what those Policies would look like (with everything else left to defaults):

// Full license policy
{
  data: {
    type: "policies",
    attributes: {
      name: "Full License",
      duration: 31557600 // 1 year
    }
  }
}
// Trial license policy
{
  data: {
    type: "policies",
    attributes: {
      name: "Trial License",
      duration: 1209600 // 2 weeks
    }
  }
}

The only difference is the duration of each policy, i.e. the "trial" version will expire in 2 weeks, while the "full" version will be valid for 1 year.


I want to offer a premium version of my free software

This particular model is usually referred to as "freemium" or "in-app purchases", and it's one of the models that Keygen was originally built around. For reference, we even have an example application which implements a similar form of this type of licensing.

And here's a representation of the 2 policies (with other attributes left to defaults),

// Base license policy
{
  data: {
    type: "policies",
    attributes: {
      name: "Base License"
    }
  }
}
// Premium license policy
{
  data: {
    type: "policies",
    attributes: {
      name: "Premium License"
    }
  }
}

You can then store each policy's ID within your product to determine if the current user is allowed to use the "premium" features of your product. You can find a policy's ID by viewing the policy from your account dashboard.

View example implementation
const fetch = require("node-fetch")

// Policies representing our product editions. You can get this information
// from your dashboard: https://app.keygen.sh/policies
const KEYGEN_PREMIUM_POLICY = "f4eb56fa-cd4d-4060-b9fe-66fb70d875d9"
const KEYGEN_BASE_POLICY = "35900395-c0e6-4fda-b174-a440ef58dd12"
const PRIVILEGES = {
  premium: false,
  base: false
}

const licenses = [/* fetch user's licenses */]

for (let license of licenses) {
  const { id, relationships: { policy: { data: policy } } } = license

  switch (policy.id) {
    case KEYGEN_PREMIUM_POLICY: {
      const res = await fetch(`https://api.keygen.sh/v1/accounts/{ACCOUNT}/licenses/${id}/actions/validate`, {
        method: "POST",
        headers: {
          "Accept": "application/vnd.api+json",
          "Authorization": "Bearer {TOKEN}"
        }
      })

      const { meta, errors } = await res.json()
      if (errors) {
        continue
      }

      PRIVILEGES.premium = meta.valid

      break
    }
    case KEYGEN_BASE_POLICY: {
      const res = await fetch(`https://api.keygen.sh/v1/accounts/{ACCOUNT}/licenses/${id}/actions/validate`, {
        method: "POST",
        headers: {
          "Accept": "application/vnd.api+json",
          "Authorization": "Bearer {TOKEN}"
        }
      })

      const { meta, errors } = await res.json()
      if (errors) {
        continue
      }

      PRIVILEGES.base = meta.valid

      break
    }
  }
}

// … later in the product

if (PRIVILEGES.premium) {
  // … do something
}

I want to limit access of my software to a single machine

To limit access to a single machine (also called node-locked licensing), we'll take advantage of a few other policy attributes. Node-locked is a fancy way to say "I want a license to only be valid on a single machine" i.e. you "lock" the license to a particular machine resource.

Here's what that Policy would look like (with everything else left to defaults):

{
  data: {
    type: "policies",
    attributes: {
      name: "Node-Locked License",
      requireFingerprintScope: true, // All validations will require a machine fingerprint
      maxMachines: 1, // Licenses should only be associated with a single machine
      floating: false, // We do not want to allow more than 1 machine at a time
      concurrent: false, // We do not want them to be able to exceed our 1 machine limit
      strict: true // Invalidate licenses which do not meet machine requirements
    }
  }
}
For an in-depth guide and code examples on setting up machine activation, check out our documentation on machine activation. We also have an activation example on GitHub.

In order for a license to pass validation, it must be associated with a single machine—no more, no less. In addition, all validation requests must contain a machine fingerprint scope, which will be used to determine whether or not the machine is valid for the given license. Whenever a user goes over their single machine limit, their license will be invalidated until the additional machines are removed.


I want to limit access of my software to only x number of machines

Similar to implementing "node-locked" licenses, we can also implement "floating" licenses. Floating is a term used to describe the opposite of "node-locked" i.e. a "floating" license is valid across multiple machines, but only up to a maximum machine count.

Here's what that would look like,

{
  data: {
    type: "policies",
    attributes: {
      name: "Floating License",
      requireFingerprintScope: true, // All validations will require a machine fingerprint
      maxMachines: 5, // Licenses can be associated to a maximum of 5 machines
      floating: true, // We want to allow more than 1 machine at a time
      strict: true // Invalidate licenses which do not meet machine requirements
    }
  }
}

In order for a license to pass validation, it must be associated with at least 1 machine, but no more than 5 machines total. In addition, all validation requests must contain a machine fingerprint scope, which will be used to determine whether or not the machine is valid for the given license. Whenever a user goes over their machine limit, their license will be invalidated until the additional machines are removed.

For an in-depth guide and code examples on setting up machine activation, check out our documentation on machine activation. We also have an activation example on GitHub.

This type of policy/licensing model can be used to implement a "concurrent" or "per-seat" licensing system, e.g. where you only allow n machines to be active at any given time. If you do not want the user to be able to exceed the machine limit (even though it would still invaliate their license), you can set concurrent = false.


I want to offer a limited supply of "special" licenses

In order to implement a "limited supply" of licenses, we'll need to create a pooled policy. Before we start, let's go over a little bit of terminology:

  1. A pooled policy is a term used within Keygen to describe a policy which has a limited amount of pre-determined keys available for use. Once a policy's pool is depleted, you must add additional keys in order to continue to create new licenses.
  2. Keys are a special resource within Keygen that represent an unused key within a particular pooled policy's key "pool". Keys are not valid "license keys" until a license resource is created from the pooled policy. When a license is created which implements a pooled policy, a key is taken from the top of the pool and is used for the new license.

Below is a representation of a pooled policy,

{
  data: {
    type: "policies",
    attributes: {
      name: "Limited Edition License",
      usePool: true // Pull keys from the policy's pool when creating licenses
    }
  }
}

Now, if we tried to create a license which implements this policy right away, we would receive an error saying that our policy's pool is empty. So before we can allow license creation, we need to add keys to our policy's pool. Once a policy's pool is depleted, you must add additional keys in order to continue to create new licenses.


I want to license individual features of my software to users

This particular model is called "feature licensing", and it's one of the models that Keygen was originally built around. For reference, we even have an example application which implements feature licensing.

The feature policies would end up looking like,

// Feature "X"
{
  data: {
    type: "policies",
    attributes: {
      name: "Feature X"
    }
  }
}
// Feature "Y"
{
  data: {
    type: "policies",
    attributes: {
      name: "Feature Y"
    }
  }
}
// Feature "Z"
{
  data: {
    type: "policies",
    attributes: {
      name: "Feature Z"
    }
  }
}

You can then store each policy's ID within your product to determine if the current user is allowed to use each particular feature of your product. You can find a policy's ID by viewing the policy from your account dashboard.

View example implementation
const fetch = require("node-fetch")

// Policies representing our product's features. You can get this information
// from your dashboard: https://app.keygen.sh/policies
const KEYGEN_FEATURE_X = "aac4905c-84d0-41a3-af6e-1026e28c04d3"
const KEYGEN_FEATURE_Y = "dd025847-42fb-49b0-b898-80c34d7734b4"
const KEYGEN_FEATURE_Z = "b6a5ae11-ec60-4ecd-9902-ae48a1077623"
const ALLOWED_FEATURES = {
  [KEYGEN_FEATURE_X]: false,
  [KEYGEN_FEATURE_Y]: false,
  [KEYGEN_FEATURE_Z]: false
}

const licenses = [/* fetch user's licenses */]

for (let license of licenses) {
  const { id, relationships: { policy: { data: policy } } } = license

  switch (policy.id) {
    case KEYGEN_FEATURE_X:
    case KEYGEN_FEATURE_Y:
    case KEYGEN_FEATURE_Z: {
      // Validate the current license
      const res = await fetch(`https://api.keygen.sh/v1/accounts/{ACCOUNT}/licenses/${id}/actions/validate`, {
        method: "POST",
        headers: {
          "Accept": "application/vnd.api+json",
          "Authorization": "Bearer {TOKEN}"
        }
      })

      const { meta, errors } = await res.json()
      if (errors) {
        continue
      }

      ALLOWED_FEATURES[policy.id] = meta.valid

      break
    }
    default: {
      // This version of our product doesn't use this policy so it's okay to skip
      // it (so we can implement new features without breaking old versions)
      break
    }
  }
}

// … later in the product

if (ALLOWED_FEATURES[KEYGEN_FEATURE_X]) {
  // … do something
}

I want to require periodic "check-in" for licenses to remain valid

You can require licenses to periodically "check-in" using the check-in action. Below is a policy which requires licenses to check in every 2 weeks in order to remain valid. Check-in policies are great for allowing offline usage of your product, but still requiring a periodic "check-in" to validate licenses e.g. to ensure they aren't expired or suspended.

Here's that policy:

{
  data: {
    type: "policies",
    attributes: {
      name: "Check-In License",
      requireCheckIn: true, // Require periodic check-ins
      checkInInterval: "week", // Can be: "day", "week", "month" or "year"
      checkInIntervalCount: 2 // How many intervals e.g. every 2 "weeks"
    }
  }
}

When a license's check-in gets close, we will fire off a license.check-in-required-soon webhook event so that you can notify your customer to let them know that they need to connect to the internet soon and validate their license.


I want each license to have a usage limit

You can set up license policies to enforce a usage limit by utilizing the maxUses attribute, along with utilizing a license's usage-related actions, such as increment-usage and decrement-usage.

Here's what that policy would look like,

{
  data: {
    type: "policies",
    attributes: {
      name: "Usage License",
      maxUses: 25
    }
  }
}

Then you would utilize it by incrementing the license's uses attribute through its increment-usage action when usage occurs (you choose when to increment usage),

View example implementation
const fetch = require("node-fetch")

const res = await fetch("https://api.keygen.sh/v1/accounts/{ACCOUNT}/licenses/b18e3f3a-330c-4d8d-ae2e-014db21fa827/actions/increment-usage", {
  method: "POST",
  headers: {
    "Accept": "application/vnd.api+json",
    "Authorization": "Bearer {TOKEN}"
  }
})

const { data, errors } = await res.json()
if (errors) {
  if (res.status === 422) {
    // … handle case where they've reached their usage limit
  }

  return
}

// … allow usage

You can reset a license's usage count from your admin dashboard, or via the licensing API.


Don't see the licensing model you're looking for? Reach out and let us know, and we'll get it added here plus answer any questions you have about implementation.