> ## Documentation Index
> Fetch the complete documentation index at: https://docs.strandai.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Command line (strand)

> Run H&E slides through Lattice from your terminal. Upload, estimate, submit, wait, cancel, inspect, and export through the Python SDK.

The Python SDK includes the `strand` command for running the upload-to-results
workflow without a script. It uses the same `strand.Client` as the library and
MCP tools. Each command maps one-to-one to an SDK method and uses the same
argument names.

## Install

```bash theme={"dark"}
pip install strand-sdk
# with the extras needed to open results as AnnData:
pip install "strand-sdk[anndata]"
```

Installing the package puts `strand` on your `PATH`.

```bash theme={"dark"}
strand --help
strand --version
```

## Configuration

The CLI reads the same environment variables as `strand.Client()`.

| Variable          | Purpose                            | Default                    |
| ----------------- | ---------------------------------- | -------------------------- |
| `STRAND_API_KEY`  | API key (`sk-strand-…`). Required. | none                       |
| `STRAND_BASE_URL` | API base URL.                      | `https://app.strandai.com` |

```bash theme={"dark"}
export STRAND_API_KEY="sk-strand-…"
```

Generate a key at [app.strandai.com](https://app.strandai.com/settings/api-keys).
Commands print machine-readable JSON to stdout, so they compose with
[`jq`](https://jqlang.github.io/jq/) and shell pipelines. On failure the CLI
writes a one-line message to stderr and exits with a non-zero status.

## End-to-end run

```bash theme={"dark"}
# 1. Upload a slide. Capture the sample id from its current ingest snapshot.
sample_id=$(strand upload biopsy.svs --mpp 0.25 | jq -r .id)

# 2. Wait until automatic ingest makes the sample prediction-ready.
while :; do
  status=$(strand samples get "$sample_id" | jq -r .status)
  case "$status" in
    ready) break ;;
    *failed) echo "ingest failed: $status" >&2; exit 1 ;;
  esac
  sleep 2
done

# 3. Price the exact request without creating a job or reserving credits.
strand predict "$sample_id" --markers CD3e,CD8,PanCK --dry-run | jq

# 4. Submit the same prediction and capture its job id.
job_id=$(strand predict "$sample_id" --markers CD3e,CD8,PanCK | jq -r .job_id)

# 5. Wait for a terminal status (SSE with polling fallback).
strand wait "$job_id" --timeout 1800

# 6. Download OME-Zarr results or a pyramidal OME-TIFF.
strand results "$job_id" --out ./results/
strand export "$job_id" --format ome-tiff --out ./result.ome.tiff --timeout 1800
```

## Commands

### `strand markers`

List the markers your account can request (credit-free). The names printed are
exactly what `strand predict` will accept for your account.

```bash theme={"dark"}
strand markers
```

Wraps `client.markers.list()`.

### `strand upload`

Upload a local whole-slide image. The command performs a resumable chunked
upload and prints the sample's current ingest state. Dimensions may still be
absent while ingest preprocessing continues.

```bash theme={"dark"}
strand upload biopsy.svs
strand upload biopsy.svs --mpp 0.25
strand upload biopsy.svs --content-type image/tiff --if-not-exists
```

| Option                | Description                                                                                                                    |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `--mpp FLOAT`         | Microns per pixel for the slide (isotropic). Persisted at creation and takes precedence over the slide's own calibrated scale. |
| `--content-type TEXT` | MIME type override. Auto-detected from the file extension when omitted.                                                        |
| `--if-not-exists`     | Dedup by content hash. Skips the byte upload when a matching sample already exists in your org.                                |

Wraps `client.uploads.upload_file(...)`. Before an immediate dry run or
submission, poll `strand samples get SAMPLE_ID` until `status` is `ready`.

### `strand predict`

Price or submit a prediction against an uploaded sample. `--dry-run` prints
the full estimate without creating a job or reserving credits. The default
submission reserves credits and prints the job id.

```bash theme={"dark"}
strand predict <sample-id> --markers CD3e,CD8,PanCK --dry-run
strand predict <sample-id> --markers CD3e,CD8,PanCK
strand predict <sample-id> -m CD8,Ki67 --model v0.7
```

| Argument / option | Description                                                                 |
| ----------------- | --------------------------------------------------------------------------- |
| `SAMPLE_ID`       | Sample id returned by `strand upload`.                                      |
| `--markers`, `-m` | Comma-separated marker names like `CD3e,CD8,PanCK`.                         |
| `--model TEXT`    | Lattice version. The server picks the current default when omitted.         |
| `--dry-run`       | Validate and price the request without creating a job or reserving credits. |

Wraps `client.predict.submit(..., dry_run=...)`. See [Markers](/markers/index) for the
predictable panel.

### `strand status`

Print a point-in-time status snapshot for a job: state, progress, markers,
and the served model.

```bash theme={"dark"}
strand status <job-id>
```

Wraps `client.jobs.get(<job-id>).status`.

### `strand wait`

Wait for a terminal job snapshot. The command prefers the job's SSE stream and
falls back to status polling if the stream disconnects.

```bash theme={"dark"}
strand wait <job-id>
strand wait <job-id> --timeout 1800 --poll-interval 2
```

| Option                  | Description                                                            |
| ----------------------- | ---------------------------------------------------------------------- |
| `--timeout FLOAT`       | Maximum seconds to wait. Omit it to wait indefinitely.                 |
| `--poll-interval FLOAT` | Seconds between status requests when polling is needed. Defaults to 2. |

Wraps `client.jobs.get(<job-id>).wait(...)`. A failed job or elapsed timeout
prints an error and exits non-zero; completed, partially failed, and cancelled
snapshots print as JSON.

### `strand cancel`

Cancel an eligible in-flight job, release its credit reservation, and print
the refreshed post-cancel snapshot.

```bash theme={"dark"}
strand cancel <job-id>
```

Wraps `client.jobs.get(<job-id>).cancel()`.

### `strand results`

Download a completed job's OME-Zarr result tree to a local directory. Streams
the files through the API-key result proxy.

```bash theme={"dark"}
strand results <job-id> --out ./results/
```

| Option        | Description                                                           |
| ------------- | --------------------------------------------------------------------- |
| `--out`, `-o` | Directory to write into. Created if missing. Defaults to `./results`. |

Wraps `client.jobs.get(<job-id>).download_results(dir)`.

### `strand export`

Request or reuse a generated result export, wait until it is ready, write it to
the required destination path, and print that path.

```bash theme={"dark"}
strand export <job-id> --format ome-tiff --out ./result.ome.tiff
strand export <job-id> --format ome-zarr-zip --out ./result.zip --include-segmentation
```

| Option                   | Description                                                                    |
| ------------------------ | ------------------------------------------------------------------------------ |
| `--format`               | Required: `ome-tiff` or `ome-zarr-zip`. Native OME-Zarr uses `strand results`. |
| `--out`, `-o`            | Required destination file. Parent directories are created.                     |
| `--include-segmentation` | Attach the latest mask/cell manifest to the export response.                   |
| `--timeout FLOAT`        | Maximum seconds to wait for export. Omit it to wait indefinitely.              |
| `--poll-interval FLOAT`  | Seconds between export-status requests. Defaults to 2.                         |

Wraps `client.jobs.get(<job-id>).download_export(...)`.

### `strand samples list`

List owned samples, the public cohort, or both through one cursor-paginated
surface. The JSON envelope contains `items` and `next_cursor`; every item has
canonical `id` and an `ownership` discriminator.

```bash theme={"dark"}
strand samples list
strand samples list --scope public --limit 50
strand samples list --scope all --cursor <next-cursor> --tag trial-042
```

| Option                        | Description                                              |
| ----------------------------- | -------------------------------------------------------- |
| `--scope [mine\|public\|all]` | Sample scope. Defaults to `mine`.                        |
| `--limit INTEGER`             | Page size (API range 1 to 100). Defaults to 48.          |
| `--cursor TEXT`               | Pagination cursor from a prior response's `next_cursor`. |
| `--tag TEXT`                  | Exact sample-tag filter.                                 |

Wraps `client.samples.list(...)`.

### `strand samples get`

Read an owned or public sample using its canonical id. Owned output includes
`jobs` and `job_count`. Public output includes geometry and marker channels.
`--download` mirrors a public sample's OME-Zarr store.

```bash theme={"dark"}
strand samples get <sample-id>
strand samples get <public-id> --download ./public-sample.zarr
```

Wraps `client.samples.get(...)`.

### `strand samples patch`

Patch an owned sample's display name, complete tag set, and/or physical scale.
Omitted options are not sent.

```bash theme={"dark"}
strand samples patch <sample-id> --name "Trial 42 baseline"
strand samples patch <sample-id> --tag trial-042 --tag baseline --mpp 0.26
strand samples patch <sample-id> --clear-name --clear-tags
```

| Option         | Description                                                                                 |
| -------------- | ------------------------------------------------------------------------------------------- |
| `--name TEXT`  | Set the display name. Mutually exclusive with `--clear-name`.                               |
| `--clear-name` | Revert the display name to the filename.                                                    |
| `--tag TEXT`   | Complete desired tag set; repeat for multiple tags. Mutually exclusive with `--clear-tags`. |
| `--clear-tags` | Clear all editable tags.                                                                    |
| `--mpp FLOAT`  | Set isotropic microns per pixel.                                                            |

Wraps `client.samples.patch(...)`.

### `strand uploads list`

List resumable upload records for your organization. This is distinct from the
scoped sample catalog.

```bash theme={"dark"}
strand uploads list
strand uploads list --limit 50 --cursor <next-cursor>
```

| Option            | Description                                              |
| ----------------- | -------------------------------------------------------- |
| `--limit INTEGER` | Page size (1 to 200). Defaults to 100.                   |
| `--cursor TEXT`   | Pagination cursor from a prior response's `next_cursor`. |

Wraps `client.uploads.list(...)`.

## See also

* [Python SDK](/sdks/python) for the full library surface and result handling.
* [SDK quickstart](/sdks/quickstart) for the Python and R end-to-end paths.
