Skip to main content
Move a fleet in and out of 2501 as a file. Export what you have, edit it in a spreadsheet, import it back. Useful for onboarding an inventory you already keep somewhere else, for scripted automation, and for copying a setup between environments. Hosts and agents each have their own file. Import order for a new fleet is hosts, then agents - an agent file points at hosts and cannot create them, so export your hosts after importing them to get the ids the agent file needs.

The files

One format per entity, in CSV or JSON. The columns are the same either way, and they are the fields of the API - so a file doubles as documentation of what you can set. A file belongs to one organization. The organization comes from the request, and everything a row points at is an id, which only means something inside that organization.

hosts

agents

In JSON, an agent’s credentials are one credential_config array instead of the three columns. Use JSON when you need something the columns cannot express: an optional credential, two credentials on one role, a hand-tuned order, or a public key (which has no column, because it is stored but never used to build a connection). Ids come from an export - a host’s is also in the URL of its page in Command Center. Start a hand-written agent file from a hosts export, so the host ids are already right.

Host tags are a closed list

tags is the one column whose values are not free text. Operational rules and prompts target these tags, so a tag nobody defined matches nothing — you get silent no-ops rather than an error when you type it. Four closed namespaces, and one open one: tech: values are deliberately unversioned: the specific version a host runs belongs in knowledge, so a rule tagged tech:tomcat matches every Tomcat host. Two behaviours worth knowing:
  • Only tags a row introduces are checked. A tag already stored on a host stays editable even if the vocabulary changes later, so one legacy tag cannot block every future edit of that host.
  • type:jump-host is managed for you, in lockstep with is_jump_host. Setting it by hand does nothing; removing it does not unflag the host.

References

Anywhere a file points at another entity - a credential, a specialty, an agent’s host - write its id. Exports write ids, and an id survives a rename. There is one exception, so that a whole fleet fits in one file: in a hosts file, jump_hosts may hold the name of a row above it, which is how you create a bastion and the hosts routed through it in a single import. That name is matched only against earlier rows of the same file, never against your existing hosts. So file order matters: put a bastion above the hosts routed through it, and the innermost bastion first in a chain. A reference pointing further down the file is an error that tells you which line to move, and the preview reports all of them at once. A two-hop fleet in one file, ordered innermost bastion first. edge-gw is written before bastion-dmz refers to it, and both before sensor-01 routes through the pair:
Swap the first and third lines and sensor-01 fails with “edge-gw is defined on row 3, below this one”. Everything else a file references - credentials, specialties, an agent’s host - must already exist.

What an import does to existing data

Two rules cover everything. A row with an id updates that entity. A row without one creates it. There is no third case. An id that does not exist, or belongs to another organization, is an error - an id never creates one. An update states the whole record. A file that updates anything has to carry every column: a value sets a field, an empty cell clears it, and a missing column is rejected with the column named. There is no partial update by file. What that means in practice:
  • Bulk-edit by editing an export. An export already contains every column, so export → change what you want → import does exactly what it looks like: the fleet matches the file.
  • A two-column update file is an error, not a shortcut. id,knowledge will not import. That is deliberate: the alternative is a file that quietly blanks every field it forgot to mention.
  • To change a single host or agent, use its own API endpoint (POST /api/v1/hosts/{id}) rather than a file.
  • A file that only creates may carry any subset of columns, because there is nothing to lose - name,private_ip,jump_hosts is a perfectly good first-import file. Anything the file leaves out takes its default.
In JSON the same rule applies per key: an update row carries every key, and null clears. Validation runs on the result, not on the row: a create must produce a complete, valid entity, and an update may not clear something mandatory or leave an invalid combination. Nothing can produce a half-valid host or agent - a row that would fails instead. Re-importing a file whose id cells are empty tries to create everything again. Host names must be unique in an organization, so you get errors rather than duplicates - keep the ids if you want a file you can re-apply.

Importing

In Command Center, open Hosts or Agents and use Import. Pick a .csv or .json file and press Preview first: the preview is the real server verdict for every row - names resolved against your live inventory, licences checked - not a guess made in the browser. Nothing is written until you confirm. Options:
  • Stop at the first failure. Off by default, so every row is attempted and you get a full report in one pass. On, the run stops at the first failing line and the rows below it are left untouched.
Rows are applied in file order. Every one comes back with a result - created, updated, skipped or failed - against its line number, so a partial import tells you exactly what happened where. There is no rollback: rows that succeeded stay.

Via the API

The same endpoints back the UI, so anything you can do in Command Center you can script.
dry_run: true validates everything and writes nothing. The other control is on_error (continue, the default, or stop). A batch is capped at 10000 rows. The response is HTTP 200 with one result per input row even when every row failed - the per-row verdicts are the answer. A 4xx means the request itself was wrong: bad authentication, no access to the organization, a malformed envelope, or a file-level problem such as an unknown column, which fails the whole file because no row can be trusted after it.

Exporting

Export on the Hosts or Agents page downloads the current organization as CSV. Or call the endpoint:
Add template=true for the header row alone, as a starting point for a hand-written file. Reading requires read access, importing requires write access - so an auditor can export a fleet and can never import one. Secrets never leave. A credential is exported as a reference, never its value. A fleet rebuilt from an export needs its secrets supplied again. Export is capped at 10000 rows, the same as import, so an export is always a file that can be imported.

Round trip

Exporting and re-importing without editing reports every row as updated and changes nothing. This holds within one organization: every reference in the file is an id, and ids do not exist in another organization. Two more edges:
  • A CSV export of an agent loses credential detail the role columns cannot hold - an optional credential, two credentials on one role, a custom order, a public key. JSON keeps all of it, and a lossy CSV export says so in an x-export-lossy response header.
  • An agent with no host is not a valid row in either format, so it is left out of the file and counted in a response header.

Versioning

The columns are part of the v1 API contract. New optional columns can be added, and files written today keep importing. Renaming or removing a column, or changing what one means, would be a new API version with new templates - your v1 files would keep importing against v1.