---
url: https://docs.sunlicense.com/how-to/fabric-mod.md
description: >-
  How to add license keys to a Fabric mod (Minecraft servers) - check the
  buyer's key with the SunLicense Java SDK in a DedicatedServerModInitializer,
  off the server thread, stop on a rejection, keep running offline, and sell it
  on BuiltByBit or Voxel Shop.
---

# How to add license keys to a Fabric mod

**Short answer:** add the SunLicense Java SDK (included in your jar), read the buyer's key from a file in
`config/`, check it on a background thread from your `DedicatedServerModInitializer`, and switch your features off
when SunLicense says no. A network error is not a no: keep running and check again later.

```java
// build.gradle: implementation + include "com.sunlicense:sunlicense:0.0.1"
public final class MyMod implements DedicatedServerModInitializer {
    private static final SunLicense CLIENT = new SunLicense.Builder()
            .baseUrl("https://api.sunlicense.com").token("slk_…").build();   // API key from your build, never git
    public static volatile boolean licensed;

    @Override
    public void onInitializeServer() {
        String key = readKey(FabricLoader.getInstance().getConfigDir().resolve("mymod.properties"));
        Thread.ofVirtual().start(() -> {
            try {
                SunLicense.Result r = CLIENT.validate(key, Map.of("host", machineId()));
                licensed = r.isValid() || r.getStatus() != 400;   // only a 400 is SunLicense saying no
                if (r.getStatus() == 400) LOGGER.error("MyMod license rejected: {}", r.getBody());
            } catch (SunLicense.SunLicenseException e) {
                licensed = true;   // unreachable: keep running, check again later
            }
        });
    }
}
```

Every feature starts with `if (!MyMod.licensed) return;`. `readKey` reads `license-key` from the properties file
and `machineId()` is the one from the [Paper guide](/how-to/paper-plugin#step-by-step).

**Start from the template instead:** [example-fabric-mod](https://github.com/sunlicense-com/example-fabric-mod)
(Fabric 26.3, Java 25, Loom) has this plus a stable machine id, masked keys, re-checks, a signed offline license
fallback, BuiltByBit placeholders, a sealed core, tests and CI. [All templates](/guide/templates).

**Or let your AI agent do it:** paste the [setup prompt](https://sunlicense.com/prompt) into your coding agent in the
mod's folder.

Free plan: 3 products, 100 licenses, no credit card. [Create a free account](https://sunlicense.com/start?ref=docs-howto-fabric).

## Step by step

1. **In the dashboard:** a product for your mod, a policy (for example *2 machines*: a machine is a server) and an
   **API key**, which can only validate licenses.

2. **Add the SDK to the jar.** Fabric has no shading by default; Loom's `include` nests the SDK jar in yours:

   ```groovy
   dependencies {
       implementation "com.sunlicense:sunlicense:0.0.1"
       include "com.sunlicense:sunlicense:0.0.1"
   }
   ```

3. **Keep the API key out of git:** put it in `~/.gradle/gradle.properties` and expand it into a resource at build
   time (`processResources`), like the template's `sunlicense.properties`.

4. **The buyer's key** goes in `config/mymod.properties` (`license-key=`), or comes from a `%%__SUNLICENSE__%%`
   placeholder that [BuiltByBit](/integrations/builtbybit) and [Voxel Shop](/integrations/voxel-shop) fill in on download.

5. **Server side only.** Use `DedicatedServerModInitializer` (the `server` entrypoint in `fabric.mod.json`) so the
   check runs on the server that holds the license, not on every player's client.

6. **Never block the server thread.** Check on a background thread; act on the result on the server thread
   (`server.execute(...)`) if you need to touch the game.

7. **Machine id:** a hash of OS, architecture and host name, sent as `host` (the [Paper guide](/how-to/paper-plugin#step-by-step)
   has the snippet; [why it matters](/guide/sdks#choosing-a-machine-id)).

8. **Results:** 200 starts your features, 400 stops them with the [reason](/guide/concepts#validation-result-codes)
   (keys masked in logs), anything else keeps running and retries in about 10 minutes, or uses an
   [offline license](/guide/offline-licenses). Re-check every few hours (`ServerTickEvents` or a scheduled executor)
   so a revoked key reaches running servers.

## Selling it

Mods sold on BuiltByBit or Voxel Shop get the buyer's key written into the jar by the placeholder. Anywhere else
([Tebex](/integrations/tebex), [Gumroad](/integrations/gumroad), [Payhip](/integrations/payhip), [other stores](/integrations/)),
SunLicense issues a key per purchase and the buyer pastes it into the config file.

A license check belongs in mods you sell for servers. Free mods on Modrinth or CurseForge are free downloads:
don't lock them; if you want install numbers for those, use [product analytics](/guide/product-analytics) without a license.

## Going further

* [Protection](/guide/protection): a sealed core and tamper reports. [Obfuscation](/guide/obfuscation) for the jar.
* [Paper](/how-to/paper-plugin) and [Velocity](/how-to/velocity-plugin) versions of this guide.
  More on [licensing Minecraft mods](https://sunlicense.com/for/minecraft-mods/).
