Skip to content

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 (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 into Claude Code, Cursor or Codex in the bot's folder.

Free plan: 3 products, 100 licenses, no credit card. Create a free account.

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.

  4. Results: 200 logs in, 400 exits with the reason (mask the key in logs: AX8P-XXXX-XXXX-XXXX), anything else starts and retries in about 10 minutes, or uses an offline license.

  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, Gumroad, Lemon Squeezy, Stripe or BuiltByBit: SunLicense issues a key per purchase and revokes it on a refund. The SunLicense Discord bot can hand buyers their key and a customer role in your own support server.

Going further ​

Send feedback

Found something broken, or have an idea? It goes straight to our board.

The first line becomes the title.
Images (optional, up to 5: paste, drop or capture)
Check each image before sending, and remove any that show something private.

We also receive this page's address and your browser version.

Ctrl Enter to send