# How to Set Up Recurring Jobs and Scheduled Tasks in Agent-Native

> Learn to set up recurring jobs and scheduled tasks in Agent-Native using Nitro plugins and `registerJob`. Schedule tasks with cron expressions or fixed intervals for efficient background work.

- Repository: [Builder.io/agent-native](https://github.com/BuilderIO/agent-native)
- Tags: how-to-guide
- Published: 2026-06-29

---

**Agent-Native runs background work through recurring jobs registered as Nitro plugins using the `registerJob` function, with job implementations stored in `templates/*/server/jobs/` and scheduled via cron expressions or fixed intervals.**

Setting up recurring jobs and scheduled tasks in Agent-Native requires understanding its Nitro-based plugin architecture. The BuilderIO/agent-native repository provides a robust automation framework where background tasks are registered at server startup and persist across restarts. This guide walks through the core implementation patterns using the `registerJob` API and the scheduling utilities found in the templates and core packages.

## Core Architecture of Recurring Jobs

Agent-Native implements recurring jobs as **Nitro plugins** that register async functions with the internal scheduler. According to the source code in [`.agents/skills/automations/recurring-jobs.ts`](https://github.com/BuilderIO/agent-native/blob/main/.agents/skills/automations/recurring-jobs.ts), the framework requires two distinct components: a job implementation module containing the actual business logic, and a plugin file that calls `registerJob` to add the task to the dispatch core. The scheduler guarantees execution even after server restarts by storing job definitions in the persistent dispatch layer.

## Step-by-Step: Creating a Recurring Job

### Step 1: Implement the Job Logic

Create a TypeScript module under your template’s `server/jobs/` directory that exports an async function. This function receives a context object containing the database connection, logger, and other services. In [`templates/clips/server/jobs/poll-calendars.ts`](https://github.com/BuilderIO/agent-native/blob/main/templates/clips/server/jobs/poll-calendars.ts), the implementation fetches external calendar data and upserts it into the local database:

```typescript
// templates/clips/server/jobs/poll-calendars.ts
import { getDb } from '@agent-native/core/db'
import { googleCalendarClient } from '../lib/google-calendar-client'

export async function pollCalendars(ctx: any) {
  const db = getDb()
  const accounts = await db.selectFrom('calendar_accounts').selectAll().execute()

  for (const acct of accounts) {
    const events = await googleCalendarClient(acct).listEvents({
      timeMin: new Date().toISOString(),
      timeMax: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(),
    })
    
    await db
      .insertInto('calendar_events')
      .values(events.map(e => ({ ...e, accountId: acct.id })))
      .onConflictDoUpdate({ target: ['googleEventId'], set: { ...e } })
      .execute()
  }

  ctx.logger.info('Poll-calendars completed', { accounts: accounts.length })
}

```

### Step 2: Register the Job in a Nitro Plugin

Create a plugin file under `templates/*/server/plugins/` that imports the `registerJob` function from `@agent-native/core/automation`. The plugin executes when the Nitro server starts, adding your job to the scheduler with a specific cadence. The [`templates/clips/server/plugins/calendar-jobs.ts`](https://github.com/BuilderIO/agent-native/blob/main/templates/clips/server/plugins/calendar-jobs.ts) file demonstrates registering the calendar poll job to run every five minutes:

```typescript
// templates/clips/server/plugins/calendar-jobs.ts
import { defineNitroPlugin } from '@nitrojs/core'
import { registerJob } from '@agent-native/core/automation'

export default defineNitroPlugin(() => {
  registerJob('poll-calendars', '*/5 * * * *', async (ctx) => {
    const { pollCalendars } = await import('../jobs/poll-calendars')
    await pollCalendars(ctx)
  })
})

```

### Step 3: Configure the Schedule

The `registerJob` function accepts either a **cron expression** (e.g., `*/5 * * * *` for every five minutes) or a fixed-rate interval measured in seconds. The first parameter is the unique job identifier, the second is the schedule string, and the third is the async handler function. The scheduler parses these configurations within the dispatch core and triggers execution according to the specified pattern.

## Manual Triggering via Actions

For scenarios requiring manual execution or custom parameters, expose the job logic through the actions API using `defineAction`. This creates a typed interface that UI components or other services can invoke via `useActionMutation`. The following example from [`actions/calendar/poll.ts`](https://github.com/BuilderIO/agent-native/blob/main/actions/calendar/poll.ts) wraps the same job logic for on-demand execution:

```typescript
// actions/calendar/poll.ts
import { defineAction } from '@agent-native/core/actions'

export const pollCalendarsNow = defineAction('pollCalendarsNow', {
  description: 'Trigger the calendar-poll job immediately',
  async run(_, ctx) {
    const { pollCalendars } = await import('../../templates/clips/server/jobs/poll-calendars')
    await pollCalendars(ctx)
    return { ok: true }
  },
})

```

## Leveraging the Scheduling Package

For complex calendar-based operations, the `packages/scheduling` library provides server-side utilities that recurring jobs can import. The [`packages/scheduling/src/server/availability-engine.ts`](https://github.com/BuilderIO/agent-native/blob/main/packages/scheduling/src/server/availability-engine.ts) file exports functions like `getAvailableSlots` that compute time slots based on team availability. Jobs can also manipulate schedule definitions programmatically using utilities found in [`packages/scheduling/src/actions/update-schedule.ts`](https://github.com/BuilderIO/agent-native/blob/main/packages/scheduling/src/actions/update-schedule.ts), enabling dynamic modification of recurring intervals based on external calendar data.

## Summary

- **Job implementations** belong in `templates/*/server/jobs/` as async functions accepting a context object with database and logger services.
- **Registration** occurs in `templates/*/server/plugins/` using `defineNitroPlugin` and the `registerJob` function from `@agent-native/core/automation`.
- **Scheduling** supports standard cron expressions or fixed intervals, with persistence handled by the dispatch core across server restarts.
- **Manual triggers** can be exposed via `defineAction` in the `actions/` directory to create callable endpoints for immediate job execution.
- **Advanced utilities** in `packages/scheduling` provide time-zone handling and availability calculations for calendar-centric automation.

## Frequently Asked Questions

### Where does Agent-Native store recurring job implementations?

Recurring job implementations are stored as TypeScript modules under `templates/*/server/jobs/`, such as [`templates/clips/server/jobs/poll-calendars.ts`](https://github.com/BuilderIO/agent-native/blob/main/templates/clips/server/jobs/poll-calendars.ts). These files export async functions that contain the actual business logic, database queries, and external API calls to be executed on each schedule tick.

### How does Agent-Native ensure jobs persist after server restarts?

The framework stores job registrations in the **dispatch core**, which maintains persistent state independent of the server process. When the Nitro server restarts, plugins in `templates/*/server/plugins/` re-register the jobs via `registerJob`, and the scheduler restores the execution timeline from its persistent store, guaranteeing that missed intervals are handled appropriately.

### Can I trigger a recurring job manually instead of waiting for the schedule?

Yes, by exposing the job logic through an action using `defineAction` in the `actions/` directory. This creates a typed API endpoint that can be invoked from UI components via `useActionMutation` or called directly from other server-side code, allowing immediate execution of the same logic contained in your scheduled job.

### What is the difference between `registerJob` and `defineAction`?

**`registerJob`** is used within Nitro plugins to schedule background work that runs automatically on a timer (cron or fixed interval), while **`defineAction`** creates callable API endpoints for request/response interactions. Jobs registered with `registerJob` run autonomously in the background, whereas actions defined with `defineAction` must be explicitly invoked by a client or another service.