QStash
An HTTP message queue with retries, delays and schedules, in one command.
AI/LLM: this page is available in plain markdown at /docs/resources/qstash.md
npx extraorbital add qstash✓ Provisioned qstash/default
✓ Wrote 4 variables to .env
QSTASH_URL, QSTASH_TOKEN, QSTASH_CURRENT_SIGNING_KEY, QSTASH_NEXT_SIGNING_KEYA project that mentions QSTASH_URL or QSTASH_TOKEN gets this from
extraorbital provision without naming it.
What it writes
| Variable | Example |
|---|---|
QSTASH_URL | https://qstash.extraorbital.dev/v2/publish |
QSTASH_TOKEN | eyJVc2VySUQ… |
QSTASH_CURRENT_SIGNING_KEY | sig_7f2a… |
QSTASH_NEXT_SIGNING_KEY | sig_9c4e… |
Publish a message with a target URL and QStash delivers it with retries, delays and schedules. There is no connection to hold open, which suits serverless functions and agents that sleep between runs. The two signing keys let you verify deliveries and rotate without downtime.
Options
npx extraorbital add qstash [--slug <name>]| Flag | Default | Description |
|---|---|---|
--slug | default | A second, fully separate set of credentials |
Prototype allowance
1,000 messages per day and 50 GB of bandwidth free on the Prototype plan, counted across your whole account and shared by up to five queues. Past it, $1.00/100K messages — our own cost, with nothing added, and retries are still free — on a paid plan.
Use it
Publish with a delay:
import { Client } from "@upstash/qstash";
const qstash = new Client({ token: process.env.QSTASH_TOKEN! });
await qstash.publishJSON({
url: "https://my-agent.example.com/api/jobs",
body: { task: "summarize", docId: "doc-42" },
delay: 60, // seconds
});Verify on the receiving end, so only your queue can trigger the handler:
import { Receiver } from "@upstash/qstash";
const receiver = new Receiver({
currentSigningKey: process.env.QSTASH_CURRENT_SIGNING_KEY!,
nextSigningKey: process.env.QSTASH_NEXT_SIGNING_KEY!,
});
export async function POST(request: Request) {
const body = await request.text();
const ok = await receiver.verify({
signature: request.headers.get("Upstash-Signature")!,
body,
});
if (!ok) return new Response("Invalid signature", { status: 401 });
return Response.json({ received: JSON.parse(body) });
}