NetStacksNetStacks

Tasks API

Schedule, run, and cancel automated tasks (config backups and AI agent tasks) with cron-based scheduling, timezones, and retries.

Overview

The Tasks API lets you create, manage, run, and cancel scheduled automation tasks. Tasks run on standard cron schedules with IANA timezone support and configurable retry/timeout behavior. Each run produces a separate execution record that tracks state, timing, output, and errors.

Controller (Enterprise) feature

Scheduled tasks are served by the NetStacks Controller. All task endpoints live under /api/tasks, require a valid JWT bearer token, and require the Operator permission. Execution-history endpoints (below) require the Admin permission.

The router exposes three groups of endpoints, all nested under /api/tasks:

  • /api/tasks/schedules — General scheduled tasks of any type (full request body, including retry/timeout fields).
  • /api/tasks/agent-schedules — A simplified interface for AI agent tasks (you pass a prompt instead of a free-form parameters object).
  • /api/tasks/executions/:id/cancel — Cancel a running or pending execution.
Implementation status of task types

Five task types are accepted by the schema, but only two run today: backup and agent_task. The deployment, health_check, and custom_script types are accepted at create time but their executor is not yet implemented — a run of those types fails with a "not yet implemented" error. Schedule only backup and agent_task tasks until the others ship.

Endpoints

All paths below are relative to your Controller base URL (for example https://netstacks.example.net). Path parameters are UUIDs.

General schedules (all task types)

MethodPathDescriptionSuccess
GET/api/tasks/schedulesList all scheduled tasks in the org200
POST/api/tasks/schedulesCreate a scheduled task201
GET/api/tasks/schedules/:idGet one task (full fields)200
PUT/api/tasks/schedules/:idUpdate a task200
DELETE/api/tasks/schedules/:idDelete a task204
POST/api/tasks/schedules/:id/runRun immediately (background)202
POST/api/tasks/schedules/:id/toggleEnable or disable a task200

Agent schedules (agent tasks only)

MethodPathDescriptionSuccess
GET/api/tasks/agent-schedulesList the caller's agent task schedules200
POST/api/tasks/agent-schedulesCreate an agent task schedule201
GET/api/tasks/agent-schedules/:idGet one agent schedule200
PUT/api/tasks/agent-schedules/:idUpdate an agent schedule200
DELETE/api/tasks/agent-schedules/:idDelete an agent schedule204
POST/api/tasks/agent-schedules/:id/runRun agent task now (background)202
POST/api/tasks/agent-schedules/:id/toggleEnable or disable200

Executions

MethodPathDescriptionSuccess
POST/api/tasks/executions/:id/cancelCancel a running/pending execution204
Agent task execution history

Execution records for agent tasks are listed through the admin-scoped agent-tasks router, not the schedules router: GET /api/admin/agent-tasks/history and GET /api/admin/agent-tasks/history/:execution_id. These require the Admin permission. See the Executions section.

Task Types & Parameters

The task_type field accepts these snake_case values: deployment, backup, health_check, custom_script, and agent_task. Each type reads its own keys from the free-form parameters object. Only backup and agent_task are executable today.

backup

Collects running configs from devices matching a filter and stores them as a config snapshot. Recognized parameters keys:

snapshot_name (string, optional)
Name for the created snapshot. Defaults to the task name when omitted.
device_filter (object, optional)
Selects which devices to back up. Supported keys are site and device_type. An empty object {} targets all devices in the org.
credential_id (UUID string, optional)
Overrides the credential used for collection. When omitted, the device's default credential is used, then the org default.
backup-parameters.jsonjson
{
  "snapshot_name": "Nightly DC1 Core Backup",
  "device_filter": { "site": "dc1", "device_type": "router" },
  "credential_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479"
}

agent_task

Runs an AI agent prompt through the agent system. The only recognized parameter is prompt. When you use the /api/tasks/agent-schedules endpoints, you pass prompt as a top-level field and the Controller wraps it into parameters for you.

agent-parameters.jsonjson
{ "prompt": "Review the last 7 days of config snapshots and flag anomalies." }
deployment / health_check / custom_script

These types are defined in the schema but their executors are not yet implemented; running them returns an error. Their parameter shapes are subject to change and are intentionally not documented here.

Cron Scheduling & Retries

Cron expressions

Schedules use standard 5-field cron syntax (minute hour day-of-month month day-of-week), validated server-side. The 6-field form with a leading seconds field is not used by the default parser — stick to 5 fields. Examples:

  • 0 2 * * * — every day at 02:00
  • 0 */6 * * * — every 6 hours
  • 0 9 * * 1-5 — weekdays at 09:00
  • 0 0 1 * * — first day of each month at midnight

The timezone must be a valid IANA identifier (for example America/New_York, Europe/London). It defaults to UTC when omitted. The Controller computes next_run_at in the requested timezone and returns it (as UTC) on the response.

Retries and timeout

On POST /api/tasks/schedules these are top-level request fields (not inside parameters):

FieldTypeDefaultMeaning
max_retriesinteger3Retry attempts after a failed run
retry_delay_secondsinteger60Delay between retry attempts
timeout_secondsinteger3600Max run duration before timeout
Agent schedules use fixed defaults

The /api/tasks/agent-schedules create endpoint does not expose max_retries, retry_delay_seconds, or timeout_seconds. Agent task schedules are created with max_retries = 3, retry_delay_seconds = 60, and timeout_seconds = 3600 (1 hour). To customize these, create the task via /api/tasks/schedules with task_type: "agent_task" instead.

List view omits per-task retry values

GET /api/tasks/schedules (the list view) returns the schema defaults 3 / 60 / 300 for max_retries, retry_delay_seconds, and timeout_seconds because the list query does not select those columns. To read the real per-task values, call GET /api/tasks/schedules/:id.

Code Examples

Create a scheduled backup task

create-backup-task.shbash
curl -X POST https://netstacks.example.net/api/tasks/schedules \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Nightly DC1 Core Backup",
    "description": "Back up running configs for DC1 routers",
    "task_type": "backup",
    "cron_expression": "0 2 * * *",
    "timezone": "America/New_York",
    "parameters": {
      "snapshot_name": "Nightly DC1 Core Backup",
      "device_filter": { "site": "dc1", "device_type": "router" }
    },
    "enabled": true,
    "max_retries": 2,
    "retry_delay_seconds": 120,
    "timeout_seconds": 1800
  }'

