TypeScript SDK v4 is now available! See what's new
Scheduling

Running code at specific times

Schedule one-off work for appointment reminders, off-peak AI maintenance, embargoed publishing, and user-picked send times.

It's common to need to run code at a specific time. You might send appointment reminders 10 minutes before the event, publish blog posts at a scheduled time, compact a vector store index at 2am, or prune stale agent sessions during off-peak hours.

Job systems allow you to specify the time that a code should run when you enqueue the job. Within Inngest, jobs run automatically when an event is received. How can you schedule work to run at a specific time?

The answer is: pass the date and time that you want the event to run at within the event itself. Within your function, you can sleep or sleepUntil the date within the event.

§How this works

In order to run code at a specific time based off of event data, first make sure that you send your desired time in the event:

typescript
01import { Inngest } from "inngest";
02
03// Create a new client
04const inngest = new Inngest({ id: "API" });
05
06// Send an event with the time to schedule a blog post in the event data.
07await inngest.send({
08 name: "blog/post.scheduled",
09 data: {
10 scheduleAt: "2023-04-01T12:30:00.000Z",
11 postID: "ed6f81",
12 },
13});

Then, within your function, you can pause until the time in the event:

typescript
01import { inngest } from "./client";
02
03const delayed = inngest.createFunction(
04 { id: "schedule-post", triggers: [{ event: "blog/post.scheduled" }] },
05 async ({ event, step }) => {
06 const at = new Date(event.data.scheduleAt);
07
08 // Use the built-in `sleepUntil` tool to sleep until the time in the event.
09 // This function will pause then resume running the code below at the given time.
10 await step.sleepUntil("wait-for-scheduled", at);
11
12 await step.run("publish-post", () => {
13 // Any code here runs at the time in the event.
14 schedulePost(event.data.postID);
15 });
16 }
17);

This function runs immediately when the blog/post.scheduled event is received, then pauses until the time and date within the event. This lets you create a single function that can schedule work at any time using data from the event, intsead of hard-coding times within your code.

You can also cancel these scheduled functions using the cancellation guide.

§Alternative approaches

  • Scheduled queue jobs. Most job queues (BullMQ, Sidekiq, SQS with delay queues) accept a runAt or delay parameter when enqueueing. Works for delays under ~15 minutes; provider-specific limits kick in beyond that, and your jobs hold queue resources the whole time they wait.
  • Cron + a database table. Run a sweeper cron that polls a scheduled_jobs table for due work. Simple, but the cron is now the bottleneck (see Reliable scheduling systems) and "due" is rounded to the cron interval.
  • Inline setTimeout. Works locally; gets erased the moment your server restarts. Don't.

The pattern above (emit an event with a future timestamp, then sleepUntil inside a durable function) keeps the wait state in Inngest rather than your queue, and survives deploys without re-architecting anything.

§Additional Resources