Announcing our new Vercel integration
Join our Discord
Sign up for free

Step functions

Inngest allows you to extend functions with multiple steps. Each step within a function is its own individual unit of code. They can be written in any language, are retried independently on intermittent external failures, and many steps can run in parallel.

This documentation introduces high level step-function features, example step function configuration, and advanced use cases.

High level features

  • Each step is an individual unit of code, written in any language
  • Steps are retried independently of each other, on external failures
  • Steps can run in parallel
  • Each step can access output from prior steps
  • Steps can pause functions until specific matching events are received
  • Steps can be re-used, shared, and composed between functions, eg. for sending emails or SMS messages

Example confifugration

This configuration shows a basic function with two steps running in sequence. The first step runs immediately after the function is triggered. The second step runs after the first step has successfully completed.

"name": "test",
"id": "improved-chamois-c592a7",
"triggers": [
"event": "github/delete",
"definition": {
"format": "cue",
"synced": true,
"def": "file://./events/github-delete.cue"
"steps": {
"step-1": {
"id": "step-1",
"path": "file://./steps/step-1",
"name": "test",
"runtime": {
"type": "docker"
"after": [{
This step runs automatically at the start of the function when it's triggered. When "after" is ommitted, the step automatically runs immediately after starting the function.
"step": "$trigger"
This second step will run after the first step completes, in sequence. It can read all of the first step's response within the arguments passed to the step.
"step-2": {
"id": "step-2",
"path": "file://./steps/step-2",
"name": "test",
"runtime": {
"type": "docker"
"after": [{
"step": "step-1"

In this example, the second step can access the event data and the output from the first step's execution. If the first step replies with our suggested response, the second step will receive the following:

"event": { ... } // Event data
"steps": {
"step-1": { ... } // The output of step 1

After configuration

The full spec for the after field is defined within the function spec here:

#After: {
// step is the step that must complete in order to run the next step,
// or the "$trigger" if this step runs immediately.
step: string | "$trigger"
// if defines an expression which must evaluate to true in order to run
// the next step.
if?: string
// wait allows you to delay a step from running for a set amount of time, eg.
// to delay a step from running you can set wait to "10m". This will enqueue
// the step to run after 10 minutes.
wait?: string
// async allows you to specify an event that must be received within a specific
// amount of time (ttl) to continue with the specified step.
async?: {
// event specifies the event name that must be received in order to
// continue an event
event: string
// ttl is the maximum wait time to wait for the event to be received to
// continue the function
ttl: string
// match defines an expression that must evaluate to true, using the async
// event data, in order continue the function
match?: string
// onTimeout specifies that this step should be executed on timeout only,
// if the event is not received within the TTL.
onTimeout?: bool


You can specify an expression which must evaluate to true in order for the step to be invoked.


You can specify that steps should run after a wait, eg. to schedule a step to execute 10 minutes after the trigger you can specify "wait": "10m" within the after JSON block.


You can also pause functions in between steps by specifing an async block. The async block halts a function, sets up a temporary trigger which listens for the specified event, and automatically resumes the function when a matching event is received.

Using this functionality it's possible to easily build flows that model complex user journeys, such as retention flows, drip campaigns, etc.

Adding steps to functions

To add a step to a function, create a new JSON block for each step within the steps section of your function's configuration file, with path pointing to the step's code.

Coming soon, you'll be able to use the CLI to automatically scaffold new steps within your function using our library of scaffolds.