Schedules — Agentuity Documentation

Schedules

Create platform-managed cron jobs with HTTP and sandbox destinations

When a job needs to run on a timer, like data syncs, nightly cleanup, or report generation, Schedules let you configure timing and destinations outside your app code. The platform calls your endpoints on a cron schedule, tracks each delivery, and retries on failure.

When to Use Schedules

ApproachBest For
SchedulesPlatform-managed recurring jobs with delivery tracking and multiple destination types
Cron RoutesIn-process scheduled handlers with direct access to agent context
QueuesEvent-driven async processing, not time-based

Managing Schedules

ContextAccessDetails
Agentsctx.scheduleSee examples below
Routesc.var.scheduleSee Using in Routes
CLIagentuity cloud scheduleManage schedules from the command line
Web AppWeb AppCreate and inspect schedules in the UI

Creating Schedules

Pass a name, an expression (standard five-field cron), and an optional array of destinations. The schedule and its destinations are created atomically: if any destination fails validation, the entire operation is rolled back.

import { createAgent } from '@agentuity/runtime';
 
const agent = createAgent('ScheduleSetup', {
  handler: async (ctx, input) => {
    const result = await ctx.schedule.create({ 
      name: 'Daily Report',
      description: 'Generate and email daily reports',   // optional
      expression: '0 9 * * 1-5',  // weekdays at 9am
      destinations: [
        {
          type: 'url',
          config: {
            url: 'https://example.com/reports/generate',
            method: 'POST',
            headers: { 'Authorization': 'Bearer my-token' },
          },
        },
      ],
    });
 
    ctx.logger.info('Schedule created', {
      id: result.schedule.id,
      nextRun: result.schedule.due_date,
    });
 
    return { scheduleId: result.schedule.id };
  },
});

Common Cron Expressions

ExpressionFires
* * * * *Every minute
*/5 * * * *Every 5 minutes
0 * * * *Every hour
0 0 * * *Daily at midnight
0 9 * * 1-5Weekdays at 9am
0 0 * * 1Weekly on Monday at midnight
0 0 1 * *Monthly on the 1st at midnight

The server validates cron expressions on creation and update. due_date on the schedule reflects the next computed execution time.

Destination Types

Each schedule can have multiple destinations. When the schedule fires, the platform sends a request to each destination.

TypeConfigDescription
url{ url, headers?, method? }Sends an HTTP request to the URL on schedule
sandbox{ sandbox_id, command? }Runs a command in a sandbox on schedule

URL Destination

import { createAgent } from '@agentuity/runtime';
 
