Skip to main content

ksef2 CLI

Scriptable terminal workflows for the ksef2 Python SDK.

Use local profiles, environment-backed authentication, JSON output, and resumable workflow files to automate KSeF invoicing from shells, CI jobs, and operational scripts.

What is ksef2 CLI?

ksef2-cli exposes the ksef2 Python SDK as a command-line tool. It is designed for developers and operators who want to authenticate with KSeF, query invoice metadata, send invoices, download invoice XML, export packages, inspect sessions, and manage administrative resources without writing Python code.

The CLI avoids hidden state by default. Commands can read credentials from global options or environment variables, while local profiles store reusable non-secret defaults such as environment, NIP, auth method, certificate paths, and secret environment variable names.

This project is not published, endorsed, or supported by Poland's Ministry of Finance. Official KSeF documentation remains the source of truth for API behavior.

Install

KSeF2 CLI 1.0 requires Python 3.12 or newer and the published ksef2 SDK 1.1 or newer, below 2.0. Install the CLI from PyPI:

uv tool install ksef2-cli

or:

pipx install ksef2-cli

The package exposes two equivalent executables:

ksef2 --help
ksef2-cli --help

Package metadata requires the ksef2 SDK >=1.1,<2, including fixes for certificate retrieval and offline PDF code lookups. tests/unit/test_release_metadata.py fails if the project version, those bounds, ksef2_cli.__version__, or the locked SDK drift apart.

Check what you are running, CLI and SDK together:

ksef2 --version
ksef2-cli 1.0.0
ksef2 1.1.0

--version is read before the local config file, so it answers even with a broken or missing config. It prints plain text in every output mode.

Choose an environment

The default environment is TEST. An explicit --env wins over a profile, and a profile's environment wins over the default, so --env production and a profile that says environment = "production" both keep working unchanged:

ksef2 --env production --nip "$KSEF2_NIP" --token "$KSEF2_TOKEN" --json auth login

Examples below name TEST explicitly. A copy-pasted command that omits --env and a profile now talks to TEST rather than to production.

Authenticate

For repeated local work, create a profile once and let commands inherit its environment, NIP, and authentication settings:

ksef2 profile create test-company \
  --env test \
  --nip 5261040828 \
  --test-cert

ksef2 --profile test-company --json auth login

For CI and one-off scripts, pass global options before the command group:

ksef2 --env test --nip "$KSEF2_NIP" --token "$KSEF2_TOKEN" --json \
  invoices metadata \
  --role seller \
  --date-from 2026-01-01T00:00:00Z \
  --all

Supported direct authentication methods:

  • --token or KSEF2_TOKEN
  • --test-cert for the TEST environment
  • --cert and --key for PEM XAdES credentials
  • --p12 for PKCS#12/PFX XAdES credentials
  • --auth-state to reuse state saved by auth login --save-state

auth login, auth resume, and auth refresh mask both token values in text and in JSON output. Each line shows a short prefix and suffix of the token next to its expiry time, so a log, a shell transcript, or a pasted issue comment keeps nothing reusable. --show-tokens prints the raw bearer tokens instead, and a command that also uses --save-state stays masked even under --show-tokens, because the file is where the credential belongs. When a later command needs the credential, save it rather than capturing the output.

Saving auth state lets later commands skip a fresh KSeF login:

ksef2 --profile test-company --json auth login --save-state ksef2-auth.json
ksef2 --env test --auth-state ksef2-auth.json --json sessions auth-list

--save-state holds the access and refresh bearer tokens in plain text. The CLI writes the file with mode 0600, which stops other users reading it and does nothing else: it is not encrypted, and it survives chmod-preserving copies, backups, and git add. Keep it outside the repository, or ignore it in .gitignore, and put it somewhere private such as $XDG_RUNTIME_DIR on a shared machine.

Each command that reuses the state writes any refreshed token back to that file, and keeps that mode. auth refresh accepts the same --auth-state, so a refresh needs neither a token in the arguments nor a fresh login:

ksef2 --env test --auth-state ksef2-auth.json --json auth refresh

End the session and remove the file with ksef2 --auth-state ksef2-auth.json --json auth logout.

Send and download invoices

Send one XML invoice, wait for processing, save a UPO, and keep a receipt for later status checks:

ksef2 --profile test-company \
  invoices send invoice.xml \
  --wait \
  --upo-dir upos \
  --receipt invoice-receipt.json

Download the UPO later from the saved receipt:

ksef2 --profile test-company \
  invoices upo \
  --receipt invoice-receipt.json \
  --out invoice-upo.xml

Submit many XML files as one batch:

ksef2 --env test --nip "$KSEF2_NIP" --token "$KSEF2_TOKEN" \
  invoices send invoice-1.xml invoice-2.xml \
  --mode batch \
  --wait \
  --upo-dir upos

Prepare a batch separately when its parts should survive a failed upload:

