Skip to main content
Advanced Options are intended to support edge cases or testing pipelines and are not required for regular use.
failureFunction
string
Defines a function that executes if the workflow fails after all retries are exhausted.For details, see failureFunction.
When adding a failureFunction, you must set useFailureFunction: true in client.trigger() when starting a workflow run.
export const { POST } = serve<string>(
  async (context) => { ... },
  {
    failureFunction: async ({
      context,      // context during failure
      failStatus,   // failure status
      failResponse, // failure message
      failHeaders   // failure headers
      failStack.    // failure stack trace (if available)
    }) => {
      // handle the failure
    }
  }
);
failureUrl
string
The failureUrl option defines an external endpoint that will be called if the workflow fails after all retries are exhausted.This option is an advanced alternative to failureFunction. For more details, see Advanced failureUrl Option.
When adding a failureUrl, you must set failureUrl in client.trigger() when starting a workflow run.
export const { POST } = serve<string>(
  async (context) => { ... },
  {
    failureUrl: "https://<YOUR-FAILURE-ENDPOINT>/..."
  }
);
retries
number
Defines the number of retry attempts if a workflow step fails. The default value is 3.For details, see retry configuration.
We recommend configuring workflow runs when starting them with client.trigger(), rather than applying configuration on the server side.See Configure a Run for details.
export const { POST } = serve<string>(
  async (context) => { ... },
  {
    retries: 3
  }
);
retryDelay
string
Defines the delay between retry attempts. This option accepts an expression that evaluates to the number of milliseconds.You can use the retried variable—which starts at 0 for the first retry—to compute a dynamic delay. For a constant delay, provide a fixed millisecond value.For details, see retry configuration.
We recommend configuring workflow runs when starting them with client.trigger(), rather than applying configuration on the server side.See Configure a Run for details.
export const { POST } = serve<string>(
  async (context) => { ... },
  {
    retryDelay: "(retried + 1) * 1000" // delay in milliseconds
  }
);
flowControl
object
Applies throttling to workflow execution using rate limits or concurrency limits.See flow control for details.
We recommend configuring workflow runs when starting them with client.trigger(), rather than applying configuration on the server side.See Configure a Run for details.
export const { POST } = serve<string>(
  async (context) => { ... },
  {
    flowControl: { key: "custom-flow-control-key",  rate: 10, parallelism: 3 }
  }
);
initialPayloadParser
bool
Enables custom parsing of the initial request payload.Use this option if the incoming payload is not plain JSON or a simple string. The parser function lets you transform the raw request into a strongly typed object before workflow execution begins.
type InitialPayload = {
  foo: string;
  bar: number;
};

// 👇 1: provide initial payload type
export const { POST } = serve<InitialPayload>(
  async (context) => {
    // 👇 3: parsing result is available as requestPayload
    const payload: InitialPayload = context.requestPayload;
  },
  {
    // 👇 2: custom parsing for initial payload
    initialPayloadParser: (initialPayload) => {
      const payload: InitialPayload = parsePayload(initialPayload);
      return payload;
    },
  }
);
url
string
Specifies the full endpoint URL of the workflow, including the route path.By default, Upstash Workflow infers the URL from request.url when scheduling the next step. However, in some environments, request.url may resolve to an internal or unreachable address.Use this option when running behind a proxy, reverse proxy, or local tunnel during development where request.url cannot be used directly.
export const { POST } = serve<string>(
  async (context) => { ... },
  {
    url: "https://<YOUR-DEPLOYED-APP>.com/api/workflow"
  }
);
baseUrl
string
Similar to url, but baseUrl only overrides the base portion of the inferred URL rather than replacing the entire path. This is useful when you want to preserve the route structure while changing only the host or scheme.
If you have multiple workflow endpoints, you can set the UPSTASH_WORKFLOW_URL environment variable instead of configuring baseUrl on each endpoint. The UPSTASH_WORKFLOW_URL environment variable corresponds directly to this option and configures it globally.
export const { POST } = serve<string>(
  async (context) => {
    ...
  },
  // options:
  {
    baseUrl: "<LOCAL-TUNNEL-PUBLIC-URL>"
  }
);
qstashClient
object
Use qstashClient if you want to provide your own QStash client instead of letting Workflow use the default from environment variables.This is useful if you’re working with multiple QStash projects in the same app.
import { Client } from "@upstash/qstash";
import { serve } from "@upstash/workflow/nextjs";

export const { POST } = serve(
  async (context) => { ... },
  {
    qstashClient: new Client({ token: "<QSTASH_TOKEN>" })
  }
);
receiver
object
The Receiver verifies that every request to your endpoint actually comes from QStash, blocking anyone else from triggering your workflow.The receiver option allows you to pass a QStash Receiver explicitly.By default, Workflow initializes the Receiver automatically using the environment variables QSTASH_CURRENT_SIGNING_KEY and QSTASH_NEXT_SIGNING_KEY.This is useful if you’re working with multiple QStash projects in the same app.
import { Receiver } from "@upstash/qstash";
import { serve } from "@upstash/workflow/nextjs";

export const { POST } = serve<string>(
  async (context) => { ... },
  {
    receiver: new Receiver({
      currentSigningKey: "<QSTASH_CURRENT_SIGNING_KEY>",
      nextSigningKey: "<QSTASH_NEXT_SIGNING_KEY>",
    })
  }
);
env
object
By default, Workflow uses process.env to read credentials and initialize QStash. If you’re in an environment where process.env isn’t available, or you want to inject values manually, you can pass them with env.Inside your workflow, these values are also exposed on context.env.
import { Receiver } from "@upstash/qstash";
import { serve } from "@upstash/workflow/nextjs";

export const { POST } = serve<string>(
  async (context) => {
    // the env option will be available in the env field of the context:
    const env = context.env;
  },
  {
    env: {
        QSTASH_URL: "<QSTASH_URL>",
        QSTASH_TOKEN: "<QSTASH_TOKEN>",
        QSTASH_CURRENT_SIGNING_KEY: "<QSTASH_CURRENT_SIGNING_KEY>",
        QSTASH_NEXT_SIGNING_KEY: "<QSTASH_NEXT_SIGNING_KEY>",
    }
  }
);
verbose
boolean
Enables verbose mode to print detailed logs of workflow execution to the application’s stdout.Verbose mode is disabled by default.
export const { POST } = serve<string>(
  async (context) => { ... },
  {
    verbose: true
  }
);
Each log entry has the following structure:
{
  timestamp: number,
  workflowRunId: string,
  logLevel: string,
  eventType: string,
  details: unknown,
}
eventTypeDescription
ENDPOINT_STARTeach time the workflow endpoint is called
RUN_SINGLE or RUN_PARALLELwhen step(s) are executed
SUBMIT_STEPwhen a single step is executed
SUBMIT_FIRST_INVOCATIONwhen a new workflow run starts
SUBMIT_CLEANUPwhen a workflow run finishes
SUBMIT_THIRD_PARTY_RESULTwhen a third-party call result is received (see context.call)