---
title: "Inngest"
description: "Use Inngest for MainSDK events and background jobs - the default driver, main.yaml, retries, schedules, local dev server, testing and swapping."
section: "integrations"
canonical: "https://mainsdk.dev/docs/integrations/inngest"
updated: "2026-10-28"
---

# Inngest

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

Canonical: https://mainsdk.dev/docs/integrations/inngest · Updated: 2026-10-28

Inngest is the default driver behind [`events` and `jobs`](https://mainsdk.dev/docs/interfaces/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

### Durable workflows (`workflows`)

- Status: Preview · ships as a beta API
- Driver: `inngest` (Inngest). later
- Docs: https://mainsdk.dev/docs/interfaces/workflows.md

```yaml
workflows:
  driver: inngest
```

Swap to another provider: `db`, `local`, `temporal`, `vercel-workflow`.

### Events and background jobs (`jobs`)

- Status: Building for 0.3 · ships as a stable API
- Driver: `inngest` (Inngest). not built yet; the webhooks-jobs spec names it
- Docs: https://mainsdk.dev/docs/interfaces/jobs.md

```yaml
jobs:
  driver: inngest
```

Swap to another provider: `db`, `local`, `vercel-queues`, `trigger-dev`, `sqs`.

## 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 [TBD].

## Configure

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

## Use it

```ts title="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](https://mainsdk.dev/docs/interfaces/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

```yaml title="main.yaml"
jobs:
  driver: vercel-queues   # was inngest
```

> **Warning:** The idempotency record is MainSDK's own, so a duplicate key stays a duplicate across the swap. What happens to jobs already queued or scheduled on Inngest is not specified yet [TBD]; let them drain before you switch.

## 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

- [Events and background jobs](https://mainsdk.dev/docs/interfaces/jobs) and [Webhooks](https://mainsdk.dev/docs/interfaces/webhooks)
- [Trigger.dev](https://mainsdk.dev/docs/integrations/trigger-dev), [Amazon SQS](https://mainsdk.dev/docs/integrations/amazon-sqs) and [Vercel](https://mainsdk.dev/docs/integrations/vercel)
