Start your bot
In about ten minutes you will have a bot that answers a slash command in your own test server. You need a Zrzur account, a server of your own and Node.js 22 or newer. There is nothing to install.
1. Create an application
In the Developer Portal, choose New application, give it a name and choose Create. The application comes with a bot account. Open My applications
2. Copy the token
The token appears once, right after you create the application. Copy it now. Whoever has the token controls the bot.
3. Install the bot in a test server
On the Overview tab of your application choose Add to a server, and pick a server where you have Manage Server. More about installing
Do this before the first run. A bot that is not in the server gets 403 MissingPermission when it writes to one of its channels, which looks like a missing permission. Is the bot in that server?
4. No library needed
Node.js 22 has everything built in: the API is plain HTTP and WebSocket. The official library, @zrzur/sdk, cannot be downloaded yet. About the SDKs
5. Write your first bot
Save this as hello-raw.mjs in an empty folder. It registers /hello and answers it.
// Hello World without any library: Node 22+, fetch and WebSocket only.
//
// ZRZUR_BOT_TOKEN=YOUR_BOT_TOKEN node hello-raw.mjs
const token = process.env.ZRZUR_BOT_TOKEN;
const api = process.env.ZRZUR_API_URL ?? "https://api.zrzur.com";
const ws = process.env.ZRZUR_WS_URL ?? "wss://events.zrzur.com";
if (!token) {
console.error("Set ZRZUR_BOT_TOKEN first.");
process.exit(2);
}
/** An interaction's token is in its path: it must never reach a log */
const masked = (path) =>
path.replace(/(\/interactions\/[^/]+\/)[^/]+/, "$1***");
async function rest(method, path, body) {
const response = await fetch(api + path, {
method,
headers: {
"x-bot-token": token,
...(body ? { "content-type": "application/json" } : {}),
},
body: body ? JSON.stringify(body) : undefined,
});
if (!response.ok)
throw new Error(
`${method} ${masked(path)}: ${response.status} ${await response.text()}`,
);
return response.status === 204 ? null : response.json();
}
// 1. Who am I? A bot's application id is its user id.
const me = await rest("GET", "/users/@me");
// 2. Register the global /hello command (PUT replaces the whole list).
await rest("PUT", `/applications/${me._id}/commands`, [
{ name: "hello", description: "Say hello" },
]);
// 3. Connect to the gateway and answer interactions.
const socket = new WebSocket(`${ws}/?version=1&format=json`);
// intents 33 = SERVERS (1) + INTERACTIONS (32): commands, nothing else
socket.addEventListener("open", () =>
socket.send(JSON.stringify({ type: "Authenticate", token, intents: 33 })),
);
async function handle(event) {
if (event.type === "Ready") console.log(`ready as ${me.username}`);
if (
event.type === "InteractionCreate" &&
event.interaction.command?.name === "hello"
) {
const { _id, token: interactionToken } = event.interaction;
// answer within 3 seconds
await rest("POST", `/interactions/${_id}/${interactionToken}/callback`, {
type: "Message",
data: { content: "Hello from Zrzur!" },
});
}
}
socket.addEventListener("message", ({ data }) => {
const event = JSON.parse(data);
// several events can arrive together in one Bulk packet
for (const item of event.type === "Bulk" ? event.v : [event])
handle(item).catch(console.error);
});
// keep the connection alive
setInterval(
() => socket.send(JSON.stringify({ type: "Ping", data: Date.now() })),
20_000,
);
6. Run it
Set the token, then start the bot:
mkdir my-bot
cd my-bot
# save hello-raw.mjs here, then:
export ZRZUR_BOT_TOKEN=YOUR_BOT_TOKEN
node hello-raw.mjsWhen the terminal shows ready, type /hello in a channel of your test server. The bot answers Hello from Zrzur!
7. Receive events
The bot keeps a WebSocket connection to the gateway. Commands, button clicks, new messages and member changes arrive there as events. Gateway
8. Build a real bot
This bot stops when the line drops and knows only one command. The complete bot has commands, buttons, a form, moderation, saved data, error handling and reconnects, in one file. Build a complete bot