ksef2 --env test --nip "$KSEF2_NIP" --token "$KSEF2_TOKEN" \
  batch prepare invoice-1.xml invoice-2.xml --out batch-parts

ksef2 --env test --nip "$KSEF2_NIP" --token "$KSEF2_TOKEN" \
  batch submit --prepared batch-parts --state-file batch-state.json

List and record-query commands print aligned plain-text tables with uppercase headers and one row per record. Empty lists print No results.. Identifiers stay complete; long names and descriptions may end in …, and optional columns may disappear on narrow terminals. Datetime columns use UTC, dates use YYYY-MM-DD, and amounts use two decimal places. Redirected output uses a 120-column layout. Pagination metadata, when returned by the command, appears below the table.

An online receipt carries the session AES key and IV in plain text, exactly as a session state file does; a batch receipt holds the session reference and the file names. Every one of them is written with mode 0600, and every one of them is a credential for as long as it can continue the workflow.

online send decides what to do with the session it used by how it got it: a session the command opened closes when the command ends, and a session resumed with --state-file stays open for the next command. --close and --keep-open say which you want regardless.

Stopping a send after KSeF accepted the invoice does not unsubmit it. Ctrl-C, a timed-out --wait, and a dropped connection all end the command with the invoice still inside the session, so read the saved receipt or state file, or ask with invoices status, online list, or batch status, before sending the file a second time.

Use --json before the command group for all fields or when scripts consume the output. JSON retains the SDK fields, values, and nulls.

Output and exit codes

A successful command writes to stdout: plain text by default, one JSON document with --json. Command errors go to stderr as plain text or one JSON object in --json mode, leaving stdout empty. A completed invoices send can instead return a result on stdout with rejected invoice items and exit 1.

In --json mode that object carries one error:

{"error": {"type": "RemoteServiceError", "title": "KSeF rejected the request", "message": "...", "details": [], "hints": [], "exit_code": 1, "ksef": {"status_code": 400, "ksef_code": 21405, "trace_id": "..."}}}

type names the CLI error class and title gives the short summary. details, hints, and exit_code are always present. The ksef object appears when KSeF answered, and holds that service's status_code, ksef_code, and trace_id; quote the trace ID when you report an incident. report_url appears on an unexpected CLI error, and traceback appears with --verbose. A rejected command line reports "type": "UsageError" with "exit_code": 2.

Exit code Meaning
0 Success.
1 Runtime failure: a rejected request, a KSeF timeout, missing or contradictory credentials, a bad file.
2 Usage failure: unknown option, missing argument, or an invalid value such as a bad NIP checksum or an unreadable --auth-state path.
130 Interrupted (Ctrl-C). The command may still have changed something server-side.

invoices send exits 1 when any file failed, after printing every per-file result.

Documentation

Development

uv sync --all-groups
uv run ksef2 --help
uv run ksef2 --version
uv run python -m coverage run -m pytest -q
uv run python scripts/validate_docs_frontmatter.py docs/

uv sync installs the published ksef2 SDK from PyPI, and uv.lock pins the exact version. See CONTRIBUTING.md for the checks that keep the two in step.

The CLI command domains live under src/ksef2_cli/commands/. Source documentation lives under docs/.

Contributing

Issues and pull requests are welcome. Before opening a PR, run the focused test or docs check that covers your change, and update source docs when command behavior changes.

Metadata

Release files for ksef2-cli 1.0.0

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

Source distribution (sdist)

Source distribution for ksef2-cli 1.0.0
File Size Uploaded Provenance Actions
ksef2_cli-1.0.0.tar.gz 1.8 MB With provenance Details for ksef2_cli-1.0.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for ksef2-cli 1.0.0
File Interpreter ABI Platform Actions
ksef2_cli-1.0.0-py3-none-any.whl Python 3 none any Details for ksef2_cli-1.0.0-py3-none-any.whl

Total release size: 1.9 MB

Release files / ksef2_cli-1.0.0.tar.gz

Download URL ksef2_cli-1.0.0.tar.gz
Size 1.8 MB
Tags Source
SHA-256 checksum
How to use checksums
df589d05f5fec82ea797676eb5d304de8851b6f8865682ef79dd3c7d2232b3ff
BLAKE2b-256 checksum
How to use checksums
58115060a847e291903f293573f8da4c5962f70515abbe840b631f025f8e5823
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 26, 2026.

Transparency log

Release files / ksef2_cli-1.0.0-py3-none-any.whl

Download URL ksef2_cli-1.0.0-py3-none-any.whl
Size 106.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
20e367a259c3984583310f343b77444d03e94693d5c25baff1db5037744dad3a
BLAKE2b-256 checksum
How to use checksums
37320887667da5257a1e8c15669a37733ddfb61b0cd6f74d75e0078c588225b9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 26, 2026.

Transparency log

Security for ksef2-cli 1.0.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for ksef2_cli-1.0.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 26, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

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