# How Agent-Native Handles Automations with Schedule‑Triggered Conditions

> Learn how Agent-Native handles schedule-triggered automations. Discover its database scheduler and automation engine integration for efficient rule execution.

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

---

**Agent‑Native implements schedule‑triggered automations through a built‑in database scheduler that polls the `scheduled_jobs` table every minute, executing rules defined in `automation_rules` via the automation engine in `templates/mail/server/lib/automation‑engine.ts`.**

The **BuilderIO/agent‑native** repository provides a complete automation subsystem within its Mail template, enabling users to define time‑based rules that execute without manual intervention. This architecture leverages Drizzle ORM for persistence, a lightweight job runner for scheduling, and a robust action engine for performing operations against external APIs like Gmail. Below is a technical walkthrough of how schedule‑triggered conditions are modeled, scheduled, and executed.

## Database Schema for Scheduled Automations

The persistence layer resides in [`templates/mail/server/db/schema.ts`](https://github.com/BuilderIO/agent-native/blob/main/templates/mail/server/db/schema.ts) and defines three critical tables that power the scheduler.

**automation_rules** stores the rule definition, including a JSON `schedule` column that encodes the trigger condition (e.g., `{ type: "daily", time: "09:00" }`). Each row tracks the `ownerEmail`, `enabled` flag, filter criteria, and the array of actions to perform.

**scheduled_jobs** acts as the timer queue. When a rule includes a schedule, the system inserts a row with `type = "automation"`, a computed `run_at` timestamp, and status `"pending"`. The table supports generic job scheduling (also used for email snoozing) but distinguishes automation triggers via the type field.

**automation_settings** (accessed within `automation‑engine.ts`) stores per‑account watermarks and configuration flags, such as the `automation‑watermark` key that tracks the last processed Gmail message ID for incremental sync.

Performance is ensured through targeted indexes: `idx_automation_rules_owner` accelerates user‑specific rule lookups, while `idx_scheduled_jobs_status_run_at` optimizes the polling query that fetches due jobs.

## Job Runner and Scheduling Mechanism

The scheduler implementation lives in `templates/mail/server/plugins/mail‑jobs.ts`. It registers a **setInterval** loop (approximately one‑minute resolution) that queries `scheduled_jobs` for rows with status `"pending"` or `"processing"` where `run_at <= now()`.

For each due job, the runner:

1. Loads the associated automation rule(s) from `automation_rules`.
2. Invokes `processAutomations` from `templates/mail/server/lib/automation‑engine.ts`.
3. Updates the job status from `"pending"` → `"processing"` → `"done"`.

The scheduling logic itself, including timestamp calculation for recurring runs, is handled by utilities in [`templates/mail/server/lib/jobs.ts`](https://github.com/BuilderIO/agent-native/blob/main/templates/mail/server/lib/jobs.ts) (see `scheduleEmailSend` and `scheduleSnooze` for reference implementations of `run_at` computation). Because this runs within the Node.js process, **no external cron service is required**.

## Automation Engine and Message Processing

`templates/mail/server/lib/automation‑engine.ts` contains the core `processAutomations` function, which orchestrates data fetching and rule evaluation.

The engine first refreshes OAuth tokens for the target account (line 116 logs `Token refresh failed for ${accountEmail}` upon expiry). It then retrieves inbox messages using a watermark‑based approach: it consults `automation_settings` to find the last processed message ID, fetches newer messages via the Gmail API, and falls back to a full message list if the History API is unavailable (lines 233‑258).

For each candidate message, the engine evaluates the rule’s `filter` predicate. Matches trigger `runAutomationAction` (imported from `templates/mail/server/lib/automation‑actions.ts`), which executes concrete side effects such as:

- **`addLabel`** – applies a Gmail label ID to the message.
- **`starMessage`** – stars the conversation.
- **`forward`** – sends the message to a specified recipient (requires extending the action union type).

Error handling is **best‑effort**: individual rule failures are caught and logged (`Rule evaluation failed` at line 540; `Failed for ${account.accountId}` at lines 701‑743) without aborting the batch, ensuring one misconfigured rule does not block account processing.

## Public API and Manual Triggering

The system exposes REST endpoints under `/api/automations` defined in `templates/mail/server/routes/api/automations/`:

- **`GET /api/automations`** ([`index.get.ts`](https://github.com/BuilderIO/agent-native/blob/main/index.get.ts)) – lists all rules for the authenticated user.
- **`POST /api/automations`** ([`index.post.ts`](https://github.com/BuilderIO/agent-native/blob/main/index.post.ts)) – creates a new rule and inserts the initial `scheduled_jobs` entry.
- **`PATCH /api/automations/:id`** (`[id].patch.ts`) – updates rule criteria or schedule.
- **`DELETE /api/automations/:id`** (`[id].delete.ts`) – removes the rule and cancels pending jobs.
- **`POST /api/automations/trigger`** ([`trigger.post.ts`](https://github.com/BuilderIO/agent-native/blob/main/trigger.post.ts)) – immediately executes all enabled automations, bypassing the schedule.

To manually trigger automations (useful for testing or "run now" UI buttons):

```bash
curl -X POST https://your-app.com/api/automations/trigger \
     -H "Authorization: Bearer $AGENT_NATIVE_TOKEN"

```

The handler lazily imports `triggerAutomations` from the handlers directory, which then invokes the engine for the requesting user’s account.

## Frontend Integration and React Hooks

The UI layer consumes these endpoints through the **`use‑automations`** hook located in `templates/mail/app/hooks/use‑automations.ts`. This React Query wrapper provides cached data fetching and automatic background updates.

When mutations occur (create, update, delete), the hook invalidates the `["automations"]` query key, causing [`templates/mail/app/pages/SettingsPage.tsx`](https://github.com/BuilderIO/agent-native/blob/main/templates/mail/app/pages/SettingsPage.tsx) to re‑fetch the rule list without a page reload.

### Example: Creating a Daily Automation

```typescript
import { useMutation, useQueryClient } from '@tanstack/react-query';

function CreateScheduledAutomation() {
  const qc = useQueryClient();

  const createAutomation = useMutation({
    mutationFn: async (payload) => {
      const res = await fetch('/api/automations', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(payload),
      });
      return res.json();
    },
    onSuccess: () => qc.invalidateQueries({ queryKey: ['automations'] }),
  });

  const handleSubmit = () => {
    createAutomation.mutate({
      name: 'Morning Newsletter Sort',
      filter: { from: '*@newsletter.com' },
      actions: [{ type: 'addLabel', labelId: 'Label_123' }],
      schedule: { type: 'daily', time: '09:00' },
      enabled: true,
    });
  };

  return <button onClick={handleSubmit}>Create Daily Rule</button>;
}

```

The `schedule` object is serialized to JSON and stored in `automation_rules.schedule`; the backend creates the corresponding `scheduled_jobs` row with the calculated next run time.

## Summary

- **Schedule‑triggered automations** in Agent‑Native rely on the `scheduled_jobs` table polled by `mail‑jobs.ts` to drive execution.
- **Rule definitions** reside in `automation_rules`, including JSON schedules and filter criteria, indexed by owner for fast retrieval.
- **The automation engine** in `automation‑engine.ts` handles token refresh, watermark‑based incremental sync, and best‑effort error isolation.
- **Actions** are implemented in `automation‑actions.ts`, with built‑in support for labeling, starring, and extensible hooks for custom operations.
- **Manual execution** is available via `POST /api/automations/trigger`, while the React frontend uses `use‑automations` for real‑time synchronization.

## Frequently Asked Questions

### How does Agent‑Native handle authentication failures during long‑running automation jobs?

The automation engine in `templates/mail/server/lib/automation‑engine.ts` proactively refreshes OAuth tokens before processing each batch (logging `Token refresh failed for ${accountEmail}` on line 116 if refresh fails). This ensures that scheduled jobs running hours or days after the initial user login still maintain valid Gmail API credentials without manual re‑authorization.

### What happens when a scheduled automation rule fails during execution?

The engine employs **best‑effort error isolation**: each rule executes inside a `try/catch` block (see lines 540 and 701‑743 in `automation‑engine.ts`). If a rule throws—due to invalid filters, API errors, or malformed actions—the error is logged to the console (`Rule evaluation failed` or `Failed for ${account.accountId}`), the offending rule is skipped, and processing continues for remaining rules. The job status may update to `"done"` even if individual actions failed, as failures are logged rather than halting the entire batch.

### Is an external cron service required to run schedule‑triggered automations?

No. Agent‑Native uses an **in‑process scheduler** via `setInterval` in `templates/mail/server/plugins/mail‑jobs.ts`, polling the `scheduled_jobs` table approximately every minute. This design eliminates infrastructure dependencies like `cron` or distributed job queues, making the Mail template self‑contained and deployable to standard Node.js hosting without additional configuration.

### How can I add custom action types to the automation engine?

Extend the **Action** union type in [`templates/mail/shared/types.ts`](https://github.com/BuilderIO/agent-native/blob/main/templates/mail/shared/types.ts) to include your new action identifier (e.g., `"forward"`). Then modify `runAutomationAction` in `templates/mail/server/lib/automation‑actions.ts` to handle the new case, implementing the specific API calls or business logic required. The automation engine will automatically pick up the new action type when processing rules, as it iterates over the actions array dynamically.