const agent = createAgent('WebhookScheduler', {
  handler: async (ctx, input) => {
    const { destination } = await ctx.schedule.createDestination(input.scheduleId, { 
      type: 'url', 
      config: {
        url: 'https://example.com/webhook/sync',
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${input.apiToken}`,
          'Content-Type': 'application/json',
        },
      },
    });
 
    ctx.logger.info('URL destination added', { destinationId: destination.id });
    return { destinationId: destination.id };
  },
});

Sandbox Destination

import { createAgent } from '@agentuity/runtime';
 
const agent = createAgent('SandboxScheduler', {
  handler: async (ctx, input) => {
    const { destination } = await ctx.schedule.createDestination(input.scheduleId, { 
      type: 'sandbox', 
      config: {
        sandbox_id: input.sandboxId,
        command: 'bun run src/run/sync.ts',
      },
    });
 
    ctx.logger.info('Sandbox destination added', { destinationId: destination.id });
    return { destinationId: destination.id };
  },
});

Listing and Updating Schedules

import { createAgent } from '@agentuity/runtime';
 
const agent = createAgent('ScheduleManager', {
  handler: async (ctx, input) => {
    // List schedules with optional pagination
    const { schedules, total } = await ctx.schedule.list({ limit: 20, offset: 0 });
    ctx.logger.info('Schedules', { count: schedules.length, total });
 
    // Get a specific schedule with its destinations
    const { schedule, destinations } = await ctx.schedule.get(input.scheduleId);
    ctx.logger.info('Next run', { dueDate: schedule.due_date });
 
    // Update the cron expression: due_date is automatically recomputed
    const { schedule: updated } = await ctx.schedule.update(input.scheduleId, { 
      expression: '0 0 * * *',  // change to daily at midnight
    });
 
    return { nextRun: updated.due_date, destinationCount: destinations.length };
  },
});

Delivery Tracking

Each time a schedule fires, one delivery record is created per destination. Use listDeliveries to inspect delivery history and check for failures.

import { createAgent } from '@agentuity/runtime';
 
const agent = createAgent('DeliveryInspector', {
  handler: async (ctx, input) => {
    const { deliveries } = await ctx.schedule.listDeliveries(input.scheduleId); 
 
    for (const delivery of deliveries) {
      ctx.logger.info('Delivery record', {
        date: delivery.date,
        status: delivery.status,      // 'pending' | 'success' | 'failed'
        retries: delivery.retries,    // number of retry attempts
        error: delivery.error,        // null on success
      });
    }
 
    const failed = deliveries.filter(d => d.status === 'failed');
    return { total: deliveries.length, failed: failed.length };
  },
});

Delivery Statuses

StatusDescription
pendingDelivery is queued and has not been attempted yet
successThe destination received the request and responded successfully
failedThe delivery encountered an error; error field contains the reason

The retries field on each delivery record tracks how many retry attempts were made before the final status was recorded.

Fetching a Specific Destination or Delivery

Two convenience methods let you look up a single record by ID without iterating manually.

getDestination(scheduleId, destinationId) fetches the schedule via get() and returns the matching destination:

const dest = await ctx.schedule.getDestination(input.scheduleId, input.destinationId);
ctx.logger.info('Destination type', { type: dest.type });

getDelivery(scheduleId, deliveryId, params?) calls listDeliveries() and returns the matching record. Pass params to control the pagination window if the delivery may not be in the first page:

const delivery = await ctx.schedule.getDelivery(input.scheduleId, input.deliveryId);
ctx.logger.info('Delivery status', { status: delivery.status, retries: delivery.retries });

Both methods throw if the ID is not found in the result set.

Removing Destinations and Schedules

import { createAgent } from '@agentuity/runtime';
 
const agent = createAgent('ScheduleCleaner', {
  handler: async (ctx, input) => {
    // Remove a single destination (keeps the schedule)
    await ctx.schedule.deleteDestination(input.destinationId);
 
    // Delete a schedule and all its destinations and delivery history
    await ctx.schedule.delete(input.scheduleId); 
 
    return { deleted: true };
  },
});

Using in Routes

Routes access the schedule service via c.var.schedule:

import { createRouter } from '@agentuity/runtime';
 
const router = createRouter();
 
router.post('/schedules', async (c) => {
  const body = await c.req.json();
 
  const result = await c.var.schedule.create({ 
    name: body.name,
    expression: body.expression,
    destinations: body.destinations,
  });
 
  return c.json({ scheduleId: result.schedule.id, nextRun: result.schedule.due_date });
});
 
router.get('/schedules/:id/deliveries', async (c) => {
  const scheduleId = c.req.param('id');
  const { deliveries } = await c.var.schedule.listDeliveries(scheduleId);
 
  return c.json({ deliveries });
});
 
export default router;

Best Practices

  • Use descriptive names: names like "Daily User Report - EU" are easier to manage than "job-1" when listing schedules in the dashboard
  • Keep destinations idempotent: the platform may retry failed deliveries, so ensure your endpoints handle duplicate invocations safely
  • Monitor delivery history: poll listDeliveries or check the dashboard to catch recurring failures before they accumulate
  • Remove unused destinations before changing schedules: deleting a destination does not affect other destinations on the same schedule, making partial updates safe

Next Steps

  • Cron Routes: In-process scheduled handlers that run inside your deployed app
  • Queues: Async, event-driven processing for work that doesn't need a fixed time trigger
  • Sandbox: Isolated execution environments used as schedule destinations