Every workflow you can run from the Versus UI can also be run over the REST API — so you can launch and supervise automation from a CI pipeline, a change system, a cron job, or your own tooling. This guide is generic: it applies to any workflow — upgrades, certificate renewals, firewall changes, migrations, custom scripts — not one specific playbook.
The API lets you do four things, which map to the four parts of this guide:
All endpoints are under your Versus base URL, e.g. https://versus.example.com/api/…
The API uses JWT bearer tokens. Exchange a username / password for an access token:
POST /api/auth/token/
Content-Type: application/json
{ "username": "svc-automation", "password": "••••••••" }
── 200 OK ──
{ "access": "eyJhbGc…", "refresh": "eyJhbGc…" }Send the access token on every subsequent request:
Authorization: Bearer eyJhbGc…
Access tokens last 8 hours; refresh with POST /api/auth/token/refresh/ ({ "refresh": "…" }) to get a new access token without re-sending credentials. Use a dedicated service account for automation, scoped by RBAC to only what it needs. If the account has MFA enforced, complete the TOTP challenge (/api/auth/totp/verify/) as part of obtaining the token.
List the workflows available to you:
GET /api/workflows/
Each entry includes its id, name, slug, category, and graph (the nodes and sections that make up the flow). Fetch one to inspect its structure and any inputs it expects:
GET /api/workflows/{id}/Workflows are organized into sections — labelled groups of steps (for example Pre-checks, Apply, Verify). Sections are what let you run a workflow in controlled stages (see §3). The full catalog of step types is available at GET /api/workflow-steps/.
There are two ways to start a run. Use whichever fits your caller.
POST /api/workflow-trigger/ resolves everything by human-readable identifiers, so your script doesn't need to look up database IDs:
POST /api/workflow-trigger/
Authorization: Bearer …
Content-Type: application/json
{
"workflow": "NetScaler Upgrade", // workflow name or slug (case-insensitive)
"device_group": "ns-pair-1", // target device group by name
"run_name": "Nightly upgrade", // optional label
"stop_after_section": 0, // optional: pause after section 0
"context": { } // any extra workflow inputs
}
── 201 Created ──
{ "run_id": 42, "status": "pending", "status_url": "/api/workflow-status/42/" }Some workflow families accept convenience fields — e.g. an upgrade can take firmware_version (and optionally vendor), which Versus resolves to the right firmware image and injects into the run context for you. Anything else your workflow reads goes in context.
If you already have the workflow's numeric ID, post directly to it:
POST /api/workflows/{id}/run/
{ "device_group": 7, "name": "Nightly upgrade", "context": { } }pending; poll its status to see when it starts.This is the key to safe, staged execution. Pass stop_after_section: N (0-based) to run up to and including section N, then pause. For example, stop_after_section: 0 runs only the first section (say, Pre-checks) and stops.
When you're ready to continue, advance the run with resume:
POST /api/workflow-runs/{run_id}/resume/
{ "stop_after_section": 1 } // run through section 1, then pause againOmit stop_after_section on resume to run the rest of the workflow to completion. This lets you gate each stage behind your own checks — run pre-checks via the API, validate the result in your pipeline, then release the Apply section, then Verify, each with its own resume call.
GET /api/workflow-status/{run_id}/Returns the run broken down by section → step → device, with timing, per-step status, and — for any failed step — an extracted error snippet and AI-generated resolution guidance. Poll this until the run reaches a terminal status (completed, failed, cancelled) or pauses (waiting_approval, paused).
For the raw run object (including step_runs with their IDs, which you need for step-level actions below), use GET /api/workflow-runs/{run_id}/. List and filter runs with GET /api/workflow-runs/?workflow=&status=&device_group=.
If a workflow contains an Approval node, the run pauses at waiting_approval. Clear it:
POST /api/workflow-runs/{run_id}/resume_approval/
{ "approved": true, "comment": "Change CR-1234 approved" }Send "approved": false to reject and stop the run.
| Action | Endpoint | Body |
|---|---|---|
| Pause a running run | POST /api/workflow-runs/{id}/pause/ | — |
| Resume / advance sections | POST /api/workflow-runs/{id}/resume/ | stop_after_section? |
| Cancel a run | POST /api/workflow-runs/{id}/cancel/ | — |
| Retry a failed step | POST /api/workflow-runs/{id}/retry_step/ | { step_run_id } |
| Resume a failed run | POST /api/workflow-runs/{id}/resume_failed/ | — |
| Mark a step complete | POST /api/workflow-runs/{id}/mark_complete/ | { step_run_id } |
Step-level actions take a step_run_id, which you read from GET /api/workflow-runs/{id}/.
If a run made changes you need to reverse, request a back-out plan — Versus generates the compensating steps for what ran:
POST /api/workflow-runs/{run_id}/request_backout/
{ "step_run_ids": [ … ] } // optional; omit to back out the whole runRetrieve it with GET /api/workflow-runs/{run_id}/backout/.
A typical staged, API-driven change looks like this:
POST /api/auth/token/ → get a bearer token.POST /api/workflow-trigger/ with stop_after_section: 0 → run pre-checks, get run_id.GET /api/workflow-status/{run_id}/ until it pauses; validate the pre-check results in your pipeline.POST /api/workflow-runs/{run_id}/resume/ (no stop_after_section) → release the rest.retry_step, or request_backout to reverse.Because the same runs appear in the Versus UI, an operator can watch, intervene, or take over at any point — the API and the console act on the same workflow run.
versus / integrations objects inside custom script steps.