All posts
Security & Licensing/August 3, 2026

How Software Licensing Works: A Practical Guide for SaaS and Plugin Businesses

A license is a verifiable record, not a serial number. Here is how key generation, activation limits, and API validation actually work under the hood.

How Software Licensing Works: A Practical Guide for SaaS and Plugin Businesses

Most conversations about software licensing start in the wrong place. People ask "what format should my license key be?" when the format barely matters. The question that actually determines whether your licensing system works is: what is a license, structurally, and where does the decision about whether it is valid actually get made? Get that right and the rest — key formats, activation limits, domain locks — falls into place. Get it wrong and you end up with a system that looks secure but can be defeated by editing a single file.

A License Is a Record, Not a String

The most common misconception, especially among developers building their first commercial plugin or SaaS product, is that a license is the key itself — a string of characters like ABCD-1234-EFGH-5678 that gets typed into an activation field. It isn't. The key is just a lookup token. The actual license is a row in a database on a server you control, and that row is what carries the meaning:

  • Which customer or account owns it
  • Which product and which plan or tier (Free, Monthly, Yearly, Lifetime, and so on)
  • Its current status (active, expired, suspended, refunded, cancelled)
  • How many activations or seats it's allowed
  • Which sites, domains, or machines are currently activated against it
  • When it expires, if it expires at all

Once you think of a license this way, a lot of design decisions become obvious. The key itself can be short, long, human-readable, or a UUID — it genuinely does not matter, because it is never the thing being trusted. It is only ever used to look up the record that is actually trusted. Businesses that treat the key as the source of truth end up baking validation logic into the client, which is the single biggest mistake in this space and the subject of the next section.

How a Key Gets Generated After Purchase

In a typical flow, a purchase event — a completed Stripe checkout, a PayPal subscription activation, a WooCommerce order transitioning to "completed" — fires a webhook back to your licensing system. That webhook handler is where the license record is actually born. A reasonable sequence looks like this:

  1. The payment provider confirms the charge and sends a webhook with the order and customer details.
  2. Your backend verifies the webhook is authentic (more on this in our piece on securing a SaaS API), then creates or updates a customer record.
  3. A license record is created, linked to that customer, the specific product, and the plan purchased.
  4. A key is generated — often just a hashed or randomly generated string associated with the record's primary key — and stored alongside the record.
  5. The key is delivered to the customer, typically by email and in an account dashboard, along with activation instructions.

Notice that nothing about the software itself is involved in this process yet. Licensing is a backend concern that happens independently of whether the customer has even downloaded the product. This separation is what makes it possible to handle refunds, chargebacks, plan upgrades, and renewals cleanly — you are always editing one record in one place, not trying to push a change out to code that already shipped.

Activation, Deactivation, and Seat Limits

Purchasing a license and activating it are two different events, and conflating them is another common source of bugs. Activation happens when the software itself — a WordPress plugin, a desktop app, a mobile client — calls home with the license key and an identifier for where it's being installed (a domain, a device fingerprint, a machine ID). The server checks three things: does this key exist, is it currently valid, and is there room left under its activation limit.

Seat or activation limits are simply a count of how many "places" a license is allowed to be active at once, and they are typically tied to the plan:

  • A Free plan might allow a single activation with reduced features
  • A Monthly or Yearly plan might allow one to five site activations
  • A Lifetime or Agency plan might allow unlimited or a much higher cap

Each successful activation creates its own record — not just a counter increment — because you need to know which specific sites are using the license, not just how many. That granularity is what lets a customer see "activated on example.com and staging.example.com" in their dashboard and deactivate one without affecting the other. Deactivation, correctly implemented, is just the reverse: the software (or the customer, from their dashboard) tells the server to release that specific activation record, freeing up a seat for reuse elsewhere.

Why Validating Through an API Call Beats a Hardcoded Check

Here is the part that separates licensing systems that hold up from ones that don't. If your software checks the license by running logic that ships inside the product itself — say, a function that validates a checksum or compares a string pattern locally — then every user of that software has a full copy of the exact logic deciding whether they're allowed to use it. That logic can be read, understood, and patched out. It doesn't matter how cleverly it's written; if it runs entirely on hardware you don't control, using code the end user has full access to, it is not actually enforcing anything against a motivated user. It mostly just deters casual copying.

