# Manage Jobs

A Job is an asynchronous operation dispatched to one or more devices. Fleets tracks the execution status independently for each target device, from the initial queued state through to success, failure, or timeout.

## When to Use Jobs

Use Jobs instead of Commands when:
- You need to target multiple devices at once (for example, push a firmware update to a group)
- Devices may be intermittently offline (jobs are queued and delivered on reconnect)
- You need a persistent record of which devices completed the operation and which did not

## Create a Job from the Console

1. In your Fleets deployment, go to **Commands & Jobs** > **Jobs** tab.
2. Click **+ Create Job**.
3. Fill in the fields:
   - **Name**: (Optional) A human-readable name for the job. If omitted, the job is identified by its ID.
   - **Job Type**: Select **SNAPSHOT** (runs once against current targets) or **CONTINUOUS** (applies to new devices that join the target group over time).
   - **Description**: (Optional) A description of the job's purpose.
   - **Target Things**: Select one or more individual devices.
   - **Group**: Select a Thing Group to target all its current members.
   - **Document**: (Required) A JSON object that defines the operation. The device receives this as its job document.
4. Click **Create Job**.

![create_job](./_assets/create_job.png)

## Create a Job via API

```text
POST /api/v1/jobs
```

Request body:
```json
{
  "description": "Firmware update to v2.1.5",
  "targets": {
    "thingGroupNames": ["com.example.thermostat (default)"]
  },
  "document": {
    "action": "updateFirmware",
    "params": {
      "version": "2.1.5",
      "url": "https://firmware.example.com/thermostat-v2.1.5.bin"
    }
  }
}
```

The `targets` field accepts:
- `thingNames`: a list of individual Thing names
- `thingGroupNames`: a list of Thing Group names (all current members are targeted)

The response includes the `jobId` (UUID).

## View Jobs and Execution Status

1. Go to **Commands & Jobs** > **Jobs** tab.
2. Use the **Status** and **Job Type** filters to narrow the list.
3. The list shows each job's **Name**, **Job Type**, **Status**, **Targets** (number of things and group names), and **Created At** timestamp.
4. Click a job name to open the job detail, which includes a per-device execution breakdown.

### Job Statuses

| Status | Meaning |
|---|---|
| `IN_PROGRESS` | At least one device execution is active |
| `COMPLETED` | All device executions have reached a terminal state |
| `CANCELED` | The job was canceled |
| `DELETION_IN_PROGRESS` | The job is being deleted |

### Execution Statuses per Device

| Status | Meaning |
|---|---|
| `QUEUED` | Waiting for the device to pick up the job |
| `IN_PROGRESS` | Device has started executing |
| `SUCCEEDED` | Device reported success |
| `FAILED` | Device reported failure |
| `REJECTED` | Device rejected the job |
| `TIMED_OUT` | No update received within the timeout window |
| `CANCELED` | This device's execution was canceled |
| `REMOVED` | Execution record was deleted |

## Cancel a Job

To stop dispatching a job to remaining devices:

1. Open the job detail.
2. Click **Cancel Job**.
3. Confirm the operation.

Or via API:
```text
POST /api/v1/jobs/{jobId}/cancel
```

Request body (all fields optional):
```json
{
  "reasonCode": "USER_CANCEL",
  "comment": "Pausing rollout for investigation"
}
```

Canceling a job does not stop executions that are already `IN_PROGRESS` on devices. To force-cancel active executions, use the `"force": true` parameter.

## Cancel a Single Device Execution

To cancel the job for a specific device without affecting others:

```text
POST /api/v1/jobs/{jobId}/executions/{thingName}/cancel
```

## Delete a Job

Jobs in a terminal state (`COMPLETED`, `CANCELED`) can be deleted:

```text
DELETE /api/v1/jobs/{jobId}
```

To delete an active job:
```text
DELETE /api/v1/jobs/{jobId}?force=true
```

::: warning Important Notice

Deleting a job removes all execution records for that job. This cannot be undone.

:::

## Job Device Protocol

On the device side, Jobs follow a pull-based protocol. When a new job is queued for a device, Fleets publishes a notification to:
```text
$emqx/things/{thingName}/jobs/notify
```

The device then pulls the job document, executes it, and reports status. See [Device Integration: Jobs](../device_integration/mqtt_integration.md#jobs) for the full device-side protocol.

## Next Steps

- [Commands and Jobs Overview](./commands_and_jobs_overview.md)
- [Send Commands](./send_commands.md)
- [Device Integration: Jobs](../device_integration/mqtt_integration.md#jobs)
