Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

unstract-cli

unstract — one CLI for the Unstract suite: extract a document with LLMWhisperer, run it through a Document Studio API deployment, get structured JSON back. It also clones one organization's resources into another.

curl -LsSf https://raw.githubusercontent.com/Zipstack/unstract-cli/main/install.sh | sh
unstract auth login                   # asks for your keys, checks them, stores them
unstract docstudio deployment ls      # what can I run?

For an agent or CI, no prompts and no file — the environment is the profile:

export UNSTRACT_ORG_ID=... UNSTRACT_DEPLOYMENT_KEY=... LLMWHISPERER_API_KEY=...
unstract -o json whisper extract ./doc.pdf
unstract -o json docstudio deployment run invoice-parser ./doc.pdf

The installer fetches uv if it is missing and installs the CLI with it; uv brings its own Python, so nothing on the machine has to match. Already have uv? uv tool install git+https://github.com/Zipstack/unstract-cli is the same thing. Set UNSTRACT_CLI_SOURCE to install a branch or a local checkout instead.

Or run it without installing: uvx --from git+https://github.com/Zipstack/unstract-cli unstract --discover groups.

Output

unstract prints a table by default — in a terminal and in a pipe alike, so what you see while trying something is what a script sees running it.

Parsing anything? Pass -o json. stdout then carries exactly one envelope, on success and on failure alike:

{"ok": true, "data": {...}, "error": null, "meta": {"contract_version": 1}}

-o json output depends on nothing but the command and its arguments — not the terminal, not the config, not the environment. -o raw prints one field unwrapped, for piping a document's text somewhere else. Diagnostics, warnings and progress always go to stderr.

Consuming the JSON: ignore fields you do not recognise, and refuse a meta.contract_version above the one you were written against. unstract --discover full publishes the whole contract alongside every command and flag.

If a coding agent is driving (detected from the environment it sets), the default becomes json. --agent yes|no forces that either way, and an explicit -o always wins over both.

Failures exit non-zero with a stable code. The codes are this CLI's own convention, not a service's — they are the ExitCode enum in core/errors.py, and --discover full publishes the table so a caller does not have to copy it:

Code Meaning
0 success
1 generic failure
2 usage error
3 authentication failed
4 not found
5 validation failed — also a completed run in which a document failed; the full result, successful documents included, is in error.details
6 rate limited
7 timed out (the job handle is in the error payload — resume, do not resubmit)
8 server error
9 result already consumed (one-shot read; use --save next time)
10 the result was read but could not be saved — it is in error.details
130 interrupted (128 + SIGINT) — the user stopped it, not a failure

Credentials

Three keys, each for one job:

  • LLMWhisperer key — extracts text (whisper …). Minted in the LLMWhisperer console.
  • Deployment key — runs deployments (deployment run, deployment status). Shown on the API deployment's own page in the Unstract UI; one an organisation admin mints under Settings → Global API Deployment Keys covers every deployment in the organisation.
  • Platform key — identifies the organisation and lists what is in it (auth whoami, deployment ls). Minted by an organisation admin under Settings → Platform API Keys.

auth login takes whichever of the three you have, checks the two it can (whoami for the platform key, the usage endpoint for the LLMWhisperer key; a deployment key has nothing side-effect-free to call and is stored as given) and writes them to one profile. Run it again to rotate a key. A login that stores a different host drops the profile's other keys rather than leave them beside a server that never accepted them: it asks first, or without a terminal fails until --force. Without a terminal pass them as flags — --platform-key, --deployment-key, --llmwhisperer-key, any one of them - to read from stdin.

Configuration

~/.unstract/config.toml, or a project-local .unstract.toml found by upward search, or $UNSTRACT_CONFIG, or --config. Every setting resolves flag > env > profile > built-in default, and the CLI is fully usable with no config file at all. The flag tier is the connection options on each product group — --base-url, --api-key, --org-id and --platform-key on docstudio, --base-url/--api-key on whisper, --base-url/--platform-key on auth — which override the profile for that one invocation without writing anything.

default_profile = "cloud-us"

[profiles.cloud-us.llmwhisperer]
base_url = "https://llmwhisperer-api.us-central.unstract.com/api/v2"
api_key = "env:LLMWHISPERER_API_KEY"