Validating through an API call to a server you control changes the trust boundary entirely. The software sends the key and some context; your server, which the end user cannot inspect or modify, makes the actual decision and returns a signed or simple pass/fail response. This has several concrete advantages beyond the obvious security one:

  • Real-time revocation. A refunded or charged-back license stops working immediately, without shipping a software update.
  • Live plan changes. An upgrade from Monthly to Yearly, or a downgrade, takes effect on the next check without any client-side code change.
  • Usage visibility. You can see which licenses are active, which have gone silent, and which are being hammered with suspicious activation attempts.
  • Business-rule flexibility. You can change how licensing works — grace periods, new tiers, promotional extensions — on the server without touching the shipped product at all.

This doesn't mean local checks are useless — caching a recent "valid" response locally so the software still works for a reasonable grace period during a network outage is good practice. The key distinction is that the decision is made server-side, and the client only ever holds a short-lived, revocable answer to that decision, not the logic itself.

Online vs Offline Activation

Most SaaS products and WordPress plugins use online activation exclusively: the software has internet access, so it can call your licensing API directly, either at activation time, on a periodic heartbeat, or both. This is simpler to build and gives you the real-time benefits described above.

Offline activation exists for a real reason, not as an afterthought: some environments — air-gapped enterprise networks, certain on-premise deployments, some desktop software used in secure facilities — genuinely cannot reach the internet. The typical pattern there is a manual exchange: the customer generates a machine fingerprint or request file, submits it (through a separate channel) to your server, and receives back a signed license file they install manually. That file contains the same information an online check would return, just delivered out of band and usually valid for a fixed period before it needs to be refreshed the same way. If you are building a product primarily for WordPress or typical SaaS use, you can usually skip offline activation entirely and only add it if enterprise customers specifically require it — it adds real complexity for a narrow audience.

Domain and Machine Locking

Activation limits control how many places a license can run; domain or machine locking controls which specific places. The right fingerprint depends entirely on what you're building:

  • WordPress plugins and themes typically lock to the site URL/domain, since that's the natural unit of "one install."
  • Desktop software typically hashes stable hardware identifiers into a machine ID.
  • SaaS APIs typically don't lock to a location at all — the "activation" is the account or the API token itself, and access control happens through that token's scope rather than a fingerprint.

A practical detail worth planning for up front: legitimate customers change domains, migrate servers, and replace hardware constantly. A licensing system that treats every fingerprint change as a violation will generate more support tickets than it prevents abuse. Most well-run systems allow a small number of self-service domain or machine changes per period, and route anything beyond that to a support conversation rather than a hard block.

Handling Expiry, Renewals, and Grace Periods

A license record isn't static once created — most of the interesting engineering work happens around what changes it over time. Subscription-based plans (Monthly, Yearly) need an expiry date that advances automatically on successful renewal billing and, just as importantly, a defined behavior when a renewal payment fails. Hard-cutting access the instant a card declines is unnecessarily harsh given how often declines are transient — an expired card, a temporary bank hold, a false fraud flag. A short grace period, during which the license still validates but the customer sees a clear warning to update their payment method, tends to recover far more renewals than an immediate hard lock, without meaningfully weakening enforcement.

Lifetime licenses remove the expiry question but raise a different one: what exactly is "lifetime" scoped to? Most businesses that offer it mean lifetime access to the current major version plus updates within it, not an unconditional promise to support and update the product forever regardless of how the underlying platform evolves. Whatever the answer is, it needs to be encoded explicitly in the license record — as a version ceiling, an update-window end date, or a flag — rather than left as an assumption that only gets resolved the first time a customer asks a support agent.

Why This Matters at Any Scale

A solo developer selling a single WordPress plugin needs licensing for a narrower reason than a SaaS company managing thousands of subscriptions, but the underlying mechanics are identical. For the solo seller, the goal is usually just to stop the most casual, unintentional sharing and to have a clean way to gate updates and support to paying customers — we cover that trade-off in detail in our piece on preventing piracy. For a SaaS company, the same license record becomes the backbone of billing reconciliation, dunning logic, renewal reminders, and usage-based feature gating, tied directly into whichever payment processor is handling recurring billing.

Either way, the discipline is the same: keep the license as a server-side record, keep the enforcement decision on your server, and treat the key itself as nothing more than a lookup token. If you're evaluating how to license a product you're building — whether that's a WordPress plugin, a desktop app, or a full SaaS platform — it's worth designing this piece before writing the product itself, since retrofitting licensing onto software that was never built to check in with a server is considerably harder than building it in from day one. We build licensing systems like this as part of our SaaS and plugin development work; take a look at our products for examples of it running in production, or get in touch if you're planning something similar.

Share