Background Jobs for Vibe Coders: Ship Delayed Tasks Without Learning DevOps

You’re building fast. Cursor is writing your components, v0 is generating your UI, and you shipped your SaaS landing page before lunch. Your app is live on Vercel with a Supabase backend and Stripe billing.

Then your co-founder asks: “Can we send users a reminder if they haven’t finished onboarding after 24 hours?”

And suddenly you’re reading about message queues, Redis, worker processes, and something called BullMQ. The vibes are gone.

You Don’t Need a Queue. You Need One Line of Code.

Here’s the thing about background jobs, delayed tasks, and scheduled emails: they’re all just HTTP requests that happen later.

You already know how to make HTTP requests. You do it every time you call an API. The only difference is when.

// This sends a request RIGHT NOW
await fetch('https://your-app.com/api/send-reminder', {
  method: 'POST',
  body: JSON.stringify({ userId: '123' }),
});

// This sends the SAME request in 24 HOURS
await fetch('https://zeplo.to/https://your-app.com/api/send-reminder?_delay=86400&_token=YOUR_TOKEN', {
  method: 'POST',
  body: JSON.stringify({ userId: '123' }),
});

That’s it. Same fetch(). Same endpoint. Same body. Just prefix with zeplo.to/ and add _delay in seconds.

No Redis. No workers. No DevOps. No new concepts to learn.

What Can You Build?

Here’s a cheat sheet of common tasks and the one-liner to implement them:

Send an email later

// Send onboarding tip 2 hours after signup
await fetch(`https://zeplo.to/${APP_URL}/api/email/onboarding-tip?_delay=7200&_retry=3&_token=${TOKEN}`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ email: user.email, tip: 'day-1' }),
});

Expire something after a timeout

// Cancel unpaid order after 30 minutes
await fetch(`https://zeplo.to/${APP_URL}/api/orders/expire?_delay=1800&_retry=2&_token=${TOKEN}`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ orderId }),
});

Run a daily digest

// Set up once — runs every morning at 8am UTC
await fetch(`https://zeplo.to/${APP_URL}/api/digest?_cron=0|8|*|*|*&_retry=3&_token=${TOKEN}`, {
  method: 'POST',
});

Retry a flaky API call

// Call a payment API with automatic retry on failure
await fetch(`https://zeplo.to/https://api.stripe.com/v1/charges?_retry=5&_token=${TOKEN}`, {
  method: 'POST',
  headers: { 'Authorization': `Bearer ${STRIPE_KEY}` },
  body: new URLSearchParams({ amount: '2000', currency: 'usd', source: tokenId }),
});

Why This Works for AI-Assisted Development

If you’re building with Cursor, Copilot, Windsurf, or prompting Claude to write your backend — you already know that the simpler the API, the better the AI output.

Compare these prompts:

“Set up a BullMQ queue with Redis connection, create a worker process, add a job that sends an email in 24 hours with exponential backoff retry, and make sure the worker restarts on failure”

“When a user signs up, use fetch to call zeplo.to with my email endpoint URL, a 24 hour delay, and 3 retries”

The second prompt gives you working code on the first try. Every time. Because it’s just fetch() with a URL.

There’s nothing to configure, nothing to install, and nothing for the AI to hallucinate about. Your AI assistant already knows how fetch() works.

The 5-Minute Setup

Step 1: Sign up at app.zeplo.io/register (free, 500 requests/month)

Step 2: Copy your token from the dashboard

Step 3: Add it to your .env:

ZEPLO_TOKEN=your_token_here

Step 4: Make a helper (optional but clean):

// lib/zeplo.ts
export async function scheduleRequest(url: string, options: {
  delay?: number,
  retry?: number,
  cron?: string,
  method?: string,
  body?: any,
  headers?: Record<string, string>,
}) {
  const params = new URLSearchParams({ _token: process.env.ZEPLO_TOKEN! });
  if (options.delay) params.set('_delay', String(options.delay));
  if (options.retry) params.set('_retry', String(options.retry));
  if (options.cron) params.set('_cron', options.cron);
  
  return fetch(`https://zeplo.to/${url}?${params}`, {
    method: options.method || 'POST',
    headers: options.headers || { 'Content-Type': 'application/json' },
    body: options.body ? JSON.stringify(options.body) : undefined,
  });
}
// Usage
await scheduleRequest('https://my-app.vercel.app/api/send-email', {
  delay: 3600,
  retry: 3,
  body: { userId: '123', template: 'welcome' },
});

What You’re NOT Doing

Let’s appreciate what you didn’t have to do:

  • Install Redis
  • Learn BullMQ API
  • Configure a worker process
  • Set up a message broker
  • Write retry logic
  • Build a dead letter queue
  • Monitor worker health
  • Handle worker crashes
  • Manage queue backpressure
  • Deploy a separate service

You wrote a fetch() call. Ship it.

When You Might Need More

Zeplo is perfect for: - Delayed tasks (emails, reminders, expirations) - Scheduled jobs (daily reports, cleanups, syncs) - Retry logic (payment processing, webhook delivery) - Any “do this later” pattern in a serverless app

If you ever need complex job orchestration, priority queues, or processing millions of events per second — you’ll know, and you’ll have outgrown the vibe coding phase anyway. Cross that bridge when the revenue justifies the infrastructure.

Until then, ship fast.


Zeplo adds background jobs to any app with zero infrastructure. Just fetch(). Start free →

Return to Blog