For partner servers

Webhooks: your server hears about every player from a creator code

A player signs up for NLauncher with your code or link — we instantly send a POST to your server. Once they’ve played enough, we send another. Your server hands out the bonus itself: spawn currency, a kit, a rank.

Set up in 5 minutes

  1. Creator dashboard → “Server integration (webhooks)”. No dashboard yet? Apply — an admin grants creator status.
  2. Webhook URL — an address on your server, https:// only, e.g. https://myserver.com/api/nlauncher/ref. All your codes go to one address; which code was used is in ref_code.
  3. Secret key — “Copy” it and put it on your server (as an environment variable). You use it to check the signature of every request.
  4. “Send test request” — the dashboard instantly shows your server’s response code and body. Below is a log of the last 50 requests with a “Retry” button.

Events

referral.registered

An account signed up with a creator’s code or link (or entered the code within 7 days of signing up). status: pending — waiting for them to play, review — we’re checking it.

referral.qualified

The player is really playing: they’ve hit the threshold (usually 2 hours over 3 different days). It’s the same moment the creator’s NLcoin reward is booked. The best time for a bonus. hold_until — when our fake-account check ends.

referral.revoked

The player no longer counts: ban, admin decision, duplicate. reason: banned, admin, duplicate. You can take the bonus back. Only sent if you already got an event about this player.

test

The “Send test request” button: "test": true, a made-up player — don’t give anything.

Request

POST <url>
Content-Type: application/json
User-Agent: NLauncher-Webhooks/1
X-NL-Signature: sha256=<hex HMAC-SHA256(secret, raw body)>
X-NL-Event: referral.qualified
X-NL-Delivery: 5f0c7a52-3c1e-4c1f-9b8e-2a6d1e4b7c90   (= event_id, same for retries)
X-NL-Attempt: 1
X-NL-Timestamp: 1790683201                             (unix, = sent_at in the body)

{
  "event": "referral.qualified",
  "event_id": "5f0c7a52-3c1e-4c1f-9b8e-2a6d1e4b7c90",
  "ref_code": "MYSERVER",
  "nickname": "Steve",
  "user_id": "3f2b9c1e8a7d4e6f9b0c1d2e3f4a5b6c",
  "minecraft_uuid": "3f2b9c1e-8a7d-4e6f-9b0c-1d2e3f4a5b6c",
  "created_at": "2026-09-29T12:00:00Z",
  "status": "qualified",
  "hold_until": "2026-10-06T12:00:00Z",
  "attempt": 1,
  "sent_at": "2026-09-29T12:00:01Z"
}
event_idUUID, the same for all retries of one event — use it to drop duplicates.
ref_codeThe creator code the player came with.
nicknameThe player’s Minecraft nickname (NLauncher account).
user_idPermanent NLauncher account id — the profile UUID without dashes.
minecraft_uuidThe same UUID with dashes — how a server with NLauncher login sees the player. On an offline-mode server the UUID differs — match by nickname.
created_atWhen the event happened, RFC 3339, UTC. attempt and sent_at — of this attempt.
X-NL-Signaturesha256= + hex HMAC-SHA256 of the raw body; the key is the secret as a string.
X-NL-TimestampUnix send time, the same as sent_at inside the signed body. Drop requests older than 10 minutes.

New fields may be added — don’t fail on unknown ones.

Signature check

Compute the HMAC over the raw body bytes (before parsing JSON) and compare in constant time.

const crypto = require('crypto');
const express = require('express');
const app = express();
const SECRET = process.env.NL_WEBHOOK_SECRET;

app.post('/api/nlauncher/ref', express.raw({ type: 'application/json' }), (req, res) => {
  const got = req.get('X-NL-Signature') || '';
  const want = 'sha256=' + crypto.createHmac('sha256', SECRET).update(req.body).digest('hex');
  if (got.length !== want.length || !crypto.timingSafeEqual(Buffer.from(got), Buffer.from(want))) {
    return res.status(401).send('bad signature');
  }
  const ev = JSON.parse(req.body);
  if (alreadySeen(ev.event_id)) return res.sendStatus(200);
  if (ev.event === 'referral.qualified') giveBonus(ev.nickname, ev.minecraft_uuid);
  if (ev.event === 'referral.revoked') takeBonus(ev.nickname);
  res.sendStatus(200);
});

Test your implementation: the secret test_secret and the body {"event":"test","event_id":"00000000-0000-4000-8000-000000000000"} give sha256=85e7ea9606714a7ed6ba43a628cdd42854e4c5a5686eed34974dc2f155ee7211.

Response and retries

Privacy

A partner only receives the player’s nickname and id — to give the bonus. We never share email, IP, device or any other data. Players see this condition when they enter a creator code.

Ready to connect?The URL, secret and test request are in the creator dashboard.
Open dashboard Become a creator