NxCreateDocs

How It Works

Understand how NxCreator runs your bot code, what tools are available, and how to set up automated tasks.

This page explains what happens behind the scenes when you write and save bot code on NxCreator — and how to use the built-in tools that come with every bot.

What is NxCreator doing?

When you create a bot on NxCreator, the platform takes care of everything you would normally have to set up yourself: renting a server, installing software, keeping the bot process running 24/7, and connecting to Telegram. You just write the logic — what should happen when a user sends a message — and NxCreator handles the rest.

Think of it like this: NxCreator is the kitchen, and your code is the recipe. You write the recipe, and the kitchen handles the oven, the electricity, and making sure everything stays hot.

How your code runs

Each bot runs in its own private environment, completely separate from other bots on the platform. Your code sections are combined into one unified bot program, and the platform connects that program to Telegram on your behalf.

NxCreator uses a popular bot library called Telegraf under the hood. You do not need to set it up or configure it — it is already running. Your job is simply to tell it what to do by registering handlers on the pre-existing bot object. Never create your own bot instance or import Telegraf yourself.

Saving and testing

When you hit save in the editor, NxCreator immediately applies your changes to the live running bot — no deployment steps, no waiting. Just save, switch to Telegram, and test your change. If something is wrong, fix it and save again.

This tight loop — write, save, test in Telegram — is one of the biggest advantages of using NxCreator over self-hosted bots.

Built-in tools

NxCreator provides a set of ready-to-use tools inside every bot. These are called globals — they are available automatically without needing to install or import anything. Just use them by name.

Do not use require(...) to load packages. If you need something, check the table below — it is almost certainly already available.

What is available

What you want to doGlobal to useNotes
Respond to messages and commandsbot`, `Telegraf`, `Scenes`, `MarkupThe main bot object is already set up. Register handlers directly on `bot`.
Use the Grammy bot framework insteadGrammyBot`, `Grammy`, `InlineKeyboard`, `Keyboard`, `grammySessionOnly available if your bot is configured to use the Grammy framework.
Use node-telegram-bot-api insteadTelegramBotOnly available if your bot is configured to use node-telegram-bot-api.
Make HTTP requests to external APIsaxiosA standard HTTP client. Use this to fetch data from any web API.
Store and retrieve datadbA built-in database helper. No configuration needed.
Run tasks on a schedulecronFor repeating tasks. See the Scheduled Tasks section below for details.
Send emailsnodemailerAvailable with some restrictions on file attachments.
Generate text-to-speech audio URLsgoogleTTSUseful for voice bots or audio responses.
Draw images or graphicscreateCanvas`, `loadImageImage creation with size limits. File and URL-based image loading is restricted.
Resize or process imagessharpImage transformation with limits. Saving files directly is not allowed.
Generate unique IDsuuidUseful for creating unique keys or identifiers.
Hash data or generate tokenscryptoStandard cryptographic utilities.

Calling external APIs

Your bot can reach out to the internet and fetch data from any external service. Use the built-in axios global to make these requests. This is how you would connect your bot to a weather service, a payment API, a news feed, or anything else.

bot.command('news', async (ctx) => {
  try {
    const response = await axios.get('https://api.example.com/news');
    await ctx.reply('Latest news: ' + response.data.headline);
  } catch (err) {
    console.error('API request failed:', err);
    await ctx.reply('Could not fetch the news right now. Please try again later.');
  }
});

The try/catch block is important here — external APIs can fail or go down, and you want your bot to handle that gracefully instead of crashing.

Delays and repeating actions

Sometimes you want your bot to do something after a delay, or on a repeating schedule. JavaScript provides two built-in tools for this:

  • setTimeout(fn, ms) — runs something once after a delay. For example, send a follow-up message 5 seconds after a user signs up.
  • setInterval(fn, ms) — runs something repeatedly on a fixed interval. For example, check a feed every 60 seconds.
bot.command('remindme', async (ctx) => {
  await ctx.reply('Got it! I will remind you in 5 seconds...');

  setTimeout(() => {
    ctx.reply('Reminder: 5 seconds have passed!');
  }, 5000); // 5000 milliseconds = 5 seconds
});
These in-memory timers only last as long as the bot is running. If the bot restarts or your code is re-saved, they disappear. For tasks that must survive restarts, see the Scheduled Tasks section below.

Scheduled tasks that last

Imagine you want your bot to send a daily summary to a user every morning at 9am. A simple timer like setTimeout would not work for this — if the bot ever restarts, the timer is gone and the task never runs again.

NxCreator solves this with persistent scheduled tasks. These are jobs that get stored safely outside the bot process, so they survive restarts, re-saves, and updates. Even if your bot goes down and comes back up, the scheduled task will still run at the right time.

How to think about it

Setting up a persistent task is a two-step process:

  • Step 1: Define what should happen. You write the function and give it a name — for example daily-report. This is your task definition.
  • Step 2: Set when it should run. You use the scheduler to tell NxCreator when to trigger that task — once, or on a repeating schedule.

The key insight is that NxCreator stores the name of your task, not the code itself. When the time comes to run it, it looks up the registered task by name and executes it. This is why the name you use in Step 1 must exactly match the name you use in Step 2.

