Skip to main content

Use Cases

Common patterns for web apps that need background processing.

Async Processing

Offload slow tasks from your request-response cycle. Return immediately to users while work happens in the background.

Next.js Vercel

Email Sequences

Schedule follow-up emails without managing queues or workers.

// Schedule welcome email 24 hours after signup
import { Runlater } from "runlater-js"
const rl = new Runlater(process.env.RUNLATER_KEY)

await rl.delay("https://myapp.com/api/send-welcome", {
  name: "welcome-email",
  delay: "24h"
})
SaaS Reports

Report Generation

Generate PDFs or exports without timing out. Email when ready.

// Return immediately, process in background
import { Runlater } from "runlater-js"
const rl = new Runlater(process.env.RUNLATER_KEY)

await rl.delay("https://myapp.com/api/reports/generate", {
  body: { reportId, userId },
  delay: "1h"
})
return Response.json({ status: "processing" })
Webhooks Stripe

Webhook Relay

Acknowledge webhooks fast, process reliably later.

// Stripe webhook - return 200 immediately
export async function POST(req) {
  const event = await req.json();
  await fetch('https://runlater.eu/api/v1/tasks', {
    method: 'POST',
    headers: { 'Authorization': 'Bearer YOUR_API_KEY' },
    body: JSON.stringify({
      url: 'https://myapp.com/api/process-stripe',
      body: JSON.stringify(event)
    })
  });
  return Response.json({ received: true });
}

Inbound Endpoints

Receive webhooks from external services like Stripe, GitHub, or Shopify. Runlater stores every event, forwards it to your app with retries, and lets you replay failures from the dashboard. No webhook handler code needed — just point the service at your Runlater inbound URL.

Stripe Webhooks

Stripe Webhook Receiver

Point Stripe at your Runlater endpoint. Every event is stored, forwarded to your app with retries, and replayable.

1. Create an endpoint in the dashboard with your forward URL(s):

https://myapp.com/api/stripe/process

2. Copy the inbound URL and paste it in Stripe's webhook settings:

https://runlater.eu/in/ep_abc123...

3. That's it. Runlater handles retries, stores every event, and you can replay failures from the dashboard.

GitHub CI/CD

GitHub Deploy Hooks

Trigger deploys or post-merge workflows from GitHub push events. Events are serialized so deploys never overlap.

Create an endpoint named github-deploys. Events from the same endpoint are serialized, so pushes are processed one at a time.

Forward URLs: https://myapp.com/api/deploy

Add the inbound URL as a GitHub webhook on your repo → Settings → Webhooks.

Shopify E-commerce

Shopify Order Processing

Receive order webhooks reliably. Never miss an order even if your server is temporarily down.

Runlater stores the raw payload and retries delivery up to 5 times with exponential backoff. If your app was down during a webhook, just hit Replay in the dashboard.

Lanes

Use the lane parameter to serialize execution. Tasks in the same lane run one at a time within your organization — each task waits for the previous one to finish before starting. Tasks without a lane (or in different lanes) run in parallel as normal.

Payments Ordering

Payment Processing

Process charges one at a time per user to prevent double-billing or race conditions.

// Each user gets their own lane — charges never overlap
import { Runlater } from "runlater-js"
const rl = new Runlater(process.env.RUNLATER_KEY)

await rl.send("https://myapp.com/api/charge", {
  body: { userId, amount },
  lane: `payments-user-${userId}`
})
API Rate Limiting

Sequential API Calls

Call a rate-limited third-party API without overwhelming it. One request finishes before the next starts.

// All Shopify sync tasks run one at a time
import { Runlater } from "runlater-js"
const rl = new Runlater(process.env.RUNLATER_KEY)

await rl.send("https://myapp.com/api/sync-product", {
  body: { productId },
  lane: "shopify-sync"
})

Recurring Schedules

Replace server cron jobs with managed, observable scheduling via the /schedules API.

Billing

Subscription Renewals

Process recurring billing daily at 6 AM UTC.

// Process recurring billing daily at 6 AM UTC
import { Runlater } from "runlater-js"
const rl = new Runlater(process.env.RUNLATER_KEY)

await rl.cron("subscription-renewals", {
  url: "https://myapp.com/api/billing/renewals",
  schedule: "0 6 * * *"
})
Maintenance

Database Cleanup

Clean up old sessions every night at 2 AM.

// Clean up old sessions every night at 2 AM
import { Runlater } from "runlater-js"
const rl = new Runlater(process.env.RUNLATER_KEY)

await rl.cron("db-cleanup", {
  url: "https://myapp.com/api/maintenance/cleanup",
  schedule: "0 2 * * *"
})
Monitoring

Health Checks

Monitor external APIs every 15 minutes.

// Monitor external APIs every 15 minutes
import { Runlater } from "runlater-js"
const rl = new Runlater(process.env.RUNLATER_KEY)

await rl.cron("health-check", {
  url: "https://myapp.com/api/health/stripe",
  schedule: "*/15 * * * *"
})

Why Runlater?

No infrastructure

Just HTTP endpoints. Works with any stack, any platform.

Cron + one-time tasks

Recurring schedules and delayed tasks in one simple API.

Inbound endpoints

Receive webhooks with retries, replay, and full event history. No code needed.

Built-in retries

Automatic exponential backoff. No retry logic to build.

EU-hosted

Your data stays in Europe. GDPR-friendly by design.

Alternatives comparison

Alternative Limitation
setTimeout/setInterval Doesn't survive restarts, no visibility
Database polling You're building task infrastructure
SQS/RabbitMQ Overkill - workers, DLQs, monitoring
Vercel/CloudFlare cron No one-time tasks, no retries, platform lock-in
DIY webhook handler You build retries, storage, replay, idempotency