[profiles.cloud-us.docstudio]
base_url = "https://us-central.unstract.com"
org_id = "org_ABC123"
api_key = "env:UNSTRACT_DEPLOYMENT_KEY"
platform_key = "env:UNSTRACT_PLATFORM_KEY"

# Only for a deployment whose key differs from the one above.
[profiles.cloud-us.deployments."invoice-parser"]
api_key = "env:INVOICE_PARSER_KEY"

deployment run and deployment status take the API name as deployment ls prints it. ls itself authenticates with the platform key and refuses --api-key, which on docstudio means a deployment key. The key for a run resolves --api-key > $UNSTRACT_DEPLOYMENT_KEY > the deployment's own entry > the profile's api_key, so most profiles need no deployments section at all; config set docstudio api_key <key> --deployment <api_name> writes one. org_id lives on the docstudio block — auth login and auth whoami write the one the platform key resolves there. config init writes this shape minus platform_key and the deployments entry — both are the exception, not the starting point — plus a cloud-eu profile and an onprem-example shape to copy for a self-hosted install; only the active profile is ever resolved.

Either form works for a credential. auth login writes keys literally, having checked them at the moment it writes. env:VAR_NAME indirection — what config init writes and what the example above uses — keeps the secret out of the file, so it stays safe to copy or commit; that is the form for a shared machine or a CI checkout. Either way the file is created 0600, and config doctor warns when its mode is wider than that.

unstract config doctor reports where each setting resolved from — including whether an env: reference is actually set in the current process — without echoing any value. --probe also checks the two keys that can be checked — the platform key and the LLMWhisperer key, the same two auth login checks — and, with a platform key, warns about a deployments entry the organisation no longer has. It exits non-zero when one of its own checks failed, so a setup script can branch on it.

A project-local .unstract.toml found by upward search may not supply a key or base_url. Those are ignored, with a warning; everything else in it — profile selection, org_id — applies as usual. A checkout you did not write is not trusted to name the host your key is sent to. Name the file explicitly (--config or $UNSTRACT_CONFIG) and it is honoured in full.

What that protects is the key and the host, not the routing: org_id and profile selection stay repo-controllable by design, so a project file can still decide which organisation a command runs against on a host you trust. Read one before you run inside a checkout you did not write.

clone is the exception, and it is an operator command: a human moving one organisation's resources into another, holding two admin Platform keys. It is not part of the document-processing path the rest of this CLI wraps, so an agent serving a user request should not reach for it unasked. It talks to two deployments at once, which no single profile describes, so it takes both endpoints as flags and both keys from UNSTRACT_SRC_PLATFORM_KEY / UNSTRACT_TGT_PLATFORM_KEY — two keys for two organisations, so it reads neither the profile's platform_key nor $UNSTRACT_PLATFORM_KEY. It exits 0 when nothing failed, which is not the same as everything having moved: oversize and unsupported documents are skipped by design, and data.skipped counts them.

Development

uv venv && uv pip install -e '.[dev]'
uv run pytest   # offline; no network, no credentials
uv run ruff check .

Release files for unstract-cli 0.1.0rc1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for unstract-cli 0.1.0rc1
File Size Uploaded
unstract_cli-0.1.0rc1.tar.gz 168.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for unstract-cli 0.1.0rc1
File Interpreter ABI Platform
unstract_cli-0.1.0rc1-py3-none-any.whl Python 3 none any Details

Total release size:262.9 kB

Release files / unstract_cli-0.1.0rc1.tar.gz

Download URL unstract_cli-0.1.0rc1.tar.gz
Size 168.2 kB
Tags Source
SHA-256 checksum
How to use checksums
b1938ca0c2c571f427dc3678ceb4b2bf9a2bce69ceebb6ed1038eb38df273f0a
BLAKE2b-256 checksum
How to use checksums
a90a735c583f98021243ec1007875deee7edcdf7bc63236ef77332f83a73d544
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.6.14

Release files / unstract_cli-0.1.0rc1-py3-none-any.whl

Download URL unstract_cli-0.1.0rc1-py3-none-any.whl
Size 94.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
8130926eba46ac3d2b8d7fde8cebd365c350c97cf8e47effb60be34e98bf0cfe
BLAKE2b-256 checksum
How to use checksums
a4f1cc9505a805649411108441f58aa0e5c2e396d1bc8500b904967c3d16e7e2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.6.14

Release history Release notifications | RSS feed

0.1.1

2 release files

0.1.0

2 release files

This release

0.1.0rc1 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page