CLI

The Sarkoa CLI (sarkoa) uploads, lists, downloads, and manages files from the terminal. It works with the same account and API keys as the QGIS plugin.

Requirements: Python 3.10+ on Linux or macOS (curl installer or pipx). Debian/Ubuntu apt and PyPI are coming soon.

Install

Prefer inspecting the script before piping:

curl -fsSL https://packages.sarkoa.com/install.sh
curl -fsSL https://packages.sarkoa.com/install.sh | bash
sarkoa --help

pipx (CDN wheel)

pip/pipx need a versioned wheel filename in the URL. Resolve the wheels/LATEST pointer first (do not pass the rolling latest alias URL to pipx):

pipx install "https://packages.sarkoa.com/wheels/$(curl -fsSL https://packages.sarkoa.com/wheels/LATEST)"
sarkoa --help

Or: pip install "https://packages.sarkoa.com/wheels/$(curl -fsSL https://packages.sarkoa.com/wheels/LATEST)" inside a virtual environment.

Coming soon: PyPI and apt

When published: pipx install sarkoa, or the Debian/Ubuntu apt source at https://packages.sarkoa.com/apt.

More install options: Downloads.

Configuration

Point the CLI at your API key. Settings resolve in this order (first wins):

  1. --token TOKEN CLI flag (one-shot; not stored)
  2. SARKOA_* environment variables
  3. ~/.sarkoa/config JSON (via sarkoa config set)

The CLI talks to the Sarkoa web app by default. You do not need to set a base URL for normal use.

Persist credentials

sarkoa config set token YOUR_API_TOKEN
sarkoa config set org-id YOUR_ORG_UUID   # optional; same as SARKOA_ORGANIZATION_ID
sarkoa config show                      # token masked

You need an API key from the web app, see Authentication.

Environment variables

Variable Purpose
SARKOA_API_TOKEN API key from Profile → API & Integrations
SARKOA_ORGANIZATION_ID Org UUID from the web app URL (/org/<uuid>/…). Usually unnecessary (the API key already targets one org); required for download-bulk (also via org-id / --org)

download-bulk is the one command an API key cannot run. It goes through the manifest API, which accepts a signed-in session only, so an API key gets 403 there. Everything else on this page works with a key.

Commands

Grouped by what they act on. Most commands take --json where a machine-readable form makes sense, and --help on any subcommand lists its flags.

Files and folders

Command Purpose
sarkoa upload PATH Upload a file; optional --folder FOLDER to place it in a folder; optional --cloud-native PATH for a pre-built PMTiles/COG (required path when the source is > 10 GB, since remote optimize cannot run, see File optimization). Large transfers show a progress bar on a TTY (--progress / --no-progress)
sarkoa sync DIR Upload everything in a local directory that is not already in the library (--folder, --recursive, --dry-run). Run it with --dry-run first to see what it would send
sarkoa list List files: name, type (Polygon / Polyline / Raster bands / Folder), status, size, relative time, short ref (--json, --search, --folder)
sarkoa mkdir NAME Create a folder (--parent FOLDER to nest it)
sarkoa mv REF FOLDER Move a file or folder (--root to move to the library root)
sarkoa rename REF NAME Change a file or folder's display name. The stored object is untouched
sarkoa status REF Processing status and metadata (full ID, type, format)
sarkoa inspect REF Fields, statistics, geometry, and bands for styling decisions (--json). Run this before writing a style or a --where filter
sarkoa retry REF Requeue a file whose optimization failed. --dry-run prints the failure diagnosis without requeueing
sarkoa quota SIZE Ask whether an upload of that size would exceed the plan limit. SIZE is a byte count or a path (--json)
sarkoa download REF DEST Download to a destination path (progress bar on large TTY transfers)
sarkoa download-bulk Bulk download via the manifest API. Signed-in session auth only: an API key gets 403, so this one needs --org or a stored org-id
sarkoa view REF Open the file in the web app; --print-only
sarkoa delete REF --yes Delete (requires --yes; folders that still have contents also need --recursive)

Sharing

Command Purpose
sarkoa share create REF Create a public /share/… link (--access, --expires-days, --password, --json)
sarkoa share list REF List shares for a file (--json)
sarkoa share revoke REF SHARE_ID --yes Revoke a share (requires --yes)

Time series

Command Purpose
sarkoa series list List your Time series
sarkoa series create NAME Create a Time series container (see Time series)
sarkoa series append SERIES_ID PATH --captured-at ISO Add one dated snapshot to a series; optional --period-start, --idempotency-key, --temporal-mode off (recommended for vector snapshots)
sarkoa series status SERIES_ID REQUEST_ID Check whether an append finished

Maps

Command Purpose
sarkoa map list List custom maps (--json)
sarkoa map get REF Print or save a full map document (--json, --output)
sarkoa map create --file map.json Create a map from a map document (--dry-run, --json; --yes for public, password, or email-invite)
sarkoa map apply REF --file map.json Replace a map with a document (--expected-revision, --dry-run; --yes to publish)
sarkoa map validate --file map.json Validate a map document without saving
sarkoa map export REF --output map.json Export the current map document
sarkoa map clone REF Duplicate a map (--title, --json)
sarkoa map open REF Open the map viewer (--print-only)
sarkoa map embed REF Print a ready-to-paste iframe (--width, --height, --title) or just the URL (--url-only)
sarkoa map delete REF --yes Delete a map (requires --yes)
sarkoa map display REF Set visitor defaults: --mode 2d|3d and --projection mercator|flat|globe
sarkoa map access REF Set who can open the map: --mode organization|org|public|specific_users|invited_emails|password (--yes for public, password, and email invite)

Map layers

