
About
Upstash QStash expert for serverless message queues, scheduled
name: upstash-qstash description: Upstash QStash expert for serverless message queues, scheduled jobs, and reliable HTTP-based task delivery without managing infrastructure. risk: unknown source: vibeship-spawner-skills (Apache 2.0) date_added: 2026-02-27
Upstash QStash
Upstash QStash expert for serverless message queues, scheduled jobs, and reliable HTTP-based task delivery without managing infrastructure.
Principles
- HTTP is the interface - if it speaks HTTPS, it speaks QStash
- Endpoints must be public - QStash calls your URLs from the cloud
- Verify signatures always - never trust unverified webhooks
- Schedules are fire-and-forget - QStash handles the cron
- Retries are built-in - but configure them for your use case
- Delays are free - schedule seconds to days in the future
- Callbacks complete the loop - know when delivery succeeds or fails
- Deduplication prevents double-processing - use message IDs
Capabilities
- qstash-messaging
- scheduled-http-calls
- serverless-cron
- webhook-delivery
- message-deduplication
- callback-handling
- delay-scheduling
- url-groups
Scope
- complex-workflows -> inngest
- redis-queues -> bullmq-specialist
- event-sourcing -> event-architect
- workflow-orchestration -> temporal-craftsman
Tooling
Core
- qstash-sdk
- upstash-console
Frameworks
- nextjs
- cloudflare-workers
- vercel-functions
- aws-lambda
- netlify-functions
Patterns
- scheduled-jobs
- delayed-messages
- webhook-fanout
- callback-verification
Related
- upstash-redis
- upstash-kafka
Patterns
Basic Message Publishing
Sending messages to be delivered to endpoints
When to use: Need reliable async HTTP calls
import { Client } from '@upstash/qstash';
const qstash = new Client({ token: process.env.QSTASH_TOKEN!, });
// Simple message to endpoint await qstash.publishJSON({ url: 'https://myapp.com/api/process', body: { userId: '123', action: 'welcome-email', }, });
// With delay (process in 1 hour) await qstash.publishJSON({ url: 'https://myapp.com/api/reminder', body: { userId: '123' }, delay: 60 * 60, // seconds });
// With specific delivery time await qstash.publishJSON({ url: 'https://myapp.com/api/scheduled', body: { report: 'daily' }, notBefore: Math.floor(Date.now() / 1000) + 86400, // tomorrow });
Scheduled Cron Jobs
Setting up recurring scheduled tasks
When to use: Need periodic background jobs without infrastructure
import { Client } from '@upstash/qstash';
const qstash = new Client({ token: process.env.QSTASH_TOKEN!, });
// Create a scheduled job const schedule = await qstash.schedules.create({ destination: 'https://myapp.com/api/cron/daily-report', cron: '0 9 * * *', // Every day at 9 AM UTC body: JSON.stringify({ type: 'daily' }), headers: { 'Content-Type': 'application/json', }, });
console.log('Schedule created:', schedule.scheduleId);
// List all schedules const schedules = await qstash.schedules.list();
// Delete a schedule await qstash.schedules.delete(schedule.scheduleId);
Signature Verification
Verifying QStash message signatures in your endpoint
When to use: Any endpoint receiving QStash messages (always!)
// app/api/webhook/route.ts (Next.js App Router) import { Receiver } from '@upstash/qstash'; import { NextRequest, NextResponse } from 'next/server';
const receiver = new Receiver({ currentSigningKey: process.env.QSTASH_CURRENT_SIGNING_KEY!, nextSigningKey: process.env.QSTASH_NEXT_SIGNING_KEY!, });
export async function POST(req: NextRequest) { const signature = req.headers.get('upstash-signature'); const body = await req.text();
// ALWAYS verify signature const isValid = await receiver.verify({ signature: signature!, body, url: req.url, });
if (!isValid) { return NextResponse.json( { error: 'Invalid signature' }, { status: 401 } ); }
// Safe to process const data = JSON.parse(body); await processMessage(data);
return NextResponse.json({ success: true }); }
Callback for Delivery Status
Getting notified when messages are delivered or fail
When to use: Need to track delivery status for critical messages
import { Client } from '@upstash/qstash';
const qstash = new Client({ token: process.env.QSTASH_TOKEN!, });
// Publish with callback await qstash.publishJSON({ url: 'https://myapp.com/api/critical-task', body: { taskId: '456' }, callback: 'https://myapp.com/api/qstash-callback', failureCallback: 'https://myapp.com/api/qstash-failed', });
// Callback endpoint receives delivery status // app/api/qstash-callback/route.ts export async function POST(req: NextRequest) { // Verify signature first! const data = await req.json();
// data contains: // - sourceMessageId: original message ID // - url: destination URL // - status: HTTP status code // - body: response body
if (data.status >= 200 && data.status < 300) { await markTaskComplete(data.so