# Response (201 Created):
# {
#   "id": "a9b8c7d6-e5f4-3210-9876-543210fedcba",
#   "name": "Nightly DC1 Core Backup",
#   "task_type": "backup",
#   "cron_expression": "0 2 * * *",
#   "timezone": "America/New_York",
#   "parameters": { "snapshot_name": "Nightly DC1 Core Backup",
#                   "device_filter": { "site": "dc1", "device_type": "router" } },
#   "enabled": true,
#   "max_retries": 2,
#   "retry_delay_seconds": 120,
#   "timeout_seconds": 1800,
#   "next_run_at": "2026-03-11T07:00:00+00:00",
#   "created_at": "2026-03-10T17:00:00+00:00"
# }
create-backup-task.pypython
import requests

base_url = "https://netstacks.example.net/api"
headers = {"Authorization": "Bearer eyJhbGciOiJIUzI1NiIs..."}

task = requests.post(f"{base_url}/tasks/schedules", headers=headers, json={
    "name": "Nightly DC1 Core Backup",
    "description": "Back up running configs for DC1 routers",
    "task_type": "backup",
    "cron_expression": "0 2 * * *",
    "timezone": "America/New_York",
    "parameters": {
        "snapshot_name": "Nightly DC1 Core Backup",
        "device_filter": {"site": "dc1", "device_type": "router"},
    },
    "enabled": True,
    "max_retries": 2,
    "retry_delay_seconds": 120,
    "timeout_seconds": 1800,
})
task.raise_for_status()
data = task.json()
print(f"Created task {data['id']}, next run {data.get('next_run_at')}")

List scheduled tasks

list-tasks.shbash
curl "https://netstacks.example.net/api/tasks/schedules?limit=20&offset=0" \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."

