Schedules
Purpose and Scope
Schedules let an eve agent start work from time rather than from an inbound user message, webhook, or channel event. Use them for recurring backend agent activity such as daily digests, data syncs, cleanup sweeps, heartbeat checks, alert polling, and other tasks that should run on a cadence. The schedule itself is an entry point into agent execution, not a chat surface and not a replacement for a channel. It tells eve when to start work and whether that work is a prompt-only task or a custom handler. Sources: docs/schedules.mdx
In eve's filesystem-first model, each schedule is a single file under agent/schedules/ and carries a cron expression. That placement is part of the public authoring contract: the root agent owns schedules, and declared subagents cannot define their own schedules/ directory. The schedule name is derived from the path below the schedules directory, so nested folders become a readable namespace for domains such as billing, operations, or reporting. This keeps recurring entry points discoverable without a separate registry. Sources: docs/schedules.mdx
This page documents the schedule contract from the developer's point of view: how schedules are named and discovered, what defineSchedule accepts, when to choose markdown task mode, when to write a handler, and how scheduled work can hand off into a channel with explicit app authorization. It also calls out the operational differences between local development and production cadence. For durable workflow terminology such as session, turn, and step, read the execution model page alongside this one.
Relevant Source Files
docs/schedules.mdx- Defines the schedule feature, filesystem location, root-only restriction, naming convention,defineScheduleshape, cron behavior, markdown task mode, handler mode, cross-channel hand-off,waitUntil, and theappAuthprincipal.
Core Primitives
A schedule has one required timing field and one work-starting mechanism. The required field is cron, a standard five-field cron string using minute granularity. The work-starting mechanism is either markdown or run, but not both. markdown is the fire-and-forget prompt form, while run is a TypeScript handler for cases where the schedule must compute arguments, branch, or deliver through a channel. The documentation describes defineSchedule as a type-level pass-through, so TypeScript shape and compiler checks are the main guardrails. Sources: docs/schedules.mdx
interface ScheduleDefinition {
cron: string;
markdown?: string;
run?: (args: ScheduleHandlerArgs) => Promise<void> | void;
}
interface ScheduleHandlerArgs {
receive: CrossChannelReceiveFn;
waitUntil: (task: Promise<unknown>) => void;
appAuth: SessionAuthContext;
}The cron string uses the familiar order of minute, hour, day of month, month, and day of week. On Vercel, each schedule becomes a Vercel Cron Job, and the expression is evaluated in UTC. That means a weekday morning expression fires according to UTC rather than a local business timezone. If the scheduled work sends customer-facing summaries or performs time-sensitive maintenance, convert the desired local schedule into UTC or design the prompt and tools to account for the relevant tenant or region at runtime. Sources: docs/schedules.mdx
| Primitive | Contract | Use when |
|---|---|---|
cron | Five-field cron string | The schedule must run at a predictable cadence |
markdown | Prompt body for task mode | The output can be discarded and the work is autonomous |
run(args) | Handler function | The schedule needs code, branching, or channel delivery |
receive(channel, { message, target, auth }) | Cross-channel hand-off | A scheduled run should start a session on a channel |
waitUntil(promise) | Lifetime extension | Async work must settle after the handler returns |
appAuth | Runtime app principal | The scheduled run should act as the application |
Filesystem Naming and Discovery
Schedule names come from file paths under the root schedule directory. A file such as agent/schedules/billing/sweep.ts becomes the schedule named billing/sweep, and nested directories are explicitly supported. This convention gives teams a low-friction organization model: place related schedules together by product area or operational domain, and the name naturally follows. Because subagents cannot define schedules, a reader can inspect the root schedule tree to understand every recurring entry point in the application. Sources: docs/schedules.mdx
That root-only rule is important for ownership and review. A scheduled run can initiate work without a live user, so teams need a clear place to audit cadence, prompts, targets, and authorization decisions. Keeping schedules at the application boundary prevents delegated subagents from silently adding recurring work of their own. Subagents can still participate when the root agent's scheduled session invokes capabilities, but the clock-based trigger remains centralized where deployers can reason about cost, external side effects, and production behavior. Sources: docs/schedules.mdx
Markdown Task Mode
Markdown task mode is the smallest useful schedule. In TypeScript, set markdown to the prompt that the agent should run on the configured cadence. Eve starts a task-mode session, lets the agent call tools, write to backends, and log, then discards the final output. This is best for work where the important result is an external side effect or persisted state, not a conversational reply. Examples include refreshing a metrics endpoint, sweeping stale workflow state, or collecting a periodic health signal. Sources: docs/schedules.mdx
import { defineSchedule } from "eve/schedules";
export default defineSchedule({
cron: "*/5 * * * *",
markdown: "Pull open Linear issues and POST a summary to the metrics endpoint.",
});Task mode has a hard human-in-the-loop constraint: it runs to completion or fails, and it cannot park while waiting for a person or an OAuth sign-in. That makes it a poor fit for recurring work that may require approval, account authorization, or a user decision. If the task might need a human, route it through a channel or make the underlying tools fail safely and retry later. Treat the prompt as an autonomous instruction for work that can complete without interactive intervention. Sources: docs/schedules.mdx
The same task-mode shape can be authored as a plain Markdown file. In that form, frontmatter contains the cron expression and the document body becomes the prompt. This preserves eve's text-first authoring style: operational behavior that can be expressed as durable instructions can stay in Markdown, while behavior that requires runtime code can move to a TypeScript schedule. The Markdown form is especially useful for maintenance tasks where reviewers should be able to read the cadence and the instruction without scanning JavaScript. Sources: docs/schedules.mdx
---
cron: "0 0 * * 0"
---
Sweep stale workflow state.Handler Flow and Channel Handoff
Use the run handler form when a scheduled fire needs code at the moment it runs. The docs identify three common reasons: delivering to a channel, branching on conditions, or computing arguments dynamically. The handler has no channel of its own, so proactive delivery uses receive to hand the work to an existing channel module. This distinction matters: cron starts backend work, but Slack, Discord, a custom HTTP channel, or another configured channel owns the actual delivery surface and target shape. Sources: docs/schedules.mdx
import { defineSchedule } from "eve/schedules";
import slack from "../channels/slack.js";
export default defineSchedule({
cron: "* * * * *",
async run({ receive, waitUntil, appAuth }) {
waitUntil(
receive(slack, {
message: "Check for new critical alerts. Report only when there are any.",
target: { channelId: "C0123ABC" },
auth: appAuth,
}),
);
},
});The handler example demonstrates the normal proactive session pattern. Call receive with the channel definition, a message for the agent, a channel-specific target, and an explicit auth context. Then wrap that promise in waitUntil so the cron task's lifetime includes the parked session and any in-flight fetches. Without that lifetime extension, the host could consider the scheduled invocation complete before the asynchronous channel work has settled. For scheduled delivery, waitUntil(receive(...)) should be treated as the standard shape. Sources: docs/schedules.mdx
A scheduled channel hand-off does not require a message on every run. The critical-alerts example asks the agent to report only when alerts exist, and the docs state that eve tells the agent how to finish successfully without sending anything to the channel. This is useful for frequent polling: the cron cadence can stay simple, while the prompt expresses the delivery condition. You do not need a separate filter field in the schedule definition just to avoid noisy empty notifications. Sources: docs/schedules.mdx
Authorization and Safety
Scheduled runs do not inherit a live human identity. The handler receives appAuth, documented as the application principal with authenticator app, principal id eve:app, and principal type runtime. Passing that value to receive starts the target channel session as the runtime application, not as a user. This makes authorization explicit for route protection, multi-tenant boundaries, and audit trails. Downstream tools and channels can inspect the principal and decide whether app-initiated work is allowed for the requested target. Sources: docs/schedules.mdx
Design scheduled side effects as if a cron fire may be retried or resumed by the surrounding durable execution system. The schedules documentation defines how work starts, and eve's broader model runs agent turns as durable workflows. Practically, avoid coupling one schedule fire to one irreversible external action unless that action is idempotent or guarded. Posting a summary may need duplicate protection, billing operations may need an approval boundary, and cleanup tools should tolerate being called again after partial completion. Scheduled automation is powerful precisely because it runs unattended, so its failure behavior deserves review.
Local and Production Operation
Local development intentionally differs from production cadence. The docs state that eve dev never fires schedules on their cron cadence. A built app served with eve start does run production scheduled tasks, and the schedules page points developers to a dispatch route for triggering a schedule while iterating. This separation prevents accidental local polling, duplicate writes, and surprise notifications while a developer is using the interactive environment. Test the prompt and handler deliberately, then validate cadence through the production-like path. Sources: docs/schedules.mdx
A practical development loop starts by choosing the smallest authoring form that matches the job. Write Markdown for self-contained work whose output is disposable, or write a handler when the schedule needs channel delivery or runtime computation. Trigger the schedule intentionally during iteration, inspect logs and target-channel behavior, and confirm that the auth context is what downstream code expects. Before enabling a high-frequency cadence, check timezone assumptions, target identifiers, model and tool cost, and any external rate limits involved in the recurring action.
Next Steps
After adding a schedule, review the channel or tool surface that will receive the work. For proactive channel delivery, confirm the channel module, target object, credentials, and authorization behavior before relying on cron. For task-mode schedules, make sure the prompt can complete without waiting for a person or OAuth flow. Then read the related pages for channels, route protection, durable execution, and approvals so the recurring entry point fits the rest of the agent's operational model.