--- # SPDX-License-Identifier: AGPL-3.0-or-later title: Jobs description: Background job status, progress, task types, and cancellation. --- import RouteHeader from '@/components/RouteHeader.astro'; These routes list recorded background jobs and request cancellation. Create jobs through [Queue bulk job](/admin-api/bulk-jobs/#queue-bulk-job), [Create system DM broadcast](/admin-api/system-dms/#create-system-dm-broadcast), [Refresh search index](/admin-api/search-indexes/#refresh-search-index), or an [archive](/admin-api/archives/) request. Reads require `jobs:view` and cancellation requires `jobs:cancel`. Every operation on this page shares the `admin:jobs:view` bucket. The three reads record no [Admin audit entry](/admin-api/#admin-audit-entry-object), and cancellation records one. :::note[Job updates vary by task] Mention processing, link previews, announcement delivery and copy updates, message delete audit log batching, payment reconciliation when a user connects, and every scheduled task except `syncUrlBlocklists` and `syncFileShaBlocklists` run with no job record and never appear here. Fluxer deletes a job record about 90 days after the job was queued. A job also runs with no record when Fluxer fails to write that record. When a later write of status, progress or attempts fails, Fluxer logs the failure and the job continues, so the stored values can be behind the real run. Use the [archive routes](/admin-api/archives/) to track archive progress and failures. ::: ## Admin job object One object describes one recorded job. These routes can only change `cancel_requested`. ### Structure | Field | Type | Description | | --- | --- | --- | | job_id | snowflake | The ID of the job | | task_type | string | The [background job task type](#background-job-task-types) the job runs | | status | string | The latest recorded [job status](#job-statuses) | | progress_current | ?integer | The units of work reported as complete, or null | | progress_total | ?integer | The reported total units of work, or null | | progress_message | ?string | The latest reported progress message, or null | | error_message | ?string | The recorded failure reason, or null | | created_at | ISO8601 timestamp | The time the job was recorded | | started_at | ?ISO8601 timestamp | The latest recorded start time, or null when no start was recorded | | completed_at | ?ISO8601 timestamp | The recorded completion, cancellation or failure time, or null | | requested_by_user_id | ?snowflake | The recorded requester, or null | | audit_log_reason | ?string | The recorded audit log reason, or null | | jet_stream_lane | ?string | The assigned [processing lane](#processing-lanes), or null | | jet_stream_seq | ?string | The recorded queue sequence as a decimal string, or null | | attempts | integer | The recorded retry count, not a total of all executions | | max_attempts | integer | Attempt limit recorded at queue time. Retries stop at the lane's delivery limit, which can differ from this value | | run_at | ?ISO8601 timestamp | The earliest permitted run time, or null for an immediate job | | cancel_requested | boolean | Whether cancellation has been requested | | context_link | ?string | An Admin console path related to the job, or null | | payload | ?string | The JSON-encoded job input, or null | | result | ?string | The JSON-encoded result, or null | Progress fields stay null until the task reports progress. A task can report progress with no total or no message, which sets `progress_total` or `progress_message` to null. The payload can contain identifiers and message content, including in list responses. ### Example ```json { "job_id": "1501314428688998182", "task_type": "bulkUpdateUserFlags", "status": "running", "progress_current": 25, "progress_total": 400, "progress_message": "Updating flags", "error_message": null, "created_at": "2026-08-31T09:00:00.000Z", "started_at": "2026-08-31T09:00:01.000Z", "completed_at": null, "requested_by_user_id": "1478812292088791040", "audit_log_reason": null, "jet_stream_lane": "lifecycle", "jet_stream_seq": "4812", "attempts": 0, "max_attempts": 5, "run_at": null, "cancel_requested": false, "context_link": null, "payload": "{\"user_ids\":[\"1478812292088791040\"]}", "result": null } ``` ## Job cursor object Pass all three fields from `next_cursor` back to [List jobs](#list-jobs) using the corresponding cursor query parameters. ### Structure | Field | Type | Description | | --- | --- | --- | | bucket_day | string | The cursor's UTC date as `YYYY-MM-DD` | | created_at | ISO8601 timestamp | The cursor's creation time | | job_id | snowflake | The cursor's job identifier | ### Example ```json { "bucket_day": "2026-08-30", "created_at": "2026-08-30T22:41:07.512Z", "job_id": "1500901337221828608" } ``` ## Job statuses | Value | Description | | --- | --- | | queued | The job was recorded and no later status has been recorded | | running | The job was recorded as started | | succeeded | The job was recorded as completed successfully | | cancelled | The task read the cancellation request and stopped | | deadletter | The job was recorded as failed, including a failure to queue it | `queued` and `running` accept cancellation requests. The other statuses are terminal. A cancellation request does not guarantee that the task will stop. The queue drops a job 7 days after it was queued. A job still recorded as `queued` or `running` after 8 days is set to `deadletter` with the error `Expired from the job queue`. ## Processing lanes `jet_stream_lane` identifies the job's processing group. | Value | Description | | --- | --- | | realtime | Mention processing | | unfurl | Link previews | | lifecycle | Account, guild, moderation, archive and bulk work | | batch | Scheduled maintenance and index work | | crosspost | [Announcement channel](/topics/announcement-channels/) delivery, copy updates, and follower cleanup | ## Background job task types Use the returned `task_type` value to filter [List jobs](#list-jobs). The tasks queued by [Bulk jobs](/admin-api/bulk-jobs/) are `bulkUpdateUserFlags`, `bulkUpdateGuildFeatures`, `bulkAddGuildMembers`, and `bulkScheduleUserDeletion`. `harvestUserData` and `harvestGuildData` build [archives](/admin-api/archives/), `sendSystemDm` delivers a [system DM broadcast](/admin-api/system-dms/), and `refreshSearchIndex` rebuilds a [search index](/admin-api/search-indexes/). `removeChannelFollowers` runs on the `crosspost` lane after an announcement channel is deleted or converted into a text channel, and removes the channel follower webhooks that follow it. ## List jobs Returns a page of [Admin job](#admin-job-object) objects, newest first, within the requested lookback window. Requires `jobs:view`. ### Query parameters | Field | Type | Description | | --- | --- | --- | | limit? | integer | The maximum number of jobs to return (1-200, default 50) | | cursor_bucket_day? | string | `next_cursor.bucket_day` as a `YYYY-MM-DD` UTC date | | cursor_created_at? | string | `next_cursor.created_at` as an ISO 8601 timestamp (1-64 characters) | | cursor_job_id? | snowflake | The job identifier from `next_cursor.job_id` | | max_lookback_days? | integer | The number of days before today to include (1-60, default 14) | | status? | string | Filter by recorded [job status](#job-statuses) | | task_type? | string | Filter by [task type](#background-job-task-types) (1-128 characters) | | requested_by_user_id? | snowflake | Filter by recorded requester | Supply all three cursor parameters together or omit all three. An incomplete or malformed cursor returns 400 `INVALID_FORM_BODY`. An unknown task type returns an empty page. A full page always returns `next_cursor`, so the next request can return an empty page. ### Response body | Field | Type | Description | | --- | --- | --- | | jobs | array[[Admin job](#admin-job-object) object] | The jobs in this page | | next_cursor | ?[job cursor](#job-cursor-object) object | The cursor for the next request, or null | ### Response | Status | Body | Condition | | --- | --- | --- | | 200 | response body | The job page was returned | | 400 | [error response](/admin-api/#error-response) | `INVALID_FORM_BODY` because a query parameter is invalid | ### Side effects The operation records no [Admin audit entry](/admin-api/#admin-audit-entry-object). ### Rate limit 600 requests per minute for each authenticated user, on the `admin:jobs:view` bucket. ## List active jobs Returns recorded active jobs as [Admin job](#admin-job-object) objects. Requires `jobs:view`. This operation has no filters or pagination. The stored status and progress can be behind the work the task has already done. ### Response body | Field | Type | Description | | --- | --- | --- | | jobs | array[[Admin job](#admin-job-object) object] | The recorded active jobs | ### Response | Status | Body | Condition | | --- | --- | --- | | 200 | response body | The active jobs were returned | ### Side effects The operation records no [Admin audit entry](/admin-api/#admin-audit-entry-object). ### Rate limit 600 requests per minute for each authenticated user, on the `admin:jobs:view` bucket. ## Get job Returns one [Admin job](#admin-job-object) object. Requires `jobs:view`. ### Path parameters | Field | Type | Description | | --- | --- | --- | | job_id | snowflake | The ID of the job | ### Response body | Field | Type | Description | | --- | --- | --- | | job | [Admin job](#admin-job-object) object | The job | ### Response | Status | Body | Condition | | --- | --- | --- | | 200 | response body | The job was returned | | 404 | `{"error": "job_not_found"}` | No job record has this identifier | A 404 response uses this single-field body rather than the standard error response. A missing or expired record does not mean the work never ran. ### Side effects The operation records no [Admin audit entry](/admin-api/#admin-audit-entry-object). ### Rate limit 600 requests per minute for each authenticated user, on the `admin:jobs:view` bucket. ## Cancel job Requests cooperative cancellation of a job and reports whether the request was recorded. Requires `jobs:cancel`. ### Path parameters | Field | Type | Description | | --- | --- | --- | | job_id | snowflake | The ID of the job | ### Response body | Field | Type | Description | | --- | --- | --- | | cancelled | boolean | Whether a cancellation request was recorded | Returns false for a missing or terminal job. Repeating a request for a queued or running job returns true. ### Response | Status | Body | Condition | | --- | --- | --- | | 200 | response body | The request was evaluated, whether or not the flag changed | ### Side effects Sets `cancel_requested` to true for a queued or running job. `sendSystemDm`, `bulkBanFileShas`, `bulkDeleteMessagesForUsers`, and the tasks queued by [Bulk jobs](/admin-api/bulk-jobs/) check for the request while they run and stop at the next check. Other tasks run to the end. Completed work is not undone. A status of `cancelled` means the task stopped because of the request. The operation records one [Admin audit entry](/admin-api/#admin-audit-entry-object) with action `cancel_job`, target type `bulk_job`, target ID equal to `job_id`, and metadata key `cancelled`. The entry uses target type `bulk_job` for every task type, and Fluxer records it when `cancelled` is false too. ### Rate limit 600 requests per minute for each authenticated user, on the `admin:jobs:view` bucket.