task
Tasks are operations executed on devices — they are produced by many flows (ndcli sync apply, VPN configuration, backup runs, recurring scheduled tasks, and user-initiated runs via ndcli run). The task command surface is for inspecting and cancelling them. To issue a command to one or more devices, use ndcli run. To manage recurring tasks, use ndcli schedule tasks.
Each task records where it came from, and, for one a person started, who asked and from which client. See Where a task came from.
Task Types
Section titled “Task Types”| Type | Description |
|---|---|
PING | Check device connectivity (issued by ndcli run ping) |
SHUTDOWN | Power off the device (issued by ndcli run poweroff) |
REBOOT | Reboot the device (issued by ndcli run restart) |
RESTART | Restart the NetDefense agent service (issued by ndcli run plugin-reload) |
PLUGIN_INSTALL | (Re)install the NetDefense agent plugin (issued by ndcli run plugin-install) |
FIRMWARE_UPGRADE | Upgrade the device’s OPNsense firmware — a minor point release or a major series jump (issued by ndcli run firmware-upgrade) |
FIRMWARE_TRACK | One step of a firmware track: pin a device at its installed release, move it to its track’s release and update it, or put back the setting it had. NetDefense creates these; ndcli run can’t |
PULL | Pull configuration from device |
SYNC | Sync configuration to device |
BACKUP | Run a configured backup |
CONNECT | Open a remote-access tunnel session to the device |
Firmware track tasks
Section titled “Firmware track tasks”A FIRMWARE_TRACK task is created by NetDefense when you move a device into or out of a track, and at each of a track’s windows. Nobody can create one with ndcli run. List them with ndcli task list --type FIRMWARE_TRACK. A page that holds a completed one gets an extra Result column, such as advance success 26.1.11 → 26.7 (major upgrade).
ndcli task describe shows a completed one as a result block:
Firmware track result: Action: advance on the regular track Outcome: held Reason: a reboot is pending on the device (held_reboot_pending) Target: 26.1.10 Pinned: 26.1.8 Drift: noAction is enroll, advance or unenroll. The block exists only for a completed step, and its Outcome is success, noop, held or released. A step that failed, or never got an answer from the device, shows its plain Message. Its outcome, failed or failed_no_response, appears in ndcli firmware history, and a failed one in the Last Result column of ndcli firmware status too. A major upgrade adds a Kind line and Note lines, and Drift says whether the pin had been changed outside NetDefense. With -f json, the block is also returned as firmware_track_result, beside the task as NetDefense sent it. The reasons are explained in Firmware Tracks.
To stop the steps that are queued for a release or a series, halt it, switch major upgrades off for the track, or disable Firmware.
Task Statuses
Section titled “Task Statuses”| Status | Description |
|---|---|
PENDING | Task created and waiting for dispatch |
SCHEDULED | Task deferred via --at; activates when its time arrives |
IN_PROGRESS | Task currently executing on the device |
COMPLETED | Task completed successfully |
FAILED | Task did not succeed, for example because the device reported an error, or took the task and sent no final answer by its deadline |
CANCELED | Task was cancelled before dispatch, or because its device was deleted |
EXPIRED | The task’s deadline passed before it reached the device, so it never ran |
Note the spelling: the status value is CANCELED, with a single L. From ndcli 1.36.0, --status also accepts CANCELLED.
An EXPIRED task’s message says why it never reached the device, such as Task expired before delivery: the device was offline (last seen 2026-10-17T14:03:22Z). A task can also expire while its device is online. ndcli task describe prints the message as NetDefense stored it, with the time in UTC. See Expired or failed for every reason.
Where a task came from
Section titled “Where a task came from”Every task records its source. From ndcli 1.40.0, task list and task describe show it, and -f json carries it as origin.
| Source | Created by | task list shows |
|---|---|---|
MANUAL | A person: ndcli run, a sync, a pull, a remote-access session, or a command from the web app or the MCP server | The person and the client, such as ana@example.com · CLI, or Manual |
SCHEDULE | A scheduled task firing | Schedule nightly-backup |
FIRMWARE | A step of a firmware track | fast track, or Firmware track |
A task a person started also records:
- Who asked (
requested_by): the account’s email and name, read when you look at the task. If the account has since been deleted, both arenulland the task readsDeleted account. A task that a schedule or a track created has no requester. - The client (
client): how the request arrived.WEBis the web app,CLIisndcli,MCPis the MCP server, andAPIis a personal access token, whatever sent the request,ndcliwithNDCLI_TOKENset included. From ndcli 1.40.0 the MCP server is told apart fromndcli; a task started through an older one is recorded asCLI. The client describes the request. It never grants or limits anything, and a request that arrived another way has none.
Every task also belongs to a run (run_id): eight letters or digits shared by the tasks that one request created, such as the tasks of one ndcli run on several devices or of one ndcli sync apply, or that one firing of a scheduled task created. A schedule with several scheduled tasks fires one run for each. List a run with task list --run, and read its code from ndcli task describe on any of its tasks. The Tasks page of the web app gathers a run’s tasks under one header.
When a task was sent and finished
Section titled “When a task was sent and finished”Dispatched is when NetDefense sent the task to the device. Completed is when it reached its final status: COMPLETED, FAILED, EXPIRED or CANCELED. Duration is the time between the two, so it counts what the device needed, not the time the task waited. A task that was never sent, such as one that expired or was canceled before it ran, has no Dispatched and no Duration. When the device answers a task after it failed with a timeout, its answer takes the place of the timeout, and Completed moves with it.
Older tasks
Section titled “Older tasks”A task created before NetDefense began recording these has no requester, client or run, and usually no Dispatched or Completed. For one a person started, task describe says Source: Manual (who started it is not recorded), and -f json has null in the new fields. Such tasks leave the list with the rest of your history: see Task retention.
Task retention
Section titled “Task retention”NetDefense automatically removes finished tasks (COMPLETED, FAILED, EXPIRED and CANCELED) once they are older than your plan keeps them: 30 days on the default plan. The Tasks page of the web app states your organization’s period under its title. More precisely, a finished task is removed once its creation time, its expiry time and its last update are all further in the past than that period. PENDING, SCHEDULED and IN_PROGRESS tasks are never removed. Removal runs in the background about once an hour, so a task can stay visible a little past the period.
Once removed, a task no longer appears in ndcli task list, on the Tasks page of the web app or on the dashboard, and looking up its task code returns “not found”. Removing a BACKUP task leaves its backup alone, whether a schedule or run backup started it: backup files live in your organization’s own storage, and NetDefense does not delete them. Recurring scheduled tasks and their schedules are not affected either.
Commands
Section titled “Commands”| Command | Description |
|---|---|
task list | List tasks |
task describe | Show task details |
task cancel | Cancel a pending or scheduled task |
task list
Section titled “task list”List tasks in your organization.
ndcli task list [flags]| Flag | Type | Default | Description |
|---|---|---|---|
--device | string | Filter by device name (regex); --device-exact takes exact names | |
--status | string | Filter by status. For several values, repeat the flag or separate them with commas (e.g., --status FAILED --status EXPIRED); that needs ndcli 1.36.0 or later | |
--type | string | Filter by task type. For several values, repeat the flag or separate them with commas (e.g., --type SYNC --type BACKUP); that needs ndcli 1.36.0 or later | |
--expired | bool | Filter by expired status | |
--created-after | string | Filter by created date | |
--created-before | string | Filter by created date | |
--sort-by | string | created_at:desc | Sort as field:direction. The fields are created_at, updated_at, expires_at, scheduled_at, status, type and device, and the direction is asc or desc |
--page | int | 1 | Page number |
--per-page | int | 30 | Items per page (max 100) |
Filters added in ndcli 1.40.0
Section titled “Filters added in ndcli 1.40.0”These flags need ndcli 1.40.0 or later, and a control plane that applies them: see An older control plane. They narrow the list together with the flags above.
| Flag | Type | Description |
|---|---|---|
--exclude-type | string | Leave out these task types, for example CONNECT to hide remote-access sessions. Repeat the flag or separate the values with commas. It applies together with --type, and all isn’t accepted |
--origin | string | Only tasks from these sources: MANUAL, SCHEDULE or FIRMWARE. Repeat the flag or separate the values with commas, or give all for no filter |
--schedule | string | Only the tasks that the scheduled tasks of this schedule created. The schedule’s exact name |
--device-exact | string | Only the tasks of the devices with exactly these names. Repeat the flag or separate the values with commas. A deleted device isn’t found by its old name here |
--search | string | Only the tasks whose code or device name contains this text, whatever the case. Plain text, not a pattern. A deleted device’s name counts |
--scheduled-after | string | Only the tasks set to run at or after this time, the ones deferred with ndcli run --at. A task with no scheduled time never matches. See Date Filter Formats |
--scheduled-before | string | Only the tasks set to run at or before this time |
--include-active | bool | With --created-after or --created-before, also list every PENDING, SCHEDULED and IN_PROGRESS task, whenever it was created. The other filters still apply to them, so a reboot booked three days ago for tomorrow stays in a 24-hour list |
--run | string | Only the tasks of one run: its code, eight letters or digits |
--requested-by | string | Only the tasks that the account with this email asked for, whatever the case. A task that a schedule or a track created never matches |
A type, status or origin that ndcli doesn’t know is refused before anything is sent, and so is a run code that isn’t eight letters or digits.
Date Filter Formats
Section titled “Date Filter Formats”--created-after, --created-before, --scheduled-after and --scheduled-before accept:
- Relative:
30m,2h,7d,2w(minutes, hours, days, weeks ago).--scheduled-afterand--scheduled-beforealso take+30m,+2hor+7d, for that long from now - ISO 8601:
2024-01-15T10:30:00Z. A time with an offset, such as2024-01-15T10:30:00-03:00, is converted. A time without an offset is read as UTC
From ndcli 1.40.0, a relative time is counted from the current moment whatever timezone your computer is set to. Earlier releases moved the window by the computer’s offset from UTC.
List output
Section titled “List output”The table has the columns ID, Type, Status, Device and Created. These are added when the page has something to show in them:
- Source: where the task came from and, for a task a person started, who and from which client, as in Where a task came from. A control plane that doesn’t say leaves the column out.
- Duration: how long the task took, such as
3s,1m 12s,2h 05mor1d 02h, or-when that isn’t known. The column shows when at least one task on the page was sent and has finished. - Result: the result of a firmware track step.
A device that was deleted has (deleted) after its name, and a waiting task whose device isn’t connected has (offline).
╭──────────┬────────┬─────────────┬──────────────────┬─────────────────────────┬──────────────────┬──────────╮│ ID │ Type │ Status │ Device │ Source │ Created │ Duration │├──────────┼────────┼─────────────┼──────────────────┼─────────────────────────┼──────────────────┼──────────┤│ Pq3Lm8Xa │ PING │ ● COMPLETED │ fw-branch-01 │ ana@example.com · CLI │ 2026-10-18 11:02 │ 3s ││ Pq3Lm8Xb │ PING │ ● COMPLETED │ fw-branch-02 │ ana@example.com · CLI │ 2026-10-18 11:02 │ 2s ││ tQSd9RCV │ BACKUP │ ● COMPLETED │ fw-branch-01 │ Schedule nightly-backup │ 2026-10-18 00:00 │ 1m 12s ││ Dl2Gn6Hk │ SYNC │ ○ FAILED │ fw-old (deleted) │ Deleted account · MCP │ 2026-10-16 07:00 │ 39s ││ Mn4Op5Qr │ REBOOT │ ○ SCHEDULED │ fw-branch-02 │ ana@example.com · WEB │ 2026-10-18 06:00 │ - │╰──────────┴────────┴─────────────┴──────────────────┴─────────────────────────┴──────────────────┴──────────╯With -f simple, each task is one line: • Pq3Lm8Xa - PING [COMPLETED] · ana@example.com · CLI · took 3s. With -f detailed, each task is a block of fields, including its source, requester, client and run, and when it was sent and finished. With -f json, each task has the keys listed under task describe.
Examples
Section titled “Examples”ndcli task list
ndcli task list --created-after 1h
ndcli task list --device fw-prod-01
ndcli task list --status FAILED
ndcli task list --type SYNC
ndcli task list --status SCHEDULED --sort-by created_at:asc
# Multi-value: show both FAILED and EXPIRED tasksndcli task list --status FAILED --status EXPIRED
# Multi-value: show SYNC and BACKUP tasks from the last 24hndcli task list --type SYNC --type BACKUP --created-after 24h
# The last day, hiding remote-access sessions, with every task that is still activendcli task list --created-after 24h --include-active --exclude-type CONNECT
# What the nightly-backup schedule createdndcli task list --schedule nightly-backup
# Failed tasks that schedules createdndcli task list --origin SCHEDULE --status FAILED
# Everything one person asked for in the last weekndcli task list --requested-by ana@example.com --created-after 7d
# The tasks of one runndcli task list --run R7x2Kp9Q
# Tasks set to run within the next 24 hoursndcli task list --status SCHEDULED --scheduled-before +24h
# The tasks of exactly one devicendcli task list --device-exact fw-branch-01An older control plane
Section titled “An older control plane”The filters added in ndcli 1.40.0 are applied by the control plane. One that doesn’t have a filter yet ignores it without a word, which would hand you a list that looks filtered and isn’t. So ndcli checks that the control plane applied each filter you gave. When one wasn’t applied, ndcli prints no list and exits with status 1. The error reads Error: [SERVER_TOO_OLD] …: it says the control plane predates the filter, names it, and says that no list is shown. With -f json, the error is {"error": {"code": "SERVER_TOO_OLD", "message": "…"}} on standard error, and standard output stays empty. Run the command again without that filter. --run and --requested-by are the newest, so a control plane that applies the other filters may still refuse these two.
Plain ndcli task list and ndcli task describe work against an older control plane. They leave out the columns it has nothing for, and -f json has null in the fields it doesn’t send.
task describe
Section titled “task describe”Show detailed information about a specific task.
ndcli task describe [task-id]Arguments
Section titled “Arguments”| Argument | Required | Description |
|---|---|---|
task-id | Yes | Task code (8-char base62) |
Output
Section titled “Output”Shows:
- Task code, type and status
- The device, followed by
(deleted)when it was removed. For a waiting task it also says whether the device is connected and when it was last seen, such asfw-branch-01 (offline, last seen 2026-10-17 19:41:10) - The organization
Source: where the task came from, as in Where a task came from. A task that a scheduled task created addsSchedule,Spec(the scheduled task’s code and kind) andNext run, which readsdisabledwhile it is paused. A task a person started addsRequested byandClient. Any task that belongs to a run addsRun- Creation, expiry and (when set) scheduled times, then
Dispatched,CompletedandDuration, andResponded, when the device’s answer was recorded - Result or error message
Task ID: Pq3Lm8XaType: PINGStatus: COMPLETEDDevice: fw-branch-01Organization: acme-corpSource: ManualRequested by: Ana Lima <ana@example.com>Client: CLIRun: R7x2Kp9QCreated: 2026-10-18 11:02:11Expires: 2026-10-18 11:07:11Dispatched: 2026-10-18 11:02:12Completed: 2026-10-18 11:02:15Duration: 3sResponded: 2026-10-18 11:02:15
Message:pongA line appears only when the task has something for it. Times are in your configured display timezone.
With -f json, the task is printed as the control plane sent it, with a few keys of ndcli’s own added. Times are UTC and end in Z, and a key that the control plane doesn’t send is null. These keys describe where the task came from and how it ran:
| Key | Meaning |
|---|---|
origin | MANUAL, SCHEDULE or FIRMWARE |
schedule | For a task that a scheduled task created: its name, spec_code, kind, next_run_at (null while it is disabled) and firmware_track. null for any other task, and when the schedule or the scheduled task has been deleted |
requested_by | The email and name of the account that asked. null for a task nobody asked for, and both fields null once the account is deleted |
client | WEB, CLI, MCP or API. null when none is recorded |
run_id | The run’s code |
dispatched_at | When NetDefense sent the task to the device. null until then |
completed_at | When the task reached its final status. null while it has none |
duration_seconds | Added by ndcli: the seconds from dispatched_at to completed_at, or null when either is missing |
started_at | Added by ndcli: the same instant as dispatched_at. It had no value before ndcli 1.40.0, so read dispatched_at in new scripts |
device_deleted | true when the device has been removed |
device_online, device_last_seen_at | For a PENDING or SCHEDULED task: whether its device is connected, and when it was last seen, which can lag by about two minutes. null for any other task |
last_response_at | When the device’s answer was recorded. null when it never answered |
Examples
Section titled “Examples”ndcli task describe abc12345ndcli task describe abc12345 -f json- Finished tasks are removed once they are older than your plan keeps them (30 days on the default plan); looking up a removed task’s code returns “not found”. See Task retention.
task cancel
Section titled “task cancel”Cancel a pending or scheduled task.
ndcli task cancel [task-id]Arguments
Section titled “Arguments”| Argument | Required | Description |
|---|---|---|
task-id | Yes | Task code to cancel |
Examples
Section titled “Examples”ndcli task cancel abc12345- Only tasks with
PENDINGorSCHEDULEDstatus can be cancelled. - Tasks already
IN_PROGRESScannot be cancelled. - Cancelled tasks have status
CANCELED.
MCP tools
Section titled “MCP tools”The three commands have tools in the MCP server: ndcli.task.list, ndcli.task.describe and ndcli.task.cancel. Their arguments follow the flags, with an optional organization. ndcli.task.cancel changes nothing until it is called with confirm=true, and returns a preview without it.
ndcli.task.list takes every filter above, and needs ndcli 1.40.0 or later for the newer ones. An argument is the flag’s name with underscores for hyphens, such as exclude_type, device_exact and created_after, and the one that differs is run_id, for --run. Where a flag takes several values, the argument is a list.
Each task in the list, and the one ndcli.task.describe returns, carries origin, schedule, device_deleted, device_online, device_last_seen_at, last_response_at, requested_by, client, run_id, dispatched_at, completed_at and duration_seconds, with the meanings under task describe. started_at isn’t among them, so read dispatched_at. Times are UTC, and a key that the control plane doesn’t send is null.
A task started through the MCP server on your login is recorded with the client MCP from ndcli 1.40.0, so the web app shows it as Manual · MCP. A server from an older release is recorded as CLI, and one running on NDCLI_TOKEN as API.
When the control plane doesn’t apply a filter yet, ndcli.task.list returns no list. Its error has the code SERVER_TOO_OLD and names the filter: call the tool again without that argument. See An older control plane.