TL;DR:
- Inngest provides durable execution for AI agent workflows — steps persist state, retry on failure, and survive process restarts without you managing queues or workers
- The
step.run,step.sleep, andstep.waitForEventprimitives make it easy to build reliable multi-step AI pipelines in TypeScript or Python - The
useAgenthook streams durable AI workflow progress to the frontend in real time, making it suitable for user-facing agentic applications
Running AI agents in production exposes a problem that traditional web request/response architectures weren’t designed for: agent tasks are long, stateful, and expensive to restart from scratch. A five-minute document analysis pipeline that fails halfway through shouldn’t replay the LLM calls that already succeeded. An agent that needs to wait for a human approval shouldn’t hold a server process open. A batch processing agent that handles 500 items needs to distribute work and handle partial failures cleanly.
Inngest is an event-driven durable execution platform that addresses these constraints directly. Unlike full-featured orchestration platforms like Temporal, Inngest is designed to be dropped into existing TypeScript or Python backends — Next.js, Express, FastAPI — with minimal infrastructure setup and no separate workers to manage.
The Core Primitive: Step Functions
Inngest functions are composed of steps. Each step is a discrete unit of work with its own retry configuration. When a step completes, Inngest persists the result. If the function fails later, it resumes from the last completed step rather than replaying everything.
import { inngest } from "./inngest-client";
export const analyzeDocument = inngest.createFunction(
{ id: "analyze-document", retries: 3 },
{ event: "document/uploaded" },
async ({ event, step }) => {
// Step 1: Extract text — persisted on completion
const text = await step.run("extract-text", async () => {
return await extractTextFromPDF(event.data.documentUrl);
});
// Step 2: Classify document type — only runs if step 1 succeeded
const classification = await step.run("classify-document", async () => {
return await llm.classify(text);
});
// Step 3: Generate summary — uses cached classification
const summary = await step.run("generate-summary", async () => {
return await llm.summarize(text, classification);
});
return { classification, summary };
}
);
If the generate-summary step fails after retries, the next retry run skips extract-text and classify-document entirely — their outputs are replayed from Inngest’s state store. You get at-most-once LLM call semantics per step without implementing that logic yourself.
Sleeping and Waiting Without Holding Processes
Agents frequently need to pause — for rate limits, human approvals, webhook callbacks, or scheduled follow-ups. Inngest handles this with step.sleep and step.waitForEvent:
// Pause for 30 minutes — no process held open
await step.sleep("rate-limit-pause", "30m");
// Wait up to 24 hours for a human to approve
const approval = await step.waitForEvent("human-approval", {
event: "approval/received",
match: "data.taskId",
timeout: "24h",
});
if (!approval) {
// Timed out — handle the case
return { status: "timed-out" };
}
The function suspends entirely during the wait period, consuming no server resources. When the matching event arrives or the timeout expires, Inngest resumes execution from that point.
This makes human-in-the-loop patterns straightforward: trigger an approval request, wait for the event, branch on the result.
AgentKit and the useAgent Hook
Inngest’s AgentKit provides higher-level primitives for building AI agents, including the useAgent React hook that streams durable workflow state to the frontend in real time:
// Server: define the agent
export const researchAgent = inngest.createFunction(
{ id: "research-agent" },
{ event: "research/started" },
async ({ event, step }) => {
const { runId } = event.data;
const queries = await step.run("plan-research", () =>
llm.planQueries(event.data.topic)
);
const results = await step.run("run-searches", () =>
Promise.all(queries.map(q => searchWeb(q)))
);
return await step.run("synthesize", () =>
llm.synthesize(results)
);
}
);
// Client: stream progress
function ResearchPanel({ runId }: { runId: string }) {
const { steps, isRunning, result } = useAgent({ runId });
return (
<div>
{steps.map(step => (
<StepStatus key={step.id} step={step} />
))}
{result && <ResearchResult data={result} />}
</div>
);
}
Users see each step as it completes, with the final result appearing when the agent finishes. This is considerably better UX than a spinner with no feedback during a multi-minute agent run.
Deployment and Setup
Inngest works by serving a single HTTP endpoint from your existing backend that Inngest’s cloud (or self-hosted version) communicates with. There’s no separate worker process to deploy:
// Next.js App Router
import { serve } from "inngest/next";
import { inngest } from "../../inngest/client";
import { analyzeDocument, researchAgent } from "../../inngest/functions";
export const { GET, POST, PUT } = serve({
client: inngest,
functions: [analyzeDocument, researchAgent],
});
In development, the Inngest Dev Server runs locally and provides a visual dashboard of function runs, step outputs, and replays:
npx inngest-cli@latest dev
The dashboard is the main debugging interface — you can inspect every step’s input and output, replay failed runs manually, and see the full execution timeline.
When Inngest Makes Sense for AI Workloads
Inngest is a good fit when:
- You’re already on a serverless or managed backend (Next.js, Vercel, Render, Railway) and don’t want to add Kubernetes or manage Temporal workers
- Your agent tasks run for more than a few seconds and benefit from step-level persistence and retry granularity
- You need human-in-the-loop patterns with minutes or hours of wait time
- You want streaming progress to the frontend without building your own WebSocket or SSE infrastructure
It’s less suited to scenarios requiring very high throughput event processing (thousands of events per second), complex multi-agent networks with sophisticated coordination, or workloads that already have Temporal or Kafka in the stack.
Pricing and Limits
Inngest’s free tier includes 50,000 function runs per month with up to 1,000 concurrent steps. The paid plans start at $50/month for 500,000 runs. There’s also an open-source self-hosted version for teams that need to keep data on-premises.
For most teams building AI features in Next.js or similar frameworks, Inngest solves the background job and durable execution problem with minimal operational overhead — it’s worth evaluating before reaching for heavier orchestration platforms.