---
name: convex-cron-38d4f2ac8f42
description: Scheduled functions and cron jobs in Convex. Use when setting up recurring tasks, cleanup jobs, data syncing, scheduled notifications, or any background automation that runs on a schedule.
---

# Convex Cron Jobs

## Basic Cron Setup

```typescript
// convex/crons.ts
import { cronJobs } from "convex/server";
import { internal } from "./_generated/api";

const crons = cronJobs();

// Run every hour
crons.interval(
  "cleanup expired sessions",
  { hours: 1 },
  internal.tasks.cleanupExpiredSessions,
  {}
);

// Run every day at midnight UTC
crons.cron(
  "daily report",
  "0 0 * * *",
  internal.reports.generateDailyReport,
  {}
);

export default crons;
```

## Interval-Based Scheduling

```typescript
// Every 5 minutes
crons.interval("sync data", { minutes: 5 }, internal.sync.fetchData, {});

// Every 2 hours
crons.interval("cleanup temp", { hours: 2 }, internal.files.cleanup, {});

// Every 30 seconds (minimum)
crons.interval("health check", { seconds: 30 }, internal.monitoring.check, {});
```

## Cron Expression Scheduling

```
┌───────────── minute (0-59)
│ ┌───────────── hour (0-23)
│ │ ┌───────────── day of month (1-31)
│ │ │ ┌───────────── month (1-12)
│ │ │ │ ┌───────────── day of week (0-6, Sunday=0)
* * * * *
```

```typescript
// Every day at 9 AM UTC
crons.cron("morning digest", "0 9 * * *", internal.notifications.morning, {});

// Every Monday at 8 AM UTC
crons.cron("weekly summary", "0 8 * * 1", internal.reports.weekly, {});

// First day of month at midnight
crons.cron("monthly billing", "0 0 1 * *", internal.billing.process, {});

// Every 15 minutes
crons.cron("frequent sync", "*/15 * * * *", internal.sync.run, {});
```

## Internal Functions for Crons

Always use internal functions:

```typescript
// convex/tasks.ts
import { internalMutation } from "./_generated/server";
import { v } from "convex/values";

export const cleanupExpiredSessions = internalMutation({
  args: {},
  returns: v.number(),
  handler: async (ctx) => {
    const oneHourAgo = Date.now() - 60 * 60 * 1000;

    const expired = await ctx.db
      .query("sessions")
      .withIndex("by_lastActive")
      .filter((q) => q.lt(q.field("lastActive"), oneHourAgo))
      .collect();

    for (const session of expired) {
      await ctx.db.delete(session._id);
    }

    return expired.length;
  },
});
```

## Crons with Arguments

```typescript
crons.interval(
  "cleanup temp files",
  { hours: 1 },
  internal.cleanup.byType,
  { fileType: "temp", maxAge: 3600000 }
);

crons.interval(
  "cleanup cache files",
  { hours: 24 },
  internal.cleanup.byType,
  { fileType: "cache", maxAge: 86400000 }
);
```

## Batching Large Datasets

Handle large datasets to avoid timeouts:

```typescript
const BATCH_SIZE = 100;

export const processBatch = internalMutation({
  args: { cursor: v.optional(v.string()) },
  returns: v.null(),
  handler: async (ctx, args) => {
    const result = await ctx.db
      .query("items")
      .withIndex("by_status", (q) => q.eq("status", "pending"))
      .paginate({ numItems: BATCH_SIZE, cursor: args.cursor ?? null });

    for (const item of result.page) {
      await ctx.db.patch(item._id, { status: "processed", processedAt: Date.now() });
    }

    // Schedule next batch if more items
    if (!result.isDone) {
      await ctx.scheduler.runAfter(0, internal.tasks.processBatch, {
        cursor: result.continueCursor,
      });
    }
    return null;
  },
});
```

## External API Calls

Use actions for external APIs:

```typescript
// convex/sync.ts
"use node";

import { internalAction, internalMutation } from "./_generated/server";
import { internal } from "./_generated/api";
import { v } from "convex/values";

export const syncExternalData = internalAction({
  args: {},
  returns: v.null(),
  handler: async (ctx) => {
    const response = await fetch("https://api.example.com/data", {
      headers: { Authorization: `Bearer ${process.env.API_KEY}` },
    });

    const data = await response.json();

    await ctx.runMutation(internal.sync.storeData, { data, syncedAt: Date.now() });
    return null;
  },
});

// In crons.ts
crons.interval("sync external", { minutes: 15 }, internal.sync.syncExternalData, {});
```

## Logging and Monitoring

```typescript
export const cleanupWithLogging = internalMutation({
  args: {},
  returns: v.null(),
  handler: async (ctx) => {
    const startTime = Date.now();
    let processedCount = 0;

    try {
      const items = await ctx.db.query("items")
        .filter((q) => q.lt(q.field("expiresAt"), Date.now()))
        .collect();

      for (const item of items) {
        await ctx.db.delete(item._id);
        processedCount++;
      }

      await ctx.db.insert("cronLogs", {
        jobName: "cleanup",
        duration: Date.now() - startTime,
        processedCount,
        status: "success",
      });
    } catch (error) {
      await ctx.db.insert("cronLogs", {
        jobName: "cleanup",
        duration: Date.now() - startTime,
        processedCount,
        status: "failed",
        error: String(error),
      });
      throw error;
    }
    return null;
  },
});
```

## Common Pitfalls

- **Using public functions** - Always use internal functions
- **Forgetting timezone** - All cron expressions use UTC
- **Long-running mutations** - Break into batches
- **Missing error handling** - Log failures for debugging

## References

- Cron Jobs: https://docs.convex.dev/scheduling/cron-jobs
- Scheduling: https://docs.convex.dev/scheduling


---
Source attribution: aaronvanston
Original project: https://github.com/aaronvanston/skills-convex/blob/5ea8c54ce73b814da9d50d384765c1501c76d999/skills/convex-cron/SKILL.md
License: MIT
License information: https://opensource.org/license/mit
Original upstream instructions; downloaded from Mega AI Hub.

## Upstream license notice

MIT License

Copyright (c) 2026

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.



# Mega AI Hub resource metadata

Creator / publisher: aaronvanston
Original project: https://github.com/aaronvanston/skills-convex/blob/5ea8c54ce73b814da9d50d384765c1501c76d999/skills/convex-cron/SKILL.md
Attribution basis: Source repository record

Fields of work: Development & Engineering
Roles: Backend Developer, Data Engineer, DevOps / SRE
Specialties: Backend
Tools: Convex
Country applicability: Country not specified
Classification basis: Source catalog labels
Instruction SHA-256: 7fba5a9002d0385ef99b4fb3a0302015820aba0dd6c946342d2bd9e6e64d9bef