Choosing the right tool

ToolBest forSurvives bot restarts?
setTimeoutA short one-time delay (e.g. wait 10 seconds)No
setIntervalA simple repeating action (e.g. ping every minute)No
cronTime-based repeating actions while the bot is runningNo
persistentScheduler.setTimeoutA one-time future action that must not be lostYes
persistentCron.scheduleA repeating time-based task that must keep runningYes
If a task is important — like sending a daily report or expiring a user subscription — always use the persistent versions. Regular timers are fine for temporary, low-stakes behavior.

Example: send a daily report every morning

This example shows how to send a message every morning at 9:00 AM. Read it from top to bottom — first the task is defined, then a bot command schedules it.

// Step 1: Define what happens when the task runs
registerPersistentTask('daily-report', async ({ payload }) => {
  await axios.post('https://api.example.com/report', {
    chatId: payload.chatId,
    period: 'daily',
  });
});

// Step 2: A command that schedules the task for the user
bot.command('schedule_report', async (ctx) => {
  // This time pattern means: at minute 0, hour 9, every day
  const timePattern = '0 9 * * *';

  if (!persistentCron.validate(timePattern)) {
    await ctx.reply('Something went wrong with the schedule.');
    return;
  }

  await persistentCron.schedule(
    'daily-report',        // must match the name from registerPersistentTask
    timePattern,
    { chatId: ctx.chat.id }, // data passed to the task when it runs
    { key: 'daily-report-main', timezone: 'UTC' }
  );

  await ctx.reply('Done! You will get a daily report at 09:00 UTC every morning.');
});

Breaking down what each part does:

  • registerPersistentTask('daily-report', ...): registers the job logic under the name daily-report. This is the work that will run on schedule.
  • payload: a small piece of data that gets saved alongside the schedule and passed back to your task when it runs. Here it carries the chat ID so the bot knows who to message.
  • persistentCron.validate(timePattern): checks that your time pattern is valid before saving it.
  • persistentCron.schedule(...): saves the task into the persistent scheduler so it will keep running even after restarts.
  • key: 'daily-report-main': a unique name for this particular scheduled job. If a user runs the command again, it updates the existing schedule instead of creating a duplicate.

Example: send a message after a delay

Sometimes you do not need a repeating schedule — you just need something to happen once, later. For example, sending a reminder one hour after a user signs up, even if the bot restarts in between.

// Define what happens when the delayed task fires
registerPersistentTask('follow-up-message', async ({ payload }) => {
  await bot.telegram.sendMessage(
    payload.chatId,
    'Just a reminder: your free trial ends in 1 hour.'
  );
});

// Schedule it to run once, 1 hour from now
bot.command('remind_later', async (ctx) => {
  const oneHour = 60 * 60 * 1000; // milliseconds

  const jobId = await persistentScheduler.setTimeout(
    'follow-up-message',
    oneHour,
    { chatId: ctx.chat.id }
  );

  await ctx.reply('Got it! You will hear from me in an hour. (Job ID: ' + jobId + ')');
});

This pattern works well for reminders, delayed follow-ups, expiring access codes, or anything else that should fire once at a specific time in the future.

Understanding the time pattern format

Persistent repeating schedules use a time pattern with five parts, separated by spaces. Each part controls one unit of time — minute, hour, day, month, and day of the week. A * means "every".

* * * * *
│ │ │ │ │
│ │ │ │ └── day of week  (0 = Sunday, 1 = Monday, ..., 6 = Saturday)
│ │ │ └──── month        (1–12)
│ │ └────── day of month (1–31)
│ └──────── hour         (0–23)
└────────── minute       (0–59)

Some practical examples to get you started:

  • 0 9 * * * — every day at 9:00 AM.
  • */15 * * * * — every 15 minutes, all day long.
  • 0 0 * * 1 — every Monday at midnight.
  • 30 18 * * 5 — every Friday at 6:30 PM.

Quick reference

  • registerPersistentTask(name, handler) — defines a named task that the scheduler can call later.
  • persistentCron.validate(pattern) — returns true if the time pattern is valid, false otherwise.
  • persistentCron.schedule(name, pattern, payload, options) — saves a repeating scheduled task.
  • persistentScheduler.setTimeout(name, ms, payload) — saves a one-time delayed task.
  • persistentCron.cancel(jobId) or persistentScheduler.cancel(jobId) — removes a saved task.

Common mistakes to avoid

  • Scheduling before registering: Always call registerPersistentTask first, then schedule it. The scheduler needs the task to already exist.
  • Mismatched names: The name in registerPersistentTask and the name in persistentCron.schedule must be exactly the same.
  • No key set: If a user can trigger the scheduling command more than once, always pass a key in options. Without it, each run of the command creates a new copy of the job.
  • Too much data in payload: Only put small identifiers in the payload (like a user ID or chat ID). Store everything else in the database.
A solid beginner pattern: store your app data in db, register a simple named task, and schedule it with a stable key. That covers most real use cases without overcomplicating things.
Last updated March 22, 2026
Was this page helpful?