Inngest

Docs › Durable execution

Concepts

See how triggers, runs, steps, and waits let your code continue after a failure or a long pause.

Durable execution uses the same concepts for both workflows and endpoints. Learn the concepts before exploring how to build with Inngest.

Example

A payment may need approval before your app can charge a card. Inngest can start the work now, pause it while approval is pending, and continue it later. The original server process does not need to continue running while the function pauses.

Every durable function has the following pattern: trigger → run → steps → result.

Base concepts

  • Function: Either a workflow or endpoint that runs a series of steps.
  • Trigger: How a function starts. This may be an event, a schedule, an HTTP request, or an invocation through the REST API.
  • Steps: Units of work whose outputs Inngest saves. A run can continue after a failed step without repeating completed steps.
    • Waits: A step can wait until a time or another event. Waits suspend the function's compute and do not count toward concurrency.
  • Memoization: Serializing and storing a step output for use when the function replays.
  • Idempotency: An operation that has the same effect when repeated, such as a payment request that does not charge the card twice.
  • Expressions: CEL conditions that can add logic to triggers and waits.
  • Results: The data that a function returns and Inngest stores as its result.
  • Flow control: Rules for if and when functions run, such as concurrency, throttling, rate limiting, and debounce.
  • Errors vs failures: Inngest can retry errors. A step or function fails if every retry results in an error.

Additional concepts

  • Apps: An app is a service that serves a set of functions.
  • Syncs: You register an app and its functions with Inngest so it knows which functions run for each trigger.
  • Traces: Show the path a function takes across steps, waits, retries, and the result.
  • Lifecycles: A function transitions from queued to running to resolved, either completed or failed. Lifecycles run for each status transition.

Follow the quick start to see these concepts in a working function.


See a durable workflow

This example shows a workflow: an event starts background work, a step saves a result, and a wait pauses the run. Durable Endpoints apply the same step model to an HTTP request.

The parts of a workflow

Triggers

An event, schedule, webhook, or another function starts a run.

inngest.com
Steps

Inngest saves completed results so a retry can continue without repeating those callbacks.

inngest.com
Flow control

Add rules when you need to limit starts or protect a service that the workflow calls.

inngest-kb.zunoywork.com

The code

Each snippet defines a process-task function triggered by app/task.created. The step returns the task ID, then the run pauses for one second before returning a result. The snippets assume you have created an Inngest client; the quick starts show client setup and how to serve the function.

export const processTask = inngest.createFunction(
  { id: "process-task", triggers: { event: "app/task.created" } },
  async ({ event, step }) => {
    const result = await step.run("handle-task", () => ({
      processed: true,
      id: event.data.id,
    }));

    await step.sleep("pause", "1s");
    return result;
  }
);
import datetime

import inngest

@inngest_client.create_function(
    fn_id="process-task",
    trigger=inngest.TriggerEvent(event="app/task.created"),
)
async def process_task(ctx: inngest.Context) -> dict[str, object]:
    task_id = str(ctx.event.data["id"])

    async def handle_task() -> dict[str, object]:
        return {"processed": True, "id": task_id}

    result = await ctx.step.run("handle-task", handle_task)
    await ctx.step.sleep("pause", datetime.timedelta(seconds=1))
    return result
type TaskData struct {
	ID string `json:"id"`
}

type TaskResult struct {
	Processed bool   `json:"processed"`
	ID        string `json:"id"`
}

func ProcessTask(client inngestgo.Client) (inngestgo.ServableFunction, error) {
	return inngestgo.CreateFunction(
		client,
		inngestgo.FunctionOpts{ID: "process-task"},
		inngestgo.EventTrigger("app/task.created", nil),
		func(ctx context.Context, input inngestgo.Input[TaskData]) (any, error) {
			result, err := step.Run(ctx, "handle-task", func(ctx context.Context) (TaskResult, error) {
				return TaskResult{Processed: true, ID: input.Event.Data.ID}, nil
			})
			if err != nil {
				return nil, err
			}

			step.Sleep(ctx, "pause", time.Second)
			return result, nil
		},
	)
}

Inngest saves the handle-task result. If later work retries, the SDK returns that result instead of running the completed callback again. The one-second sleep pauses the run without keeping a process open. Code outside a step can run again when the function replays, so keep external side effects in steps and make retried operations safe to repeat.

What you can build

Background jobs

Move slow work out of an HTTP request and let the run continue after the response.

inngest-kb.zunoywork.com
Delayed work

Start work at a future time or pause a run until it should continue.

inngest.com
Scheduled work

Trigger recurring work from a cron schedule.

inngest.com
Multi-step workflows

Save progress between operations so a failed step does not repeat completed steps.

inngest.com

Explore both durable function types

Durable workflows

Coordinate background work started by an event, schedule, webhook, or another function.

inngest-kb.zunoywork.com
Durable endpoints

Keep the HTTP request and response model while Inngest recovers failed steps.

inngest.com

Build it in your language

TypeScript

Create and inspect this workflow with TypeScript SDK v4.

inngest.com
Python

Create and inspect this workflow with the Python SDK.

inngest.com
Go

Create and inspect this workflow with the Go SDK.

inngest.com

Updated Nov 10, 2026

Powered byZunoy