---
title: "Jobs Queue — Quick Start Examples"
aliases: [jobs-queue-quick-start, payload-jobs-example, jobs-queue-welcome-email]
tags: [payloadcms, jobs-queue, tasks, scheduling, cron, background-jobs]
sources: [raw/jobs-queue__quick-start-example.md]
created: 2026-05-15
updated: 2026-05-15
---

## Why Use a Job Queue?

Instead of running slow/risky work inline in hooks:

- **Non-blocking** — API returns immediately; email/heavy work runs async
- **Resilience** — automatic retries if external service is down
- **Scalability** — job workers can run on separate servers
- **Monitoring** — all jobs stored in DB with status + error logs

---

## Example 1: Welcome Email on User Signup

### Step 1 — Define the Task (`payload.config.ts`)

```ts
jobs: {
  tasks: [
    {
      slug: 'sendWelcomeEmail',
      retries: 3,
      inputSchema: [
        { name: 'userEmail', type: 'email', required: true },
        { name: 'userName',  type: 'text',  required: true },
      ],
      handler: async ({ input, req }) => {
        await req.payload.sendEmail({
          to: input.userEmail,
          subject: 'Welcome!',
          text: `Hi ${input.userName}, welcome to our platform!`,
        })
        return { output: { emailSent: true } }
      },
    },
  ],
}
```

### Step 2 — Queue the Job (from a Collection hook)

```ts
// In users collection config
hooks: {
  afterChange: [
    async ({ req, doc, operation }) => {
      if (operation === 'create') {
        await req.payload.jobs.queue({
          task: 'sendWelcomeEmail',
          input: { userEmail: doc.email, userName: doc.name },
        })
      }
    },
  ],
}
```

Job is stored in `payload-jobs` collection immediately; runs async — no API delay.

### Step 3 — Run Jobs via `autoRun`

```ts
jobs: {
  tasks: [ /* ... */ ],
  autoRun: [
    { cron: '*/5 * * * *' }, // check every 5 minutes
  ],
}
```

---

## Example 2: Scheduled Daily Report (No User Trigger)

### Task with `schedule` Property

```ts
{
  slug: 'generateDailyReport',
  schedule: [
    {
      cron: '0 8 * * *',  // 8:00 AM daily
      queue: 'reports',
    },
  ],
  handler: async ({ req }) => {
    const report = await req.payload.create({ collection: 'reports', data: { /* ... */ } })
    return { output: { reportId: report.id } }
  },
}
```

### `autoRun` Must Match the Queue Name

```ts
autoRun: [
  {
    cron: '* * * * *',  // check every minute
    queue: 'reports',   // MUST match schedule.queue
    limit: 10,
  },
]
```

> **Critical gotcha:** `schedule.queue` and `autoRun.queue` must be identical. Mismatch → jobs queued but never executed.

### Execution Flow

1. **8:00 AM** — `schedule` auto-queues a job into `'reports'` queue
2. **Within 1 min** — `autoRun` cron finds the job
3. **Execution** — report generated
4. **Next day** — repeats automatically

---

## One-Time Future Job (`waitUntil`)

```ts
await payload.jobs.queue({
  task: 'publishPost',
  input: { postId: '123' },
  waitUntil: new Date('2024-12-25T15:00:00Z'),
})
```

Different from `schedule` — runs **once** at specified time, not recurring.

---

## Approach Comparison

| Approach | When to Use | Example |
|----------|-------------|---------|
| **Manual Queuing** | Triggered by user actions / data changes | Welcome emails, payments, notifications |
| **Scheduled (`schedule`)** | Automatic recurring at fixed intervals | Daily reports, weekly cleanups, nightly syncs |
| **`waitUntil`** | One-time job in the future | Publish at 3pm, trial expiry reminder |

---

## Serverless Warning

`autoRun` does **not** work on Vercel/serverless. Use the [[wiki/payloadcms/jobs-queue-queues|Vercel Cron approach]] with a dedicated API endpoint instead.

---

## Key Takeaways

- Define tasks in `jobs.tasks[]` with `slug`, `inputSchema`, `retries`, and `handler`
- Queue manually: `req.payload.jobs.queue({ task: 'slug', input: {...} })`
- `autoRun` polls for pending jobs on a cron schedule
- For scheduled recurring tasks: add `schedule` to the task + matching `queue` in `autoRun`
- Queue names in `schedule.queue` and `autoRun.queue` **must match exactly**
- `waitUntil` = one-time future job; `schedule` = recurring
- `autoRun` does not work on serverless (Vercel) — use API endpoint trigger instead

---

## Related Articles

- [[wiki/payloadcms/jobs-queue|Jobs Queue — Overview]]
- [[wiki/payloadcms/jobs-queue-jobs|Jobs Queue — Jobs Detail]]
- [[wiki/payloadcms/jobs-queue-queues|Jobs Queue — Queues & Execution]]
- [[wiki/payloadcms/hooks|PayloadCMS — Hooks]]
- [[wiki/payloadcms/email|Email — Adapters, SMTP, Resend, Attachments]]

---

## Sources

- `raw/jobs-queue__quick-start-example.md`
- https://payloadcms.com/docs/jobs-queue/quick-start-example
