# Scripts (Full)

---
source: /docs/platform/scripts/cli-pipelines.md

# 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`.

---
source: /docs/platform/scripts/node-scripts.md

# Node.js Scripts

For tasks that go beyond CLI piping — complex logic, external API integration, scheduled jobs — write a Node.js script with `bkper-js`.

## Setup

```bash
# Create a script project
mkdir my-bkper-script && cd my-bkper-script
npm init -y
npm install bkper-js bkper
```

Authenticate once via the CLI:

```bash
bkper auth login
```

## The pattern

Every script follows the same structure: authenticate, get a book, do work.

```ts
import { Bkper } from 'bkper-js';
import { getOAuthToken } from 'bkper';

Bkper.setConfig({
    oauthTokenProvider: async () => getOAuthToken(),
});

const bkper = new Bkper();
const book = await bkper.getBook('your-book-id');

// Your logic here
```

### Export balances to JSON

```ts
import { Bkper } from 'bkper-js';
import { getOAuthToken } from 'bkper';
import { writeFileSync } from 'fs';

Bkper.setConfig({
    oauthTokenProvider: async () => getOAuthToken(),
});

const bkper = new Bkper();
const book = await bkper.getBook('your-book-id');

const report = await book.getBalancesReport('on:2025-12-31');
const rows = report.getBalancesContainers().map(container => ({
    name: container.getName(),
    balance: container.getCumulativeBalance().toString(),
}));

writeFileSync('balances.json', JSON.stringify(rows, null, 2));
console.log(`Exported ${rows.length} balances`);
```

### Bulk-create accounts from a JSON export

```ts
import { Account, Bkper } from 'bkper-js';
import { getOAuthToken } from 'bkper';
import { readFileSync } from 'fs';

type AccountInput = {
    name: string;
    type: 'ASSET' | 'LIABILITY' | 'INCOMING' | 'OUTGOING';
    groups?: Array<{ id?: string; name?: string }>;
};

Bkper.setConfig({
    oauthTokenProvider: async () => getOAuthToken(),
});

const bkper = new Bkper();
const book = await bkper.getBook('your-book-id');

const data: AccountInput[] = JSON.parse(readFileSync('accounts.json', 'utf-8'));

const accounts = data.map(acc => {
    const account = new Account(book).setName(acc.name).setType(acc.type);
    if (acc.groups?.length) {
        account.setGroups(acc.groups);
    }
    return account;
});

const created = await book.batchCreateAccounts(accounts);
console.log(`Created ${created.length} accounts`);
```

### Query transactions and generate a report

```ts
import { Amount, Bkper } from 'bkper-js';
import { getOAuthToken } from 'bkper';

Bkper.setConfig({
    oauthTokenProvider: async () => getOAuthToken(),
});

const bkper = new Bkper();
const book = await bkper.getBook('your-book-id');

const result = await book.listTransactions('account:Expenses after:2025-01-01');

let total = new Amount(0);
for (const tx of result.getItems()) {
    const amount = tx.getAmount();
    if (!amount) continue;

    total = total.plus(amount);
    console.log(`${tx.getDate()} | ${tx.getDescription()} | ${amount.toString()}`);
}
console.log(`\nTotal: ${total.toString()}`);
```

### With cron

```bash
# Run daily at 8am
0 8 * * * cd /path/to/script && node export.mjs
```

### In CI environments

The CLI helper `getOAuthToken()` is designed for local developer machines, where the CLI can store and refresh credentials. In GitHub Actions or other unattended environments, provide your own token source:

```ts
import { Bkper } from 'bkper-js';

const bkper = new Bkper({
    oauthTokenProvider: async () => {
        const token = process.env.BKPER_OAUTH_TOKEN;
        if (!token) {
            throw new Error('BKPER_OAUTH_TOKEN is not set');
        }
        return token;
    },
});
```

Use this pattern only when another system is already issuing and rotating the token for the job. For unattended long-running automation, implement your own OAuth flow instead of relying on the CLI's locally stored credentials.

## Error handling

Wrap API calls with proper error handling:

```ts
import { BkperError } from 'bkper-js';

try {
    const book = await bkper.getBook('your-book-id');
    // ...
} catch (error) {
    if (error instanceof BkperError && error.code === 404) {
        console.error('Book not found');
    } else if (error instanceof BkperError && error.code === 403) {
        console.error('No access to this book');
    } else if (error instanceof Error) {
        console.error('API error:', error.message);
    } else {
        console.error('Unknown error:', String(error));
    }
    process.exit(1);
}
```

## When to use scripts vs apps

