Skip to main content
To prevent unauthorized access to your workflow endpoint, you can add an authorization layer. Upstash Workflow supports two approaches:
  • Built-in request verification (recommended)
  • Custom authorization method
Upstash Workflow provides a built-in mechanism to secure your workflow endpoint by verifying request signatures. Every request to your endpoint include a valid Upstash-Signature header. How it works:
  1. Upstash Workflow automatically adds the Upstash-Signature header to every request. This signature is generated using your signing keys.
  2. When this mechanism is enabled, the SDK verifies that the signature is valid before processing the request.
This ensures that only requests originating from Upstash Workflow are processed. To enable this verification, set the following environment variables in your application:
.env
QSTASH_CURRENT_SIGNING_KEY=xxxxxxxxx
QSTASH_NEXT_SIGNING_KEY=xxxxxxxxx
You can find the values in Upstash Workflow dashboard.
For edge cases where environment variables cannot be used, you can explicitly create and pass a Receiver object to verify request signatures:
import { Receiver } from "@upstash/qstash";
import { serve } from "@upstash/workflow/nextjs";

export const { POST } = serve(
  async (context) => { ... },
  {
    receiver: new Receiver({
      currentSigningKey: "<QSTASH_CURRENT_SIGNING_KEY>",
      nextSigningKey: "<QSTASH_NEXT_SIGNING_KEY>",
    }),
  }
);

Custom Authorization Method

You can implement your own authorization mechanism with Upstash Workflow. The context object provides access to the initial request headers and payload on every workflow step. You can use them to pass your custom authentication token to verify the requests.
import { serve } from "@upstash/workflow/nextjs";

export const { POST } = serve(
  async (context) => {
    // 👇 Extract Bearer token form the request headers
    const authHeader = context.headers.get("authorization");
    const bearerToken = authHeader?.split(" ")[1];

    // 👇 Use your authentication function to verify the token
    if (!isValid(bearerToken)) {
      console.error("Authentication failed.");
      return;
    }

    // Your workflow steps..
  },
  {
    failureFunction: async () => {
      // 👇 Same auth check for failure function
      const authHeader = context.headers.get("authorization");
      const bearerToken = authHeader?.split(" ")[1];

      if (!isValid(bearerToken)) {
        // ...
      }
    },
  }
);
If you implement custom authorization in your workflow route, you should also include the same authorization check in the failure function.The failure function executes independently of the route function, so without this check, unauthorized requests could trigger the failure function