> ## Documentation Index
> Fetch the complete documentation index at: https://docs.auction-rise.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Background Jobs

> Job queue with status tracking, progress reporting, and integration with any job runner.

# Background Jobs

The template provides a lightweight job tracking layer backed by a `job_runs` table. It handles status/progress persistence so you can integrate any job runner (Trigger.dev, Inngest, a custom API route, etc.) without vendor lock-in.

## Job Status Lifecycle

```
pending → running → completed
                 ↘ failed
                 ↘ canceled
```

| Status      | Meaning                             |
| ----------- | ----------------------------------- |
| `pending`   | Job has been queued but not started |
| `running`   | Job runner has picked it up         |
| `completed` | Finished successfully               |
| `failed`    | Finished with an error              |
| `canceled`  | Canceled before completion          |

## Creating a Job

Call `enqueueJob()` to create a tracking record and get back a job ID:

```typescript theme={null}
import { enqueueJob } from "@/lib/jobs/actions";

const jobId = await enqueueJob("generate-report", workspaceId, {
  reportType: "monthly",
  startDate: "2025-01-01",
});
// Then pass jobId to your job runner
```

The `payload` field accepts any JSON-serializable object.

## Updating Job Status

From your job runner (API route, webhook handler, etc.), update the job as it progresses:

```typescript theme={null}
import { updateJobStatus } from "@/lib/jobs/actions";

// Mark as started
await updateJobStatus(jobId, { status: "running", progress: 0 });

// Update progress (0-100)
await updateJobStatus(jobId, { progress: 50 });

// Mark complete with result
await updateJobStatus(jobId, {
  status: "completed",
  progress: 100,
  result: { rowsProcessed: 1234 },
});

// Mark failed
await updateJobStatus(jobId, {
  status: "failed",
  error: "Connection timed out after 30s",
});
```

`started_at` is set automatically when status transitions to `running`. `completed_at` is set on `completed` or `failed`.

## Polling Job Status

Poll from a client component or API route:

```typescript theme={null}
import { getJobStatus } from "@/lib/jobs/actions";

const job = await getJobStatus(jobId);
// job.status, job.progress, job.result, job.error
```

## Canceling a Job

```typescript theme={null}
import { cancelJob } from "@/lib/jobs/actions";

await cancelJob(jobId);
```

<Note>
  Canceling sets the database status to `canceled` but does not stop a running job runner process. Your job runner should poll for `canceled` status and bail out gracefully.
</Note>

## JobRun Type

```typescript theme={null}
type JobRun = {
  id: string;
  type: string;
  status: "pending" | "running" | "completed" | "failed" | "canceled";
  payload: Record<string, unknown> | null;
  result: Record<string, unknown> | null;
  error: string | null;
  progress: number; // 0-100
  workspace_id: string | null;
  created_by: string;
  started_at: string | null;
  completed_at: string | null;
  created_at: string;
};
```

## Long-running API Routes

For long-running operations in Next.js, set `maxDuration` in the route file to avoid the default 10s timeout:

```typescript theme={null}
// app/api/jobs/[jobId]/run/route.ts
export const maxDuration = 300; // 5 minutes

export async function POST(request: Request) {
  // ... run your job
}
```