Command Purpose
sarkoa map layer add MAP --asset REF Add a file as a layer (--attribution-text, --attribution-url)
sarkoa map layer remove MAP LAYER Remove a layer
sarkoa map layer show|hide MAP LAYER Toggle layer visibility
sarkoa map layer style MAP LAYER --file style.json Replace a layer style
sarkoa map layer classify MAP LAYER --attribute FIELD Assign categorized class colours (--shuffle to recolour)
sarkoa map layer attribution MAP LAYER Set the cartographic credit for one layer
sarkoa map layer popup MAP LAYER Configure the click popup: --fields, --title-field, --image-fields, --disable, --clear
sarkoa map layer legend MAP LAYER Label a raster layer's legend: --title, --min-label, --max-label, --unit, --disable, --clear

Map review

Command Purpose
sarkoa map comment list MAP List review comments, open and resolved
sarkoa map comment add MAP Post a comment, or reply to a thread
sarkoa map comment resolve MAP COMMENT_ID Resolve a comment, or reopen it
sarkoa map comment delete MAP COMMENT_ID Delete a comment

Querying data

Command Purpose
sarkoa query REF Filter a hosted vector layer without downloading it: --bbox, --where FIELD:OP:VALUE (repeatable), --fields, --limit (default 100, max 1000), --offset, --no-geometry, --output. See Feature query
sarkoa stac search Search the raster catalogue: --bbox, --datetime, --collection {rgb,multispectral,singleband,elevation}, --limit, --offset

Webhooks

Command Purpose
sarkoa webhook list List endpoints (--events also prints the subscribable event types)
sarkoa webhook create URL Register an endpoint (--event repeatable, --description). Prints the signing secret once
sarkoa webhook test ID Send a signed test delivery now and show what the endpoint returned
sarkoa webhook rotate ID Issue a new signing secret (takes effect immediately, with no overlap)
sarkoa webhook deliveries Recent delivery attempts (--webhook-id, --status, --limit)
sarkoa webhook delete ID Remove an endpoint and its pending deliveries

Account

Command Purpose
sarkoa whoami Show email, organization, scopes, and auth method
sarkoa config … set / show / clear stored config

Folders in practice

Create a folder, upload into it, list its contents, or move files. Names, short refs, and full IDs all work as folder references.

sarkoa mkdir Surveys
sarkoa mkdir 2026 --parent Surveys
sarkoa upload ./data.geojson --folder Surveys
sarkoa list --folder Surveys
sarkoa mv ./other.geojson Surveys
sarkoa mv abc12345 --root
sarkoa delete Surveys --yes --recursive

sarkoa list without --folder shows the library root (folders and files that are not nested). Deleting a folder that still has contents requires --recursive.

Large series appends resume automatically if the connection drops, so you do not need to restart a long upload from the beginning.

Upload rate limits

Completing an upload (POST /api/uploads/complete) is limited to 30 requests per 10 minutes per API key. Presign allows 60 per 10 minutes. If you receive 429 Too Many Requests, wait for the number of seconds in the Retry-After response header before trying again. The CLI handles this automatically for completion. For bulk backfills, pace uploads (about one completion every 20 seconds stays under the cap).

Map documents

Custom maps are authored as JSON documents (apiVersion: "sarkoa.com/maps/v1"). A document can set the camera, basemap and theme, access mode, collaboration/download settings, and per-layer styles such as fill color, outline, categorized or graduated symbology, raster palettes, popups, and legends.

Example:

sarkoa map validate --file ./quake-map.json
sarkoa map create --file ./quake-map.json --json
sarkoa map layer add MAP_ID --asset faults.geojson --attribution-text "City of Ottawa" --attribution-url https://ottawa.ca
sarkoa map layer classify MAP_ID layer-a --attribute kind
sarkoa map layer classify MAP_ID layer-a --shuffle
sarkoa map display MAP_ID --mode 2d --projection globe
sarkoa map access MAP_ID --mode organization
sarkoa map layer style MAP_ID layer-a --file ./fault-style.json
sarkoa map open MAP_ID

Use --file=- to read a document from stdin. Prefer --expected-revision when updating an existing map so concurrent edits do not overwrite each other. Public, password-protected, and email-invite maps need --yes on map create, map apply, and map access.

Backfilling a directory

sarkoa sync uploads what is missing rather than re-sending what is already there, which makes it safe to re-run after a failure part way through a large batch.

sarkoa sync ./exports --folder Surveys --recursive --dry-run
sarkoa sync ./exports --folder Surveys --recursive

Always do the --dry-run pass first. It prints exactly what would upload and stops, which is the cheapest way to notice you pointed it at the wrong directory. Mind the completion rate limit above on large batches.

Querying instead of downloading

Once a layer is hosted, you can filter it server-side and get GeoJSON back:

sarkoa inspect parcels                 # see the attribute names first
sarkoa query parcels \
  --bbox -79.6,43.5,-79.1,43.9 \
  --where zone:eq:RM4 \
  --where assessed_value:gt:750000 \
  --fields address,zone,assessed_value \
  --output ./rm4.geojson

--no-geometry makes attribute-only work dramatically smaller. Full operator list and paging: Feature query.

Being told when processing finishes

Instead of polling sarkoa status in a loop:

sarkoa webhook create https://example.com/hooks/sarkoa \
  --event asset.processing.succeeded \
  --event asset.processing.failed
sarkoa webhook test WEBHOOK_ID

The signing secret prints once, on create. Store it then. If a delivery goes missing, sarkoa webhook deliveries --status exhausted has the HTTP code your endpoint returned and the error text. See Webhooks.

See sarkoa --help and each subcommand’s --help for flags.