# Response (200 OK):
# {
#   "tasks": [
#     {
#       "id": "a9b8c7d6-e5f4-3210-9876-543210fedcba",
#       "name": "Nightly DC1 Core Backup",
#       "task_type": "backup",
#       "cron_expression": "0 2 * * *",
#       "timezone": "America/New_York",
#       "enabled": true,
#       "last_run_at": "2026-03-10T07:00:00+00:00",
#       "next_run_at": "2026-03-11T07:00:00+00:00"
#     }
#   ],
#   "total": 5,
#   "limit": 20,
#   "offset": 0
# }

limit is capped at 100. The list view returns the schema-default retry/timeout values; call GET /api/tasks/schedules/:id for real per-task values.

Run a task immediately

run-task.shbash
# Trigger a background execution (returns 202 Accepted)
curl -X POST https://netstacks.example.net/api/tasks/schedules/a9b8c7d6-.../run \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."

# Response (202 Accepted) — a TaskExecution record:
# {
#   "id": "7c1f0e9a-2b3c-4d5e-6f70-8192a3b4c5d6",
#   "task_id": "a9b8c7d6-e5f4-3210-9876-543210fedcba",
#   "state": "pending",
#   "triggered_by": "manual",
#   "started_at": null,
#   "completed_at": null,
#   "output": null,
#   "error_message": null,
#   "retry_count": 0,
#   "created_at": "2026-03-10T17:30:00+00:00"
# }
Execution field names

A TaskExecution uses state (not status) and triggered_by (not trigger). Valid states are pending, running, completed, failed, cancelled, timeout, and approval_pending.

Toggle and delete

toggle-delete-task.shbash
# Disable a task without deleting it
curl -X POST https://netstacks.example.net/api/tasks/schedules/a9b8c7d6-.../toggle \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
  -H "Content-Type: application/json" \
  -d '{"enabled": false}'

# Delete a task permanently (returns 204 No Content)
curl -X DELETE https://netstacks.example.net/api/tasks/schedules/a9b8c7d6-... \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."

Update a task

PUT accepts a partial body — send only the fields you want to change. The task_type cannot be changed after creation. Changing cron_expression or timezone recomputes next_run_at.

update-task.shbash
curl -X PUT https://netstacks.example.net/api/tasks/schedules/a9b8c7d6-... \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
  -H "Content-Type: application/json" \
  -d '{
    "cron_expression": "0 3 * * *",
    "max_retries": 5,
    "parameters": { "device_filter": { "site": "dc1" } }
  }'

Create an agent task schedule

create-agent-task.shbash
# Simplified agent endpoint: pass a prompt, not a parameters object
curl -X POST https://netstacks.example.net/api/tasks/agent-schedules \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Weekly Config Audit",
    "description": "AI agent reviews config changes and flags anomalies",
    "prompt": "Review all config snapshots from the past 7 days. Flag unauthorized changes, security policy violations, or BGP route leaks.",
    "cron_expression": "0 9 * * 1",
    "timezone": "America/Chicago",
    "enabled": true
  }'

# Response (201 Created):
# {
#   "id": "b1c2d3e4-...",
#   "name": "Weekly Config Audit",
#   "prompt": "Review all config snapshots ...",
#   "cron_expression": "0 9 * * 1",
#   "timezone": "America/Chicago",
#   "enabled": true,
#   "last_run_at": null,
#   "next_run_at": "2026-03-16T14:00:00+00:00",
#   "created_at": "2026-03-10T17:00:00+00:00"
# }
create-agent-task.pypython
agent_task = requests.post(f"{base_url}/tasks/agent-schedules", headers=headers, json={
    "name": "Weekly Config Audit",
    "description": "AI agent reviews config changes and flags anomalies",
    "prompt": "Review all config snapshots from the past 7 days. Flag anomalies.",
    "cron_expression": "0 9 * * 1",
    "timezone": "America/Chicago",
    "enabled": True,
})
agent_task.raise_for_status()
print(f"Agent task created: {agent_task.json()['id']}")

Executions & Cancellation

Each scheduled run and each manual /run creates a TaskExecution record. Its lifecycle: pending → running → completed / failed / cancelled / timeout. Some agent runs may pause at approval_pending when an approval gate is hit.

