Skip to main content
fleetctl api calls Fleet Manager HTTP API endpoints from a shell. Its resource commands are generated from docs/openapi.yaml: all readable GET operations are exposed, plus a small allowlist of operator mutations (allocation device assignment, job creation, and burn-in rerun planning and dispatch) that need an authenticated API workflow instead of direct database access. The command list therefore follows the committed OpenAPI surface instead of a hand-written catalogue. Use it when you need a quick API inspection tool, a copy-pasteable debugging command, a way to dispatch an existing workflow, or a stable way for automation to discover read endpoints. For writing a dedicated client, start with Client Authentication and Scopes & Permissions.

Quick start

Install dependencies, then confirm the command group is wired up:
Before login works, Fleet Manager must have an OAuth2 application for the CLI. The default client ID is fleetctl-api; that application must allow the authorization-code + PKCE flow and register http://127.0.0.1/callback as a redirect URI. Django OAuth Toolkit treats loopback redirect URIs this way as valid for any callback port, so the CLI’s default runtime redirect http://127.0.0.1:8765/callback is covered. Log in with the browser-based OAuth flow:
By default, fleetctl api login uses the production API (https://api.fleet.nscale.com) and stores credentials in the default profile. Pass --base-url and --profile when you need dev, local, or a named profile:
If you are on a remote host where opening a browser is not useful, use --no-open-browser; the command prints the authorization URL so you can open it yourself. If you change --client-id or --callback-port, the selected OAuth client must still allow the resulting loopback callback. Profiles are stored in ~/.config/fleetctl/api-tokens.json by default with 0600 file permissions. The file contains plaintext access and refresh tokens, so treat it like any other local credential file. Set FLEETCTL_API_TOKEN_STORE=/path/to/api-tokens.json when you need an isolated token store for tests or automation.

Discover commands

Human users can discover the generated surface through --help:
Agents should instead read the generated command reference once before running commands: src/fleet_manager/cli/commands/api/generated/fleetctl-api-agent-command-reference.md. It is the compact command index for agent context, and outside a source checkout fleetctl api agent-reference prints the same content. Generated help pages end with a runnable example when the command needs no caller-specific values, or an explicitly labelled usage template when IDs or request data must be supplied. Resource names are singular and kebab-cased, for example device, device-group, inventory-device, and job-device-status. Most generated resources expose list for collection endpoints and get for detail endpoints. Some OpenAPI actions are exposed as their action name when the endpoint is not a plain list or retrieve.

Common call patterns

List resources:
Get one resource by path parameter:
Assign devices to an existing allocation by rack and role:
Pass --all-devices true to select every device in the allocation’s site; a request with no selector is rejected. Run without --dry-run true to apply the memberships. The operation is idempotent for existing members. If another allocation overlaps, the API still assigns every non-conflicting device but returns HTTP 409 with a typed conflict list; the CLI prints that result and exits non-zero so the conflict cannot be missed. Unassign devices using the same selectors:
Run without --dry-run true to remove the selected memberships. Devices that are not members of the target allocation are reported as idempotent no-ops; memberships in other allocations are never changed. To select the whole site, pass --all-devices true explicitly; a bare unassignment request is rejected. Override the profile URL for one call:
Add --debug to print HTTP method, final URL, status code, and request duration to stderr. Authorization headers and token values are not printed.

Dispatch an existing workflow

The job-create command dispatches an existing Fleet Manager workflow. It does not create or edit workflow definitions:
--data must contain a JSON object. Fleet Manager validates the workflow ID, devices, parameters, eligibility, and conflicts.

Plan and dispatch burn-in reruns

Preview the minimum policy-satisfying reruns for selected devices without creating Jobs:
Dispatch recalculates the plan from live state and creates the safe Jobs it finds:
Both rerun commands deliberately use the same authorization boundary as direct Job creation. The plan command is non-mutating, but it still requires permission to launch the Jobs it previews. Before using Job creation, rerun planning, or rerun dispatch:
  1. Add job:create to the fleetctl-api OAuth Application’s allowed scopes.
  2. Grant the invoking user or group the Django permission fleet_manager_core.add_job.
  3. Run fleetctl api login again so a new token is issued with the updated access.
The scopes issued to a token are the intersection of the requested scopes, the application’s allowed scopes, and the user’s permission-derived scopes:
Logging in with --scope read-only deliberately excludes job:create, even when both the application and user permit it.

Filters and output

Generated query parameters become kebab-case options. Check the command help for the exact options on each endpoint:
Array filters can be passed as comma-separated values or by repeating the option. Both forms produce the same API query for endpoints whose OpenAPI parameter uses comma-separated array encoding:
Deep-object filters use key=value entries at the CLI:
Boolean filters take explicit values:
The default output is a table. When a supported AI-agent harness is detected, including Claude Code, Codex, Conductor, Cursor, or Gemini, the default switches to ANSI-free --format json so structured output does not have to be requested on every call. Set FLEETCTL_AGENT_MODE=true or false to force either behaviour, and an explicit --format always wins. After a successful call, commands print a Next: hint on stderr suggesting follow-up commands pre-filled with the first result’s id — device list suggests fleetctl api device get <id> — so results can be chained without re-deriving the workflow. Hints never appear on stdout, so piped output stays clean. Use --columns to keep only the fields you need, including dot paths for nested fields. With the default table format it selects the table columns; with --format json it projects every object down to those fields, whatever the response shape — a results collection, a bare array, or a single object. Projection only narrows the fields inside rows; it never adds or removes response-level keys such as count:
Use JSON when another tool will consume the response:
List commands also support a count-only format:
For paginated list endpoints, table and JSON output follow every next page and merge the results. --format count reads the count from the first page and does not fetch the remaining pages. At a large site an unfiltered list is slow and returns a snapshot whose per-row status can lag the underlying record, because every page is merged in memory. Filter server-side and size with --format count first; when a decision depends on one resource’s current state, read it back by id or --name-exact rather than trusting a row from a big merged list.

Troubleshooting

  • Error: API profile 'default' is not logged in: run fleetctl api login, or pass the profile you stored with --profile.
  • Login fails before a profile is stored: verify the OAuth client exists in Fleet Manager, uses the expected client ID, supports authorization-code + PKCE, and allows http://127.0.0.1/callback.
  • 401 Unauthorized: the token is missing, expired without a refresh token, or rejected by the API. Re-run login and then whoami.
  • 403 Forbidden: the user or token is valid but does not have the required scope or Django permission. See Scopes & Permissions.
  • Command help does not show a new supported endpoint: regenerate the OpenAPI-derived CLI metadata. If API code changed, run make openapi. If only docs/openapi.yaml changed, run make api-cli.
  • Generated artifact drift in CI: run make api-cli-check locally to confirm whether src/fleet_manager/cli/commands/api/generated/operations.py and src/fleet_manager/cli/commands/api/generated/fleetctl-api-agent-command-reference.md match docs/openapi.yaml.

Logging out

Remove a stored profile when you are done: