# CLI Scripting & Piping

The Bkper CLI is designed for scripting. Every data command outputs JSON, and selected write commands accept piped JSON input — making it easy to build data pipelines, batch operations, and automated workflows.

## Output format

All data commands output **JSON only**. Results go to stdout. Hints (such as the next-page command), warnings, and notices go to stderr, so stdout is always parseable.

| Output                                                        | Shape                                                              |
| ------------------------------------------------------------- | ------------------------------------------------------------------ |
| Lists (`book list`, `transaction list`, batch writes, ...)     | `{"items":[...]}`, plus `"cursor"` only when another page exists   |
| Single items (`get`, `create`, `update`, ...)                 | One JSON object                                                    |
| Balances (`balance list`)                                     | A 2D matrix: header row first, then one row per account or group   |

```bash
# List output: one record per line inside the envelope
bkper account list -b abc123
# ]}
```

**Output details:**

- Lists and balance matrices put one record or row per line, so `head`, `grep`, and `wc -l` work on records.
- Single items are pretty-printed on an interactive terminal and compact (one line) otherwise.
- Transactions include `creditAccount.name` and `debitAccount.name`, so the from/to flow is readable without an account lookup. Custom properties are always included.
- Transaction `amount` values are exact decimal strings.
- Balance values are unformatted JSON numbers, dates are ISO `yyyy-MM-dd`, and account and group properties are extra columns. Group hierarchy is shown by indenting names 4 spaces per level.

