Cloudflare Workers
This is a step by step guide on how to receive webhooks from QStash in your Cloudflare Worker.
Project Setup
We will use C3 (create-cloudflare-cli) command-line tool to create our functions. You can open a new terminal window and run C3 using the prompt below.
npm create cloudflare@latestyarn create cloudflare@latestThis will install the create-cloudflare package, and lead you through setup. C3 will also install Wrangler in projects by default, which helps us testing and deploying the projects.
➜ npm create cloudflare@latestNeed to install the following packages: create-cloudflare@2.52.3Ok to proceed? (y) yusing create-cloudflare version 2.52.3╭ Create an application with Cloudflare Step 1 of 3│├ In which directory do you want to create your application?│ dir ./cloudflare_starter│├ What would you like to start with?│ category Hello World example│├ Which template would you like to use?│ type Worker only│├ Which language do you want to use?│ lang TypeScript│├ Do you want to use git for version control?│ yes git│╰ Application createdWe will also install the Upstash QStash library.
npm install @upstash/qstash3. Use QStash in your handler
First we import the library:
import { Receiver } from "@upstash/qstash";Then we adjust the Env interface to include the QSTASH_CURRENT_SIGNING_KEY
and QSTASH_NEXT_SIGNING_KEY environment variables.
export interface Env { QSTASH_CURRENT_SIGNING_KEY: string; QSTASH_NEXT_SIGNING_KEY: string;}And then we validate the signature in the handler function.
First we create a new receiver and provide it with the signing keys.
const receiver = new Receiver({ currentSigningKey: env.QSTASH_CURRENT_SIGNING_KEY, nextSigningKey: env.QSTASH_NEXT_SIGNING_KEY,});Then we verify the signature.
const body = await request.text();const isValid = await receiver.verify({ signature: request.headers.get("Upstash-Signature")!, body,});The entire file looks like this now:
import { Receiver } from "@upstash/qstash";export interface Env { QSTASH_CURRENT_SIGNING_KEY: string; QSTASH_NEXT_SIGNING_KEY: string;}export default { async fetch(request, env, ctx): Promise<Response> { const receiver = new Receiver({ currentSigningKey: env.QSTASH_CURRENT_SIGNING_KEY, nextSigningKey: env.QSTASH_NEXT_SIGNING_KEY, }); const body = await request.text(); const isValid = await receiver.verify({ signature: request.headers.get("Upstash-Signature")!, body, }); if (!isValid) { return new Response("Invalid signature", { status: 401 }); } // signature is valid return new Response("Hello World!"); },} satisfies ExportedHandler<Env>;Configure Credentials
There are two methods for setting up the credentials for QStash. One for worker level, the other for account level.
Using Cloudflare Secrets (Worker Level Secrets)
This is the common way of creating secrets for your worker, see Workflow Secrets
-
Navigate to Upstash Console and get your QStash credentials.
-
In Cloudflare Dashboard, Go to Compute (Workers) > Workers & Pages.
-
Select your worker and go to Settings > Variables and Secrets.
-
Add your QStash credentials as secrets here:

Using Cloudflare Secrets Store (Account Level Secrets)
This method requires a few modifications in the worker code, see Access to Secret on Env Object
import { Receiver } from "@upstash/qstash";export interface Env { QSTASH_CURRENT_SIGNING_KEY: SecretsStoreSecret; QSTASH_NEXT_SIGNING_KEY: SecretsStoreSecret;}export default { async fetch(request, env, ctx): Promise<Response> { const c = new Receiver({ currentSigningKey: await env.QSTASH_CURRENT_SIGNING_KEY.get(), nextSigningKey: await env.QSTASH_NEXT_SIGNING_KEY.get(), }); // Rest of the code },};After doing these modifications, you can deploy the worker to Cloudflare with npx wrangler deploy, and
follow the steps below to define the secrets:
-
Navigate to Upstash Console and get your QStash credentials.
-
In Cloudflare Dashboard, Go to Secrets Store and add QStash credentials as secrets.

- Under Compute (Workers) > Workers & Pages, find your worker and add these secrets as bindings.

Deployment
Newer deployments may revert the configurations you did in the dashboard. While worker level secrets persist, the bindings will be gone!
Deploy your function to Cloudflare with npx wrangler deploy
The endpoint of the function will be provided to you, once the deployment is done.
Publish a message
Open a different terminal and publish a message to QStash. Note the destination url is the same that was printed in the previous deploy step.
curl --request POST "https://qstash.upstash.io/v2/publish/https://<your-worker-name>.<account-name>.workers.dev" \ -H "Authorization: Bearer <QSTASH_TOKEN>" \ -H "Content-Type: application/json" \ -d "{ \"hello\": \"world\"}"In the logs you should see something like this:
$ npx wrangler tail⛅️ wrangler 4.43.0--------------------Successfully created tail, expires at 2025-10-16T00:25:17ZConnected to <your-worker-name>, waiting for logs...POST https://<your-worker-name>.<account-name>.workers.dev/ - Ok @ 10/15/2025, 10:34:55 PMNext Steps
That's it, you have successfully created a secure Cloudflare Worker, that receives and verifies incoming webhooks from qstash.
Learn more about publishing a message to qstash here.
You can find the source code here.