Scheduled jobs
Run background work on a schedule — digests, cleanups, syncs. You write named job handlers in api/cron.js, and schedule them with the cron MCP tools. Each job runs with the same env bindings as your routes.
Define handlers in api/cron.js
api/cron.js is a special file (not an HTTP route). Default-export a map of job name → handler. Handlers receive (env, ctx):
// api/cron.js
export default {
async daily_digest(env, ctx) {
const { results } = await env.DB
.prepare("SELECT email FROM subscribers WHERE digest = 1")
.all();
for (const row of results) {
await env.EMAIL.send({
to: row.email,
subject: "Your daily digest",
html: "<p>Here's what's new…</p>",
});
}
},
async cleanup_temp(env, ctx) {
// …
},
};An array of { name, handler } objects works too — use whichever reads better.
Schedule jobs with the MCP tools
set_cron_job— create or update a schedule, pointing at a job name fromapi/cron.js.list_cron_jobs— see an app's scheduled jobs.run_cron_job— trigger a job immediately (handy for testing).delete_cron_job— stop a schedule. (The handler stays inapi/cron.jsuntil you remove it.)
In practice you just ask your assistant: “run daily_digest every morning at 7am” — it writes the handler and calls set_cron_job.
Limits
The number of jobs per app, and whether you can schedule more often than once a day, depend on your plan — see Limits & quotas.
Jobs run against your production bindings. Use
run_cron_job to dry-run a job on demand before relying on the schedule.