`--json` and `--format json` are accepted for compatibility and have no effect. Table and CSV output formats were removed in CLI v5; reshape JSON with [jq](https://jqlang.github.io/jq/) instead.

### Reshape with jq

```bash
# Iterate list items
bkper transaction list -b abc123 -q "on:2025-01" | jq '.items[]'

# Pick columns
bkper transaction list -b abc123 -q "on:2025-01" | \
  jq '.items[] | {date, amount, from: .creditAccount.name, to: .debitAccount.name, description}'

# Export transactions to CSV
bkper transaction list -b abc123 -q "on:2025-01" | \
  jq -r '.items[] | [.date, .amount, .creditAccount.name, .debitAccount.name, .description] | @csv'

# Export a balance matrix to CSV
bkper balance list -b abc123 -q "on:2025-12-31" | jq -r '.[] | @csv'

# Read the next-page cursor
bkper transaction list -b abc123 -q "on:2025" --limit 100 | jq -r '.cursor // empty'
```

> **Tip: AI agent guidance**
> When using the CLI from an AI agent or automated script, read the JSON output directly or narrow it with jq, and use stdin piping for batch operations. For accounting numbers, compute totals with a deterministic script or query rather than summing by eye.
## Query semantics quick reference

Use these rules in scripts to avoid ambiguous or empty results:

- `on:` supports year, month, and day (`on:2025`, `on:2025-01`, `on:2025-01-31`).
- `after:` is **inclusive** and `before:` is **exclusive**.
- A full-year range uses next-year boundary:
    - `after:2025-01-01 before:2026-01-01`

```bash
# Full year with on:
bkper transaction list -b $BOOK_ID -q "on:2025"

# Same full year with explicit boundaries
bkper transaction list -b $BOOK_ID -q "after:2025-01-01 before:2026-01-01"
```

## Batch operations

Write commands (`account create`, `transaction create`, `transaction update`) accept JSON piped via stdin. The input format follows the [Bkper API Types](https://raw.githubusercontent.com/bkper/bkper-api-types/refs/heads/master/index.d.ts) — a single JSON object or an array of objects.

Groups are created explicitly with `bkper group create --name` and optional `--parent`, so hierarchy stays deterministic.

### Creating in batch

```bash
# Create transactions from JSON
echo '[{
  "date": "2025-01-15",
  "amount": "100.50",
  "creditAccount": {"name": "Bank Account"},
  "debitAccount": {"name": "Office Supplies"},
  "description": "Printer paper",
  "properties": {"invoice": "INV-001"}
}]' | bkper transaction create -b abc123

# Create accounts
echo '[{"name":"Cash","type":"ASSET"},{"name":"Revenue","type":"INCOMING"}]' | \
  bkper account create -b abc123

# Create a group explicitly
bkper group create -b abc123 --name "Fixed Costs" --hidden

# Pipe from any script that outputs JSON
python export_bank.py | bkper transaction create -b abc123
```

Batch results use the same `{"items":[...]}` envelope as lists:

```bash
bkper account create -b abc123 < accounts.json
# ]}
```

Stdin accepts a single object, an array, or the `{"items":[...]}` envelope, so list and batch output can be piped directly into another command.

### Adding properties via CLI flag

The `--property` flag can add or override properties from the stdin payload:

```bash
echo '[{"name":"Cash","type":"ASSET"}]' | \
  bkper account create -b abc123 -p "region=LATAM"
```

## Piping between commands

All JSON output is designed to feed directly into other commands. This is the most powerful pattern — combining commands into pipelines:

### Copy data between books

```bash
# Copy all accounts from one book to another
bkper account list -b $BOOK_A | bkper account create -b $BOOK_B

# Copy transactions matching a query
bkper transaction list -b $BOOK_A -q "after:2025-01-01" | \
  bkper transaction create -b $BOOK_B
```

Recreate groups explicitly with `bkper group create --name ... --parent ...` before copying accounts that reference them.

### Clone a full chart of accounts

```bash
# Recreate the group hierarchy explicitly
bkper group create -b $DEST --name "Assets"
bkper group create -b $DEST --name "Current Assets" --parent "Assets"

# Then copy accounts and transactions
bkper account list -b $SOURCE | bkper account create -b $DEST
bkper transaction list -b $SOURCE -q "after:2025-01-01" | \
  bkper transaction create -b $DEST
```

### Batch updates with jq

Use [jq](https://jqlang.github.io/jq/) to transform data between commands:

```bash
# List transactions, modify descriptions, pipe back to update
bkper transaction list -b $BOOK -q "after:2025-01-01" | \
  jq '[.items[] | .description = "Updated: " + .description]' | \
  bkper transaction update -b $BOOK

# Add a property to all matching transactions
bkper transaction list -b $BOOK -q "account:Expenses" | \
  bkper transaction update -b $BOOK -p "reviewed=true"

# Batch update checked transactions
bkper transaction list -b $BOOK -q "is:checked after:2025-01-01" | \
  bkper transaction update -b $BOOK --update-checked -p "migrated=true"
```

### Daily export

```bash
#!/bin/bash
# Export yesterday's transactions to CSV
DATE=$(date -d "yesterday" +%Y-%m-%d)
bkper transaction list -b $BOOK_ID -q "on:$DATE" | \
  jq -r '.items[] | [.date, .amount, .creditAccount.name, .debitAccount.name, .description] | @csv' \
  > "export-$DATE.csv"
```

### Bulk categorization

```bash
#!/bin/bash
# Add a property to all uncategorized transactions
bkper transaction list -b $BOOK_ID \
  -q "account:Uncategorized" | \
  bkper transaction update -b $BOOK_ID -p "needs_review=true"
```

## Combining with other tools

The CLI works with standard Unix tools:

```bash
# Count transactions matching a query
bkper transaction list -b $BOOK_ID -q "after:2025-01-01" | jq '.items | length'

# Extract specific fields with jq
bkper account list -b $BOOK_ID | \
  jq '[.items[] | {name, type}]'

# Sort by amount
bkper transaction list -b $BOOK_ID -q "after:1900-01-01" | \
  jq '.items | sort_by(.amount | tonumber) | reverse'
```

## Full CLI reference

For the complete command reference including all options, see the [bkper-cli app page](https://preview.bkper.com/apps/bkper-cli.md) or run `bkper --help`.
