MainSDK
Search⌘K
Integrations

Inngest

Use Inngest for MainSDK events and background jobs - the default driver, main.yaml, retries, schedules, local dev server, testing and swapping.

2 interfaces2 driversWritten guide
Updated 2026-10-28

Inngest is the default driver behind events and jobs. It runs your background jobs and schedules durably, so jobs.run() and jobs.schedule() survive restarts and retry with backoff. Inngest calls your app over HTTP to run each step, which suits serverless platforms where you have no long-running worker. Pick it unless you already run another queue.

Interfaces it serves

Pick Inngest in main.yaml for any of these. Your calls stay the same, and you can swap later.

Before you start

  1. Create an Inngest account (or run Inngest yourself) and an app for each environment.
  2. Copy the event key (used to send events) and the signing key (used to verify Inngest's calls into your app) and store them in your secrets manager.
  3. Expose the endpoint Inngest calls in your app. How MainSDK mounts that endpoint on a route is not specified yet.

Configure

main.yaml
jobs:
  driver: inngest
  dead-letter: true              # keep jobs that exhausted their retries
idempotency: { store: db, ttl: 24h }
# [to be decided] how the driver takes its keys; suggested names INNGEST_EVENT_KEY and INNGEST_SIGNING_KEY as secret: references

Use it

app/api/reports/route.ts
import { auth, events, jobs } from "mainsdk";

export async function POST(req: Request) {
  const session = await auth.require(req);
  const { month } = await req.json();
  await jobs.run(
    "reports.export",
    { userId: session.user.id, month },
    { idempotencyKey: `export:${session.user.id}:${month}`, retries: 3, backoff: "exponential" },
  );
  await events.publish("report.requested", { month });
  return new Response(null, { status: 202 });
}

Schedule recurring work with jobs.schedule("reports.nightly", "0 3 * * *", { region: "in" }).

Local development and testing

  • Development: use the in-process local queue in a main.env.dev.yaml overlay, or the Inngest Dev Server, which runs on your machine and shows each run. mainsdk check refuses the local queue in production.
  • Tests: createTestApp() from @mainsdk/testing runs jobs on an in-memory fake, clock.advance() plays out retries without waiting, and chaos.on("jobs", { outage: true }) simulates Inngest being down.

What MainSDK adds on top

  • CloudEvents envelope with versioned schemas; a breaking change under the same version fails MAIN-EVENT-0004.
  • Retries with backoff (5 by default, exponential) and a dead-letter queue.
  • A duplicate idempotency key never runs the side effect twice, even after a worker crash.

See Events and background jobs.

Limits and notes

  • The driver comes with the webhooks-jobs spec (phase 1) and is the jobs default.
  • Job inputs are stored by Inngest; keep personal data out of inputs where an ID will do, and check that Inngest's data location fits your region (US, EU or India).
  • Delivery is at least once with idempotent effects; exactly-once delivery is not promised.

Swap to or from it

main.yaml
jobs:
  driver: vercel-queues   # was inngest

FAQ

Do I need a worker process? No. Inngest calls your app's endpoint, so serverless deployments work as they are.

What if I don't pass an idempotency key? One is generated from the action and its inputs.

Next

Open questions

Was this page helpful?

↑↓ moveEnter openEsc close

Not decided yet