How It Works

How NxCreator runs your code, updates the bot runtime, and exposes built-in utilities.

Runtime model

Each bot runs in an isolated Node.js execution context managed by NxCreator. Your sections are evaluated into a single bot runtime, and the platform keeps the Telegram integration layer connected for you.

The legacy docs emphasized that NxCreator is close to standard Telegraf, but not identical. That distinction matters: use the built-in globals and platform patterns instead of trying to recreate your own app bootstrap logic.

Save cycle

When you save code, NxCreator updates the running bot definition without making you manage a separate deployment pipeline. In practice, that means faster testing: edit a handler, save, then verify the result immediately in Telegram.

Web requests

Use the global axios instance for HTTP requests. This is the standard way to call external APIs from a bot command or scene.

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('Failed to fetch news.');
  }
});

Timers & utilities

Standard timer functions such as setTimeout() and setInterval() can be used for reminders and recurring tasks inside the bot runtime.

bot.command('remindme', async (ctx) => {
  await ctx.reply('I will remind you in 5 seconds...');

  setTimeout(() => {
    ctx.reply('Reminder: 5 seconds passed!');
  }, 5000);
});

Utility modules such as crypto are also useful for app-specific logic, but keep long-running interval logic disciplined so it does not turn into unmanaged background behavior.

Persistent schedules

Beginners often start with setTimeout(), setInterval(), or cron.schedule(...). Those are useful, but they live only inside the current running bot process. If the bot reloads, restarts, or your code is re-evaluated, those in-memory schedules may disappear.

Use persistentScheduler and persistentCron when the schedule must survive reloads. NxCreator stores those jobs outside the temporary runtime, then runs them again later by looking up a handler name you registered earlier.

Mental model

Think of a persistent schedule as a two-step system:

  • Step 1: Register a task. You give NxCreator a stable handler name like daily-report and the function that should run.
  • Step 2: Schedule that task. You tell NxCreator when to run it, either once in the future or on a repeating cron pattern.

This separation is important. The scheduler stores the handler name, not the function body itself. So if you schedule daily-report, NxCreator later finds the task registered under that same name and executes it.

When to use which scheduler

ToolBest forPersists across reloads?
setTimeoutOne short delay inside the current runtimeNo
setIntervalSimple repeating work inside the current runtimeNo
cron.scheduleRuntime-local cron jobsNo
persistentScheduler.setTimeoutOne delayed job that must still run laterYes
persistentCron.scheduleRecurring jobs that must survive reloadsYes
If the job matters after a restart, use the persistent APIs. If it is only temporary runtime behavior, regular timers are enough.

Persistent cron example

This example sends a daily report every morning. Read it in order: first the task is registered, then a command schedules it.

registerPersistentTask('daily-report', async ({ payload }) => {
  await axios.post('https://api.example.com/report', {
    chatId: payload.chatId,
    period: 'daily',
  });
});

bot.command('schedule_report', async (ctx) => {
  const expression = '0 9 * * *';

  if (!persistentCron.validate(expression)) {
    await ctx.reply('Invalid cron expression.');
    return;
  }

  await persistentCron.schedule(
    'daily-report',
    expression,
    { chatId: ctx.chat.id },
    { key: 'daily-report-main', timezone: 'UTC' }
  );

  await ctx.reply('Daily report scheduled for 09:00 UTC.');
});

What is happening here:

  • registerPersistentTask('daily-report', ...): defines the job logic and gives it a permanent name.
  • payload: stores small data the handler will need later, such as the current chat ID.
  • persistentCron.validate(expression): checks the cron string before the job is saved.
  • persistentCron.schedule(...): writes the cron job into the persistent scheduler.
  • key: 'daily-report-main': prevents duplicate jobs by updating the same schedule if the command runs again.

One-time delayed job example

Sometimes you do not need a repeating cron expression. You just want something to happen later, even if the bot reloads first. Use persistentScheduler.setTimeout(...) for that.

registerPersistentTask('follow-up-message', async ({ payload }) => {
  await bot.telegram.sendMessage(
    payload.chatId,
    'Reminder: your trial ends in 1 hour.'
  );
});

bot.command('remind_later', async (ctx) => {
  const oneHour = 60 * 60 * 1000;

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

  await ctx.reply('Reminder saved. Job ID: ' + jobId);
});

Use this pattern for reminders, delayed follow-ups, temporary unlock expirations, or anything else that should happen once at a later time.

How it works

  • Named handlers: registerPersistentTask(name, handler) registers the function the scheduler will call later.
  • Cron validation: persistentCron.validate(expression) checks your node-cron expression before scheduling it.
  • Idempotent schedules: Pass options.key to upsert a cron job instead of creating duplicates every time the command runs.
  • Timezone support: persistentCron.schedule(..., { timezone: 'UTC' }) stores the timezone with the job.
  • Cancellation: Use persistentCron.cancel(jobId), persistentCron.stop(jobId), or persistentScheduler.cancel(jobId) to remove a persisted job.

Cron format basics

NxCreator uses node-cron style expressions. A common five-part expression looks like this:

* * * * *
│ │ │ │ │
│ │ │ │ └ day of week
│ │ │ └── month
│ │ └──── day of month
│ └────── hour
└──────── minute

Examples:

  • 0 9 * * * means every day at 09:00.
  • */15 * * * * means every 15 minutes.
  • 0 0 * * 1 means every Monday at midnight.

Common mistakes

  • Scheduling before registering: Register the task name first, then schedule it.
  • Using a different handler name later: The scheduled job and the registered task name must match exactly.
  • Forgetting a key: If users can run the setup command multiple times, use options.key to avoid duplicate cron jobs.
  • Putting too much data in payload: Keep payloads small and simple. Store large or changing data in the database and only pass identifiers in the payload.
  • Using local cron for important jobs: If the job must survive reloads, use the persistent API, not plain cron.schedule(...).
A good beginner pattern is: save important state in db, register one small named task, and schedule it with a stable key. That keeps the scheduler simple and predictable.
PREVIOUS
Quick Start
NEXT
Python Bots
Was this page helpful?
Last updated March 22, 2026