# 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](/docs-markdown/durable-execution/durable-workflows):** Either a workflow or endpoint that runs a series of steps.
- **[Trigger](/docs-markdown/durable-execution/guides-and-advanced/events-and-triggers):** How a function starts. This may be an event, a schedule, an HTTP request, or an invocation through the REST API.
- **[Steps](/docs-markdown/durable-execution/primitives):** Units of work whose outputs Inngest saves. A run can continue after a failed step without repeating completed steps.
  - **[Waits](/docs-markdown/durable-execution/primitives):** A step can wait until a time or another event. Waits suspend the function's compute and do not count toward concurrency.
- **[Memoization](/docs-markdown/durable-execution/durable-workflows):** Serializing and storing a step output for use when the function replays.
- **[Idempotency](/docs-markdown/durable-execution/guides-and-advanced/idempotency):** An operation that has the same effect when repeated, such as a payment request that does not charge the card twice.
- **[Expressions](/docs-markdown/durable-execution/guides-and-advanced/writing-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](/docs-markdown/durable-execution/flow-control):** Rules for if and when functions run, such as concurrency, throttling, rate limiting, and debounce.
- **[Errors vs failures](/docs-markdown/durable-execution/guides-and-advanced/error-handling/retries):** Inngest can retry errors. A step or function fails if every retry results in an error.

## Additional concepts

- **[Apps](/docs-markdown/platform-and-operations/apps-and-syncs):** An app is a service that serves a set of functions.
- **[Syncs](/docs-markdown/platform-and-operations/apps-and-syncs):** You register an app and its functions with Inngest so it knows which functions run for each trigger.
- **[Traces](/docs-markdown/platform-and-operations/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](/docs-markdown/durable-execution/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](/docs-markdown/durable-execution/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.](/docs-markdown/durable-execution/guides-and-advanced/events-and-triggers)

**Steps**: [Inngest saves completed results so a retry can continue without repeating those callbacks.](/docs-markdown/durable-execution/primitives)

**Flow control**: [Add rules when you need to limit starts or protect a service that the workflow calls.](/docs-markdown/durable-execution/flow-control)

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

```typescript
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;
  }
);
```

```python
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"])

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

```go
type TaskData struct {
    ID string `json:"id"`
}

type TaskEvent = inngestgo.GenericEvent[TaskData]

func registerFunction(client inngestgo.Client) error {
    _, err := inngestgo.CreateFunction(
        client,
        inngestgo.FunctionOpts{ID: "process-task"},
        inngestgo.EventTrigger("app/task.created", nil),
        func(ctx context.Context, input inngestgo.Input[TaskEvent]) (map[string]any, error) {
            result, err := step.Run(ctx, "handle-task", func(ctx context.Context) (map[string]any, error) {
                return map[string]any{"processed": true, "id": input.Event.Data.ID}, nil
            })
            if err != nil {
                return nil, err
            }

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

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.](/docs-markdown/durable-execution/durable-workflows)

**Delayed work**: [Start work at a future time or pause a run until it should continue.](/docs-markdown/durable-execution/guides-and-advanced/events-and-triggers/schedules-and-delayed-starts)

**Scheduled work**: [Trigger recurring work from a cron schedule.](/docs-markdown/durable-execution/guides-and-advanced/events-and-triggers/schedules-and-delayed-starts)

**Multi-step workflows**: [Save progress between operations so a failed step does not repeat completed steps.](/docs-markdown/durable-execution/primitives)

### Explore both durable function types

**Durable workflows**: [Coordinate background work started by an event, schedule, webhook, or another function.](/docs-markdown/durable-execution/durable-workflows)

**Durable endpoints**: [Keep the HTTP request and response model while Inngest recovers failed steps.](/docs-markdown/durable-execution/durable-endpoints)

### Build it in your language

**TypeScript**: [Create and inspect this workflow with TypeScript SDK v4.](/docs-markdown/durable-execution/quick-start/typescript-quick-start)

**Python**: [Create and inspect this workflow with the Python SDK.](/docs-markdown/durable-execution/quick-start/python-quick-start)

**Go**: [Create and inspect this workflow with the Go SDK.](/docs-markdown/durable-execution/quick-start/go-quick-start)