| Scenario                                       | Use                                       |
| ---------------------------------------------- | ----------------------------------------- |
| One-off data migration                         | Script                                    |
| Scheduled export/report                        | Script                                    |
| Reacting to book events in real time           | [Platform app](https://preview.bkper.com/docs/platform/apps/overview.md) |
| Custom UI for users                            | [Platform app](https://preview.bkper.com/docs/platform/apps/overview.md) |
| Complex multi-step workflow with external APIs | Script or app, depending on trigger       |

## Next steps

- [bkper-js API Reference](https://preview.bkper.com/docs/api/bkper-js.md) — Full SDK documentation
- [CLI Scripting & Piping](https://preview.bkper.com/docs/platform/scripts/cli-pipelines.md) — For simpler tasks, the CLI may be enough
- [Direct API Usage](https://preview.bkper.com/docs/platform/scripts/rest-api.md) — Use the REST API from any language

---
source: /docs/platform/scripts/rest-api.md

# Bkper REST API Authentication and Direct Usage

The Bkper REST API is the universal interface for interacting with Bkper Books. Every library and tool — [bkper-js](https://preview.bkper.com/docs/platform/tools/libraries.md#bkper-js), [bkper-gs](https://preview.bkper.com/docs/platform/tools/libraries.md#bkper-gs), the [CLI](https://preview.bkper.com/docs/platform/tools/cli.md) — is built on top of it. This page shows how to call it directly from any language.

If you are using an official SDK, see the library-specific guides instead:

- **Node.js / CLI** — [Node.js Scripts](https://preview.bkper.com/docs/platform/scripts/node-scripts.md)
- **Browser apps** — [Platform Apps](https://preview.bkper.com/docs/platform/apps/overview.md)
- **Google Apps Script** — [Apps Script Development](https://preview.bkper.com/docs/platform/google-workspace/apps-script.md)

## Base URL

```
https://api.bkper.app
```

All API calls use this endpoint:

- `https://api.bkper.app/v5/books` — List books
- `https://api.bkper.app/v5/books/{bookId}` — Get a specific book

## Specifications

The API publishes an [OpenAPI](https://swagger.io/resources/open-api/) specification:

- [Bkper REST OpenAPI specification](https://bkper.com/docs/api/rest/openapi.json)

You can use this specification to generate client libraries with tools like [OpenAPI Generator](https://openapi-generator.tech/) in the language of your choice.

For **TypeScript**, we maintain an updated type definitions package:

- [`@bkper/bkper-api-types`](https://www.npmjs.com/package/@bkper/bkper-api-types)

## Authentication

Every request must include a valid OAuth2 access token in the `Authorization` header:

```
Authorization: Bearer YOUR_ACCESS_TOKEN
```

No API key is required to authenticate — the Bkper API proxy provides a managed key with shared quota.

### Obtaining a token

For local development and scripts, the easiest path is through the [Bkper CLI](https://preview.bkper.com/docs/platform/tools/cli.md):

```bash
bkper auth login
bkper auth token
```

For unattended environments (CI, servers), provide your own token source using the OAuth2 flow that matches your setup. See [CLI → Authenticating scripts and local development](https://preview.bkper.com/docs/platform/tools/cli.md#authenticating-scripts-and-local-development) for the canonical pattern used by the `bkper-js` SDK.

## Direct HTTP calls

Send JSON payloads with `Content-Type: application/json`.

### List books

```bash
curl -s https://api.bkper.app/v5/books \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
```

### Create a transaction

```bash
curl -s https://api.bkper.app/v5/books/BOOK_ID/transactions \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "date": "2025-01-15",
    "amount": "100.50",
    "creditAccount": {"name": "Bank Account"},
    "debitAccount": {"name": "Office Supplies"},
    "description": "Printer paper",
    "properties": {"invoice": "INV-001"}
  }'
```

### Using fetch (browser or Node.js)

```js
const response = await fetch('https://api.bkper.app/v5/books/BOOK_ID/transactions', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_ACCESS_TOKEN',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    date: '2025-01-15',
    amount: '100.50',
    creditAccount: { name: 'Bank Account' },
    debitAccount: { name: 'Office Supplies' },
    description: 'Printer paper',
    properties: { invoice: 'INV-001' },
  }),
});

const transaction = await response.json();
```

### Using Python requests

```python
import requests

response = requests.post(
    "https://api.bkper.app/v5/books/BOOK_ID/transactions",
    headers={"Authorization": "Bearer YOUR_ACCESS_TOKEN"},
    json={
        "date": "2025-01-15",
        "amount": "100.50",
        "creditAccount": {"name": "Bank Account"},
        "debitAccount": {"name": "Office Supplies"},
        "description": "Printer paper",
        "properties": {"invoice": "INV-001"},
    },
)

transaction = response.json()
```

## Custom API key

For dedicated quota and project-level usage tracking, you can optionally configure your own API key:

1. Join [bkper@googlegroups.com](https://groups.google.com/g/bkper) to unlock access to enable the API on your project
2. [Create a new GCP project](https://console.cloud.google.com/projectcreate), or select an existing one
3. [Enable the Bkper API](https://console.cloud.google.com/apis/library/app.bkper.com) in the Google Cloud Console
4. [Create an API key](https://console.cloud.google.com/apis/credentials/key)
   - [Add API Restrictions](https://cloud.google.com/docs/authentication/api-keys#adding_api_restrictions) to `app.bkper.com` API only

Send your API key in the `bkper-api-key` HTTP header:

```
bkper-api-key: YOUR_API_KEY
```

> **Note**
> API keys are for project identification and quota management only, not for authentication. Do not store API keys in your code. See [securing an API key](https://cloud.google.com/docs/authentication/api-keys#securing_an_api_key) best practices.
> **Tip**
> For Google Apps Script, you can store the API key in the [Script Properties](https://developers.google.com/apps-script/reference/properties/properties-service#getScriptProperties()). To store it, open the online editor, *File > Project properties > Script properties*.
### Metrics

With your own API key, you can view detailed [metrics on the GCP Console](https://console.cloud.google.com/apis/api/app.bkper.com/metrics) for your project's API calls:

![REST API Metrics](https://preview.bkper.com/docs/_astro/bkper-rest-api-metrics.DHXQ7CRF.png)

The [metrics dashboard](https://console.cloud.google.com/apis/api/app.bkper.com/metrics) provides information about endpoint calls, latency, and errors — a good overview of your integration's health.

### Quota

The [quotas dashboard](https://console.cloud.google.com/apis/api/app.bkper.com/quotas) provides details of the current default and quota exceeded errors.

The default shared quota is **60 requests per minute**. If you need higher limits with your own API key, please get in touch so we can discuss your case.
