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:
However, when a failure triggers retries, a Durable Endpoint returns a redirect to a dedicated endpoint on Inngest Cloud to poll the final result.
Here is a snippet handling both the direct result and the redirected result after retries:
function handleError(error) {
// ...
}
async function handleResult(result) {
const result = await res.json()
// ...
}
fetch(`/api/your-durable-endpoint`)
.then((res) => {
if (res.redirected) {
// follow the redirect
fetch(res.url)
.then(handleResult)
.catch(handleError);
} else {
handleResult(res)
}
})
.catch(handleError);
As the Durable Endpoint redirects the request to a dedicated endpoint on Inngest's Cloud, fetch() cannot simply follow this redirect for you (CORS policy).
Instead, you need to get the redirect URL (res.url) and trigger a new fetch() request.
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.