Cancel a running execution

Cancel by execution ID (the id from a /run response), not the task ID. The Controller aborts the in-flight run if it's on this node and marks the execution record cancelled so the UI reflects it. Returns 204 No Content.

cancel-execution.shbash
curl -X POST \
  https://netstacks.example.net/api/tasks/executions/7c1f0e9a-2b3c-4d5e-6f70-8192a3b4c5d6/cancel \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."
# HTTP/1.1 204 No Content

Inspect agent task execution history

Agent task execution history is read through the admin-scoped agent-tasks router (requires the Admin permission). Filter by task_id to see one schedule's history.

execution-history.shbash
# List recent agent task executions (limit max 100)
curl "https://netstacks.example.net/api/admin/agent-tasks/history?task_id=b1c2d3e4-...&limit=50" \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."

# Get one execution's details
curl https://netstacks.example.net/api/admin/agent-tasks/history/7c1f0e9a-... \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."

# Execution summary fields: id, task_id, task_name, state, triggered_by,
# started_at, completed_at, retry_count, created_at

Questions & Answers

How do I create a scheduled task?
Send POST /api/tasks/schedules with name, task_type, cron_expression, an optional timezone (defaults to UTC), and a type-specific parameters object. The task then runs automatically on its cron schedule.
What task types actually run?
The schema accepts deployment, backup, health_check, custom_script, and agent_task, but only backup and agent_task have working executors today. The other three are accepted at create time but fail when run with a "not yet implemented" error.
Where do I set retry and timeout values?
On /api/tasks/schedules they are top-level request fields: max_retries (default 3), retry_delay_seconds (default 60), and timeout_seconds (default 3600) — not inside parameters. The agent-schedules endpoint does not expose them; it always uses 3 / 60 / 3600.
What parameters does a backup task take?
snapshot_name (defaults to the task name), device_filter with site and/or device_type keys (empty object means all devices), and an optional credential_id override.
Can I run a task immediately?
Yes. POST /api/tasks/schedules/:id/run returns 202 Accepted with a TaskExecution record while the run proceeds in the background.
How do I cancel a run?
Call POST /api/tasks/executions/:id/cancel using the execution's id (returns 204). It aborts the in-flight run if it is on the current node and marks the record cancelled.
How do I disable a task without deleting it?
POST /api/tasks/schedules/:id/toggle with {"enabled": false}. The task stays in the system but will not fire on its schedule.

Troubleshooting

Invalid cron expression (400 Bad Request)

The expression failed validation. Use standard 5-field cron: minute hour day-of-month month day-of-week. The 6-field seconds form is not supported by the default parser.

Invalid timezone (400 Bad Request)

The timezone must be a full IANA identifier such as America/New_York or Europe/London — not abbreviations like EST or GMT.

409 Conflict (duplicate name)

A task with the same name already exists in your organization. Task names must be unique — the create endpoint returns 409 Conflict on a duplicate.

Run fails with "not yet implemented"

You scheduled a deployment, health_check, or custom_script task. Those executors are not implemented yet; the execution is marked failed. Schedule backup or agent_task instead.

Backup completes with 0 devices

The device_filter matched nothing. Check the site and device_type values against your inventory, or use an empty filter {} to target all devices. Verify reachability with the Devices API first.

Task not firing on schedule

Confirm the task is enabled (enabled: true) and that next_run_at on GET /api/tasks/schedules/:id matches your expectation in the configured timezone.

List shows wrong retry/timeout numbers

The list endpoint returns schema defaults (3 / 60 / 300) rather than the stored per-task values. Fetch a single task with GET /api/tasks/schedules/:id to read the real values.

  • API Authentication — Obtain the JWT bearer token required for every task endpoint.
  • Devices API — Manage the devices that backup tasks collect configs from.
  • Error Codes — Reference for 400 / 401 / 404 / 409 / 500 responses.
  • Scheduled Tasks — UI guide for creating and managing scheduled tasks.
  • Cron Expressions — Cron syntax reference and common patterns.
  • Approvals — How agent runs reach the approval_pending state.