Building Features

Scheduled Tasks

Run code in your app on a schedule, such as nightly cleanups, data syncs and reminder emails, and manage the runs.

Stardeck can run code on a schedule. Use it for anything that should happen automatically on a recurring basis: nightly database cleanups, syncing data from another service, sending a weekly report, or emailing a daily digest.

You describe the job, and your agent writes it with the scheduling SDK. Stardeck then runs it on your schedule, retries failures, and records every run.

Ask your agent

Ask your coding agent in your app's checkout. It follows the SKILL.md inside node_modules/@stardeck-customer-apps/scheduling-sdk. If you build in the browser, ask Starcat Developer in the app's Build tab instead. A few examples:

Daily cleanup

Every night at 2am, delete sessions that expired more than 7 days ago.

Recurring digest email

Every Monday at 9am, email each user a summary of their activity from the past week.

Sync external data

Every 15 minutes, pull the latest inventory from our supplier's API and update the products table.

Scheduled reminder

Each morning at 8am, send a reminder to customers who have an appointment that day.

The agent picks the cron expression and writes the job. The schedule starts running once the change is on your live app.

Info:

Schedules run on their own on your live (production) app, and on a staging copy until it expires. In the sandbox they run only while someone has the app open in the dashboard, and a local dev server never runs them. To test a job before then, trigger it by hand. See Managing Schedules.

How It Works

Schedules are defined in your app's code with the scheduling SDK (@stardeck-customer-apps/scheduling-sdk, included in every app). Your agent writes this code. Here's what it builds, so you know what to expect.

Defining a schedule

Each schedule has an id, a cron expression (in UTC), and a handler: the function that runs.

// lib/schedules.ts
import { defineSchedule } from "@stardeck-customer-apps/scheduling-sdk";

export const dailyCleanup = defineSchedule({
  id: "daily-cleanup",
  cron: "0 2 * * *", // Every day at 2:00am UTC
  description: "Delete expired sessions",
  handler: async (ctx) => {
    const deleted = await db.delete(sessions).where(lt(sessions.expiresAt, new Date()));
    return { deletedCount: deleted.count };
  },
});

Wiring it up

Schedules are registered through a single catch-all route. The platform calls this route to discover your schedules and to run them.

// app/api/schedules/[[...path]]/route.ts
import { schedulingHandler } from "@stardeck-customer-apps/scheduling-sdk/nextjs";
import { dailyCleanup, weeklyReport } from "@/lib/schedules";

export const { GET, POST } = schedulingHandler({
  schedules: [dailyCleanup, weeklyReport],
});

When you deploy, the platform reads your schedule definitions and starts running them on the cron expressions you set. Requests between the platform and your app are signed, so only the platform can trigger your jobs.

Schedule options

OptionRequiredDefaultDescription
idYes—Unique identifier for the schedule
cronYes—Cron expression, evaluated in UTC
handlerYes—The function to run
descriptionNo—Human-readable label shown in the dashboard
maxRetriesNo3How many times to retry if the handler throws
timeoutMsNo30000Max run time before the execution is considered failed

The handler receives a context object with scheduleId, executionId, scheduledAt, and attemptNumber (1 on the first try, higher on retries).

Tip:

If a handler does heavier work like sending a batch of emails, ask the agent to raise timeoutMs. The recommended ceiling is 5 minutes (300000).

Cron Expressions

All cron expressions run in UTC, not your local timezone. If you want a job to run at 9am in your timezone, convert that time to UTC first, or tell the agent "9am Bangkok time" and it converts it.

ExpressionWhen it runs
* * * * *Every minute
*/5 * * * *Every 5 minutes
0 * * * *Every hour, on the hour
0 */6 * * *Every 6 hours
0 0 * * *Every day at midnight
0 2 * * *Every day at 2:00am
0 9 * * 1-5Weekdays at 9:00am
0 9 * * 1Every Monday at 9:00am
0 0 1 * *First day of every month

Managing Schedules

Once your app is deployed, every schedule shows up in the dashboard.

  1. Open your app in Stardeck
  2. Go to Settings and open Services → Schedules

From there you can:

  • Pause and resume a schedule without removing it from your code
  • Trigger a run manually, to test it before the next scheduled time
  • View execution history for each schedule, with status, duration, and any error message
  • Delete orphaned schedules that are no longer in your code

Your coding agent can do the same over MCP: list_app_schedules, list_app_schedule_executions, set_app_schedule_status, trigger_app_schedule and delete_app_schedule.

Note:

Schedules are discovered from your code on each deploy. If you rename a schedule's id, redeploy so the platform picks up the change. The old entry will appear as orphaned and can be deleted.

Jobs that run only when there's work

A job that checks for work every few minutes ("any reminders to send?") queries your database on every run, even when nothing is due. Ask your agent to make it adaptive instead. The cron becomes the longest gap you accept, each run tells Stardeck when the next work is due, and the code that creates work (a new booking, say) wakes the job early. The scheduling SDK's SKILL.md has the pattern (adaptive: true, nextRun(), wakeSchedule()).

Retries and Failures

If a handler throws, the platform retries it automatically up to maxRetries times (default 3). Each retry increments attemptNumber, so your code can detect whether it's a first attempt or a retry.

export const syncInventory = defineSchedule({
  id: "sync-inventory",
  cron: "*/15 * * * *",
  maxRetries: 5,
  handler: async (ctx) => {
    if (ctx.attemptNumber > 1) {
      console.log(`Retry ${ctx.attemptNumber} for inventory sync`);
    }
    const items = await fetchInventory(); // throws → platform retries
    await upsertInventory(items);
    return { synced: items.length };
  },
});

Every attempt, success or failure, is recorded in the execution history so you can see exactly what happened.

Common Questions

My schedule isn't running

  • Schedules run on their own only on your live app (and on a staging copy). Make sure your app is published.
  • Confirm the deploy succeeded after you added the schedule.
  • Check the execution history in the Schedules tab for errors. You can also trigger the schedule manually to test it.

Why did it run at the wrong time?

Cron expressions are in UTC. A job set to 0 9 * * * runs at 9:00am UTC, which is a different local time depending on your timezone. Ask the agent to set the schedule in your local time and it will convert it for you.

Can I schedule a one-time task instead of a recurring one?

Schedules are built around recurring cron expressions. For a one-off action, set a schedule and have the handler check whether it has already run (for example, by reading a flag from your database), then pause or delete it afterward.

My handler keeps timing out

The default timeout is 30 seconds. If your job does more work than that, ask the agent to raise timeoutMs (up to 5 minutes). For very large jobs, split the work across multiple runs.

Need help setting up a schedule? Reach out to support@stardeck.ai.

Next steps