Anything that shouldn't hold up a request, sending a batch of emails, processing a webhook,
running cleanup, or a periodic task, runs as a background job. One Shot runs jobs on **Solid
Queue**, which is already configured and needs no Redis: in production it runs in-process inside
Puma, and in development and test it runs jobs inline or async automatically. `perform_later` just
works from a fresh clone.

The fastest way to add one is the **`add-job`** skill in Claude Code.

## Creating a job

```bash
bin/rails g job SendInvoiceReminder
```

This generates `app/jobs/send_invoice_reminder_job.rb`. Keep `perform` small and idempotent: Solid
Queue may retry a job, so running it twice for the same input should be safe. Operate through
account-scoped associations inside the job, the same rule as everywhere else in the app; never
reach for a global scope like `Invoice.find(id)` when `account.invoices.find(id)` is available.

## Enqueuing work

```ruby
SendInvoiceReminderJob.perform_later(invoice.id)
```

Pass ids, not objects: by the time the job actually runs, the record you passed might have changed
or been deleted, and an id forces the job to re-fetch current state rather than operate on a stale
snapshot serialized at enqueue time. For work that should run later rather than now:

```ruby
SendInvoiceReminderJob.set(wait: 1.hour).perform_later(invoice.id)
```

## Recurring jobs

For work that should run on a schedule rather than in response to an event, add an entry to
`config/recurring.yml`, using cron-style scheduling. Solid Queue reads this file and schedules the
job itself; you don't need an external cron or a scheduler service.

## Testing

Write `spec/jobs/<name>_job_spec.rb`, asserting the job's actual behavior with it performed inline
(`perform_enqueued_jobs` or the equivalent for your test setup). Wherever the job is triggered from
elsewhere in the app (a controller action, another job), assert on that trigger with
`have_enqueued_job`; the test adapter for this is already configured, so no additional setup is
needed.

## Verify

`bin/check` should be green, including the new job spec.

## Next

If the job needs to send an email, see [Sending Email](/docs/building-features/sending-email).
