Durable Endpoints Beta
Durable Endpoints let you build or transform your API into fault-tolerant endpoints simply by wrapping your critical logic into durable steps.
Durable Endpoints behave like normal endpoints. The mental model stays the same: request, response. But each step brings you tracing, observability, and retry logic from the point of failure.
When to use Durable Endpoints
You have endpoints that fail partway through. Any endpoint with multiple steps where failure at step 3 means steps 1 and 2 were wasted work. Instead of writing try/catch logic everywhere or hoping for the best, simply wrap your code in steps and let failures resume from where they left off.
You want observability without the setup. If you want visibility into your endpoints without configuring a bunch of external services, Durable Endpoints give you that instantly.
You're already using Inngest. You can add durability to other endpoints without refactoring everything into a workflow or thinking heavily about event logic.
Quick Start
If you have a traditional endpoint:
import { NextRequest } from "next/server";
export const POST = async (req: NextRequest) => {
const { userId, data } = await req.json();
const user = await db.users.find(userId);
const enriched = { ...data, account: user.accountId };
const result = await processData(enriched);
await sendNotification(userId, result);
return Response.json({ success: true, result });
};
Create an Inngest client:
import { Inngest } from "inngest";
import { endpointAdapter } from "inngest/next";
const inngest = new Inngest({
id: "my-app",
endpointAdapter,
});
Then, wrap your API endpoint with inngest.endpoint and move your endpoint's critical logic into step.run blocks:
import { step } from "inngest";
import { inngest } from "@/inngest/client";
import { NextRequest } from "next/server";
export const POST = inngest.endpoint(async (req: NextRequest) => {
const { userId, data } = await req.json();
// Step 1: Validate and enrich the data
const enriched = await step.run("enrich-data", async () => {
const user = await db.users.find(userId);
return { ...data, account: user.accountId };
});
// Step 2: Process the enriched data
const result = await step.run("process", async () => {
return await processData(enriched);
});
// Step 3: Send notification
await step.run("notify", async () => {
await sendNotification(userId, result);
});
return Response.json({ success: true, result });
});
If process fails, the endpoint will retry from that step. enrich-data won't re-run.
Read the Durable Endpoint TypeScript SDK Reference for more detailed usage information.
Using Steps
Durable Endpoints support all the same step methods as Inngest functions. See the Steps documentation for the full reference:
step.run(): Reliably execute the provided block by retrying upon failurestep.sleep(): Pause execution for a durationstep.waitForEvent(): Wait for an external event
Requesting a Durable Endpoint
Durable Endpoints behave like regular API endpoints on the success path. You can request them from your front-end (or back-end) using fetch() or your favorite query or http library.
When a failure triggers retries or long-running steps like step.waitForEvent()
are used, a Durable Endpoint redirects to a separate endpoint that waits for the
call to finish.
By default, this is an endpoint either in the Inngest Dev Server or Inngest
Cloud, depending on which environment you're in, but inngest.endpointProxy()
can be used to create your own URL to satisfy CORS constraints when the endpoint
is used from browsers.
// When setting the `endpointAdapter`, use `.withOptions()` to set more config
const inngest = new Inngest({
id: "my-app",
endpointAdapter: endpointAdapter.withOptions({
asyncRedirectUrl: "/wait",
}),
});
// Then create the route with `inngest.endpointProxy()`
Bun.serve({
port: 3000,
routes: {
"/process": ...,
"/wait": inngest.endpointProxy(),
},
});
Requests will now be redirected to /wait.
SDK Support
| SDK | Support | Version |
|---|---|---|
| TypeScript | ✅ Beta | >= 3.x (with endpointAdapter) |
| Go | ✅ | >= v0.14.0 |
Limitations
Durable Endpoints is currently in beta. The following limitations apply:
- Flow control is not supported — Features like concurrency limits and rate limiting are not available for Durable Endpoints
- POST body is not yet supported — Prefer using query strings for passing data. POST body support is coming soon
- Standard HTTP responses only — Durable Endpoints should return a standard HTTP response, not an SSE stream
Examples
The Durable Endpoints example page provides practical pattern examples such as parallel steps.
The following demos are also available to check out and run locally with the Inngest Dev Server:
Explore the full Trip Booker example
Clone this example locally to run it and explore the full source code.
Explore the full DeepResearch demo
Explore a more advanced example with a DeepResearch interface entirely built with Durable Endpoints.