---
url: https://docs.sunlicense.com/how-to/discord-js-bot.md
description: >-
  How to add license keys to a discord.js bot you sell - check the buyer's key
  with the SunLicense JavaScript SDK before logging in, exit on a rejection,
  keep running offline, and re-check so a revoked key stops running bots.
---

# How to add license keys to a discord.js bot

**Short answer:** `npm install sunlicense`, read the buyer's key from `.env`, check it before `client.login()`, and exit
when SunLicense says no. A network error is not a no: start anyway and check again later.

```js
import { SunLicense } from 'sunlicense';
import { Client, GatewayIntentBits } from 'discord.js';

const key = process.env.LICENSE_KEY;
if (!key) { console.error('Put your license key in .env (LICENSE_KEY).'); process.exit(1); }
const sunlicense = new SunLicense({ baseUrl: 'https://api.sunlicense.com', token: API_KEY }); // API key from your build
try {
  const r = await sunlicense.validate(key, { host: machineId() });
  if (r.status === 400) {               // EXPIRED, REVOKED, TOO_MANY_MACHINES…
    console.error(`License rejected: ${r.data?.code}`);
    process.exit(1);
  }
} catch (e) {
  console.warn('SunLicense unreachable, starting anyway:', e.message);
}
const bot = new Client({ intents: [GatewayIntentBits.Guilds] });
await bot.login(process.env.DISCORD_TOKEN);
```

`validate` resolves to `{ valid, status, data }`; it throws only when SunLicense can't be reached.

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

**Or let your AI agent do it:** paste the [setup prompt](https://sunlicense.com/prompt) into Claude Code, Cursor or
Codex in the bot's folder.

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

## Step by step

1. **In the dashboard:** a product for the bot, a policy (*1 machine* means one running copy per license) and an
   **API key**, which can only validate licenses.
2. **Two secrets, two places.** The buyer's `DISCORD_TOKEN` and `LICENSE_KEY` go in their `.env`. Your API key
   belongs to the build you hand out, not to their `.env`: the template's `npm run build` writes it into a generated
   settings file from `SUNLICENSE_API_KEY`. Never commit either.
3. **Machine id:** hash something stable about the host and send it as `host`, so restarts count as the same machine:

   ```js
   import { createHash } from 'node:crypto';
   import os from 'node:os';
   const machineId = () => createHash('sha256').update(`${os.platform()}|${os.arch()}|${os.hostname()}`).digest('hex');
   ```

   In Docker the host name changes with the container; set `hostname:` in the buyer's compose file, or store a random
   id in a mounted volume. [Choosing a machine id](/guide/sdks#choosing-a-machine-id).
4. **Results:** 200 logs in, 400 exits with the [reason](/guide/concepts#validation-result-codes) (mask the key in
   logs: `AX8P-XXXX-XXXX-XXXX`), anything else starts and retries in about 10 minutes, or uses an
   [offline license](/guide/offline-licenses).
5. **Re-check while it runs** (`setInterval` every 6 hours, `.unref()` it) and call `bot.destroy()` and exit on a 400,
   so revoking a leaked key reaches bots that are already running.

## Selling it

Bots sold to server owners usually go through [Tebex](/integrations/tebex), [Gumroad](/integrations/gumroad),
[Lemon Squeezy](/integrations/lemon-squeezy), [Stripe](/integrations/stripe) or [BuiltByBit](/integrations/builtbybit):
SunLicense issues a key per purchase and revokes it on a refund. The [SunLicense Discord bot](/guide/discord) can hand
buyers their key and a customer role in your own support server.

## Going further

* [Protection](/guide/protection): the template seals `src/core.js` so a copy with the check deleted has nothing to run.
* [Obfuscation](/guide/obfuscation) for the JavaScript you ship.
* [JDA](/how-to/jda-bot) and [discord.py](/how-to/discord-py-bot) versions of this guide. More on
  [licensing Discord bots](https://sunlicense.com/for/discord-bots/).
