---
url: https://docs.sunlicense.com/how-to/jda-bot.md
description: >-
  How to add license keys to a JDA (Java Discord API) bot you sell - check the
  buyer's key with the SunLicense Java SDK before building JDA, exit on a
  rejection, keep running offline, and re-check so a revoked key stops running
  bots.
---

# How to add license keys to a JDA bot

**Short answer:** add the SunLicense Java SDK, read the buyer's key from an environment variable or
`config.properties`, check it before `JDABuilder...build()`, and exit when SunLicense says no. A network error is not
a no: start anyway and check again later.

```java
// build.gradle.kts: implementation("com.sunlicense:sunlicense:0.0.1") next to JDA, in one shadow jar
public static void main(String[] args) {
    SunLicense client = new SunLicense.Builder()
            .baseUrl("https://api.sunlicense.com").token(API_KEY).build();   // API key from your build, never git
    try {
        SunLicense.Result r = client.validate(System.getenv("LICENSE_KEY"), Map.of("host", machineId()));
        if (r.getStatus() == 400) {   // EXPIRED, REVOKED, TOO_MANY_MACHINES…
            System.err.println("License rejected: " + r.getBody());
            System.exit(1);
        }
    } catch (SunLicense.SunLicenseException e) {
        System.err.println("SunLicense unreachable, starting anyway: " + e.getMessage());
    }
    JDA jda = JDABuilder.createLight(System.getenv("DISCORD_TOKEN")).build();
}
```

**Start from the template instead:** [example-discord-bot-jda](https://github.com/sunlicense-com/example-discord-bot-jda)
(Java 21, JDA 6) has this plus a stable machine id, masked keys, re-checks every 6 hours, a signed offline license
fallback, a sealed `core` package, a `/license` command, tests and CI.

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

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

## Step by step

1. **In the dashboard:** a product, a policy (*1 machine* = one running copy per license) and an **API key**, which
   can only validate licenses.
2. **One runnable jar.** Add the SDK with `implementation` and build with Shadow; relocating it isn't needed in a bot
   that runs alone, but does no harm.
3. **Keep the API key out of git:** put it in `~/.gradle/gradle.properties` and expand it into a resource at build time.
   The buyer's `DISCORD_TOKEN` and `LICENSE_KEY` come from their environment or `config.properties`.
4. **Machine id** as `host`: a hash of OS, architecture and host name (snippet in the [Paper guide](/how-to/paper-plugin#step-by-step)).
   In Docker, set a fixed `hostname:` or the container id changes on every restart.
5. **Results:** 200 builds JDA, 400 exits with the [reason](/guide/concepts#validation-result-codes) (keys masked in
   logs), anything else starts and retries in about 10 minutes, or uses an [offline license](/guide/offline-licenses).
6. **Re-check every few hours** on a `ScheduledExecutorService`; on a 400 call `jda.shutdown()` and exit.

## Selling it

[Tebex](/integrations/tebex), [Gumroad](/integrations/gumroad), [Lemon Squeezy](/integrations/lemon-squeezy),
[Stripe](/integrations/stripe), [BuiltByBit](/integrations/builtbybit) and the [other stores](/integrations/) issue a
key per purchase and revoke it on a refund. A jar downloaded from BuiltByBit or Voxel Shop can carry the key through a
`%%__SUNLICENSE__%%` placeholder.

## Going further

* [Protection](/guide/protection) and [obfuscation](/guide/obfuscation) for the jar.
* [discord.js](/how-to/discord-js-bot) and [discord.py](/how-to/discord-py-bot) versions of this guide. More on
  [licensing Discord bots](https://sunlicense.com/for/discord-bots/).
