npm package: @rainnworks/indy
Description: > Give AI agents full access to French accounting — categorize transactions, file VAT, upload receipts, read financial statements — all through [Indy.fr](https://app.indy.fr).
Latest version: 0.3.2

# @rainnworks/indy

> Give AI agents full access to French accounting — categorize transactions, file VAT, upload receipts, read financial statements — all through [Indy.fr](https://app.indy.fr).

[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
[![Runtime: Bun](https://img.shields.io/badge/Runtime-Bun-black.svg)](https://bun.sh)
[![Tools: 65](https://img.shields.io/badge/Tools-65-green.svg)](#tool-inventory-65-tools)

---

## What is Indy?

[Indy.fr](https://app.indy.fr) is a French accounting SaaS platform used by tens of thousands of freelancers, consultants, and small companies (IS/BIC regime). It handles:

- **Daily bookkeeping** — bank transaction sync, categorization against the Plan Comptable Général (PCG), receipt attachment
- **Tax declarations** — monthly/quarterly VAT (CA3/CA12), corporate tax advances (2571), DAS2 (fees), annual closing (AG/PV)
- **Financial reporting** — balance sheet (bilan), income statement (compte de résultat), trial balance (balance générale)
- **Client invoicing** — create, track, and reconcile client invoices

All of this happens through a web app at `app.indy.fr`. There is no official public API.

## Why does this exist?

Indy's web interface is designed for humans clicking through forms. But accounting is repetitive — categorizing 50 bank transactions, checking VAT rates, uploading receipt photos — and AI agents are good at repetitive tasks when given the right tools.

This project reverse-engineers Indy's internal API (discovered via MITM traffic analysis) and wraps every useful endpoint as a **standalone executable tool** that any AI agent can call. Combined with structured skill files that teach agents *when* and *how* to use each tool, you get an AI accountant that can do in seconds what takes a human 30 minutes.

## What is this?

`@rainnworks/indy` is an [OpenClaw](https://github.com/nicepkg/openclaw) plugin that provides:

- **65 standalone tool binaries** — each a self-contained ~27KB Bun script. No framework needed at runtime. Pass JSON params, get JSON back.
- **Structured skill files** — markdown documents organized as parent routers + detail sub-files that teach agents the workflow logic (decision trees, cross-workflow jumps, edge cases).
- **OS-level security model** — when creating an agent, you copy only the binaries and skill files it needs into its workspace. If a binary isn't in the folder, the agent can't call it. No allowlists to misconfigure.
- **CLI** — human-friendly commands for quick lookups (`openclaw indy transactions`, `openclaw indy dashboard`).

### How it fits together

```
┌─────────────────────────────────────────────────┐
│  Source: openclaw-indy/                         │
│    bin/          ← 65 standalone tool binaries  │
│    skills/indy/  ← 18 skill files (7 parents   │
│                    + 9 sub-files + SKILL.md     │
│                    + context.md)                │
│    src/          ← TypeScript source            │
└──────────────┬──────────────────────────────────┘
               │  cp only what you need
               ▼
┌─────────────────────────────────────────────────┐
│  Agent workspace: ~/agents/my-agent/            │
│    tools/   ← subset of binaries for this job   │
│    skills/  ← subset of skill files for this job│
└─────────────────────────────────────────────────┘
```

---

## Two Ways to Use It

### Option A: Standalone Binaries (recommended for custom agents)

The repo ships pre-built binaries in `bin/` and compiled JS in `dist/` — no build step needed. Clone and go.

```bash
git clone https://github.com/rainnworks/openclaw-indy.git
cd openclaw-indy
```

Each binary is a self-contained Bun script (~27KB). Pass `INDY_REFRESH_TOKEN` as an env var and optional JSON params as the first argument:

```bash
# Check how many transactions need categorizing
INDY_REFRESH_TOKEN="..." ./bin/indy-categorize-count.js

# Get the fiscal dashboard
INDY_REFRESH_TOKEN="..." ./bin/indy-get-dashboard.js '{"year":"2025"}'

# Search transactions
INDY_REFRESH_TOKEN="..." ./bin/indy-list-transactions.js '{"search":"stripe","page":1}'
```

To create an agent, copy only the binaries + skill files it needs into its workspace (see [Creating Agents](#creating-agents) below).

### Option B: OpenClaw Plugin

Install as a managed OpenClaw plugin. All 65 tools register automatically — read tools are available by default, write tools require opt-in via the agent allowlist.

```bash
openclaw plugins install @rainnworks/indy
```

Add your token to the OpenClaw config:

```yaml
plugins:
  entries:
    indy:
      enabled: true
      config:
        refreshToken: "<your-token>"
```

Enable write tools per-agent:

```json5
{
  agents: {
    list: [{
      id: "main",
      tools: {
        allow: [
          "indy_set_accounting_account",   // specific tool
          "indy",                           // all tools from this plugin
          "group:plugins",                  // all plugin tools
        ]
      }
    }]
  }
}
```

The plugin ships skills at `skills/indy/` which load automatically when the plugin is enabled.

### Getting your refresh token

There is no official Indy API — this uses Indy's internal API via your session token.

1. Open [app.indy.fr](https://app.indy.fr), log in, open browser console (F12) and paste:

```js
(async()=>{const r=indexedDB.open('firebaseLocalStorageDb');r.onsuccess=()=>{const tx=r.result.transaction('firebaseLocalStorage','readonly');tx.objectStore('firebaseLocalStorage').getAll().onsuccess=async(e)=>{const t=e.target.result[0]?.value?.stsTokenManager?.refreshToken;if(!t){console.error('No refresh token found. Are you logged in?');return}try{await navigator.clipboard.writeText(t);console.log('✅ Refresh token copied to clipboard!')}catch{console.log('⚠️  Could not auto-copy. Here is your refresh token:\n\n'+t)}}}})()
```

2. Verify it works:

```bash
INDY_REFRESH_TOKEN="<your-token>" ./bin/indy-get-user.js
```

### Rebuilding (optional)

The `bin/` and `dist/` directories are committed to the repo — you only need to rebuild if you modify the source.

```bash
bun install
bun run build.ts          # regenerate bin/ (65 standalone binaries)
npx tsc                   # regenerate dist/ (compiled JS for OpenClaw plugin)
```

---

## Tool Inventory (65 tools)

### Auth & Profile

| Tool | Description |
|------|-------------|
| `indy-login` | Setup instructions or verify a token |
| `indy-get-user` | User profile: name, email, company, SIRET, regime, plan |
| `indy-get-payment` | Subscription and billing info |

### Transaction Categorization (core daily workflow)

| Tool | Description |
|------|-------------|
| `indy-list-pending-transactions` | Uncategorized transactions queue |
| `indy-categorize-count` | Count of transactions needing categorization |
| `indy-get-similar-transactions` | Find previously categorized matches for a transaction |
| `indy-get-similar-transactions-to-split` | Find split patterns for multi-category transactions |
| `indy-get-category-groups` | All accounting categories (PCG codes) |
| `indy-set-accounting-account` | ⚠️ Set the accounting category on a transaction subdivision |
| `indy-set-transaction-vat` | ⚠️ Set VAT rate (domestic, non-EU/intracom, or to-be-defined) |
| `indy-annotate-transaction` | ⚠️ Add/update a note on a transaction |
| `indy-get-transaction` | Get a single transaction by ID |
| `indy-list-transactions` | Search/filter the full ledger |
| `indy-get-transaction-filters` | Valid filter values (accounts, categories, date ranges) |
| `indy-get-matching-invoices` | Find client invoices matching transaction IDs |

### Receipts

| Tool | Description |
|------|-------------|
| `indy-get-receipts` | Receipts attached to a transaction |
| `indy-get-receipt` | Single receipt by ID (with OCR data) |
| `indy-upload-receipt` | ⚠️ Attach a receipt to a transaction (file path or base64) |
| `indy-upload-standalone-receipt` | ⚠️ Upload a receipt without attaching to a transaction |
| `indy-list-unpaired-receipts` | Receipts not linked to any transaction |
| `indy-get-receipt-matching-transactions` | Find bank transactions matching a receipt |
| `indy-get-matching-receipts` | Find receipts matching a list of transaction IDs |
| `indy-update-receipt-data` | ⚠️ Correct OCR-extracted receipt metadata |

### Manual Transactions

| Tool | Description |
|------|-------------|
| `indy-get-manual-transaction-types` | Available types: professionalExpense, cashIncome, etc. |
| `indy-create-manual-transaction` | ⚠️ Create an expense note, cash transaction, or personal income |
| `indy-create-transaction-from-receipt` | ⚠️ Create a transaction using a receipt's OCR data |
| `indy-delete-manual-transaction` | ⚠️ Delete a manually created transaction |

### VAT & Closing

| Tool | Description |
|------|-------------|
| `indy-get-vat-config` | VAT regime: hasVat, frequency, liability type |
| `indy-list-activities` | All fiscal obligations with deadlines and status |
| `indy-refresh-closing` | Force-refresh closing state for a declaration period |
| `indy-get-closing-state` | Read-only closing state (faster, no refresh) |
| `indy-get-categorize-all-status` | Uncategorized transactions blocking a closing |
| `indy-get-vat-rate-verification` | Transactions with undefined VAT in a closing period |
| `indy-set-closing-step-status` | ⚠️ Mark a closing step as done |
| `indy-get-vat-company-identity` | Company identity for a VAT declaration |
| `indy-set-vat-company-identity` | ⚠️ Update company identity (SIRET, IBAN, address) |
| `indy-get-vat-declaration-summary` | CA3/CA12 declaration lines with amounts |
| `indy-validate-vat-declaration-summary` | ⚠️ Confirm the declaration summary step |
| `indy-get-vat-credit-refund` | VAT credit refund eligibility and amounts |
| `indy-validate-vat-credit-refund` | ⚠️ Confirm the credit refund step |
| `indy-get-vat-validation` | Pre-submission validation: declaration type, teletransmission flag |
| `indy-get-pending-teletransmission` | Check for pending teletransmissions |
| `indy-submit-vat-teletransmission` | ⚠️ **IRREVERSIBLE** — submit declaration to tax authorities |
| `indy-get-last-teletransmission-state` | Last transmission status |

### Financial Statements

| Tool | Description |
|------|-------------|
| `indy-get-balance-sheet` | Bilan (actif/passif) for a fiscal year |
| `indy-get-income-statement` | Compte de résultat (P&L) for a fiscal year |
| `indy-get-general-balance` | Trial balance: per-account debit/credit/balance |
| `indy-get-period-choices` | Available fiscal periods for documents |

### Company & Associates

| Tool | Description |
|------|-------------|
| `indy-get-company-settings` | Full settings: regime, legal form, SIREN, APE, address, VAT config |
| `indy-get-associates` | Shareholders: name, birth, shares (‰), entry/exit dates |

### Activity Details (Annual Obligations)

| Tool | Description |
|------|-------------|
| `indy-get-cfe-declaration` | CFE declaration status and steps |
| `indy-get-das2` | DAS2 (fees to third parties) status |
| `indy-get-is-advance-payment` | IS advance payment (2571) status and teletransmission |
| `indy-get-ag-pv` | Assemblée Générale / Procès-Verbal status |
| `indy-get-vehicle-taxes` | Vehicle taxes declaration status |

### Client Invoicing

| Tool | Description |
|------|-------------|
| `indy-list-client-invoices` | Client invoices by status (PENDING, PAID, LATE) |
| `indy-list-draft-invoices` | Draft invoices |
| `indy-get-invoice-kpis` | Invoicing KPIs: invoiced, paid, late, waiting (TTC/HT) |
| `indy-list-products` | Product catalog for invoicing |

### Other

| Tool | Description |
|------|-------------|
| `indy-get-dashboard` | Monthly income/expense/VAT breakdown by year |
| `indy-get-accounting-summary` | Quick totals: count, income, expenses |
| `indy-list-bank-accounts` | Connected bank accounts with balances and sync status |
| `indy-list-documents` | Stored accounting documents |
| `indy-list-invoices` | Indy subscription billing invoices |
| `indy-fetch` | ⚠️ Raw authenticated API call (escape hatch) |

Tools marked ⚠️ are writes — always confirm with the user before calling them.

---

## Skills Architecture

The plugin ships structured skill files in `skills/indy/` that teach an agent **when** to call which tool and **how** to navigate between workflows. This is the key to building effective agents — don't just give the agent tools, give it the right skills for the task.

### File structure

```
skills/indy/
  SKILL.md                                    ← entry point, routing table
  skills.check-obligations.md                 ← "what needs doing?" — session start
  skills.categorize-transaction.md            ← core categorization loop
    └─ skills.categorize-transaction.edge-cases.md   ← personal payments, cash, non-EU, splits
  skills.receipts-and-manual-transactions.md  ← router for receipt/manual tx flows
    ├─ skills.receipts.upload-and-match.md           ← upload → OCR → match → link
    └─ skills.receipts.manual-transactions.md        ← create expense notes, cash tx
  skills.submit-vat-declaration.md            ← CA3/CA12 closing through teletransmission
  skills.end-of-year.md                       ← router for annual declarations
    ├─ skills.end-of-year.is-advance.md              ← IS advance payment (2571)
    ├─ skills.end-of-year.ag-pv.md                   ← shareholder meeting minutes
    └─ skills.end-of-year.other-declarations.md      ← DAS2, CFE, vehicle taxes
  skills.investigate-numbers.md               ← drill from statements to transactions
  skills.other.md                             ← router for read-only questions
    ├─ skills.other.financial-statements.md          ← bilan, P&L, trial balance
    ├─ skills.other.invoicing.md                     ← client invoices, KPIs, products
    └─ skills.other.company-info.md                  ← profile, company, associates, banks
  skills.context.md                           ← deep context for agent setup (NOT runtime)
```

### Loading pattern

Skills are organized as **parent files** (routers, 20-70 lines) and **sub-files** (detail, 13-56 lines). An agent should:

1. Load `SKILL.md` → read the routing table
2. Load the parent for the current task → it tells you which sub-file to load
3. Load the sub-file when you hit a fork → do the work
4. Unload the sub-file when done → return to parent or check-obligations

**Never load everything at once.** Maximum context at any point should be ~170 lines (parent + one sub-file).

### Cross-workflow jumps

Workflows are not linear. An agent frequently hits a fork and needs a different skill:

- Categorization → "paid from personal account" → `skills.receipts.manual-transactions.md`
- Categorization → "numbers look wrong" → `skills.investigate-numbers.md`
- VAT closing → uncategorized transactions → `skills.categorize-transaction.md`
- VAT closing → declaration amounts unexpected → `skills.investigate-numbers.md`
- End-of-year → AG/PV needs fiscal result → `skills.other.financial-statements.md`

The skill files document every jump point explicitly.

---

## Creating Agents

This section is for an AI (or human) creating agents that use Indy tools.

### The core idea: OS-level tool isolation

Every tool compiles to a **standalone executable** in `bin/`. Each is a self-contained ~27KB script that takes a JSON params string and an `INDY_REFRESH_TOKEN` env var. That's it — no runtime dependencies, no plugin framework needed.

```bash
# Any tool works standalone:
INDY_REFRESH_TOKEN="..." ./bin/indy-categorize-count.js
INDY_REFRESH_TOKEN="..." ./bin/indy-get-balance-sheet.js '{"year":"2025"}'
INDY_REFRESH_TOKEN="..." ./bin/indy-set-accounting-account.js \
  '{"transactionId":"abc","subdivisionId":"sub1","accountingAccountNumber":"625100"}'
```

**When you create an agent, you copy only the binaries it needs into the agent's workspace.** The agent can only execute what's physically present in its directory. No `indy-submit-vat-teletransmission.js` in the folder? The agent literally cannot submit a VAT declaration. This is enforced at the filesystem level — no allowlists to misconfigure, no config to override.

### Setting up an agent workspace

```bash
# 1. Build all binaries from the plugin source
cd openclaw-indy
bun run build.ts              # → bin/ now has 65 .js files

# 2. Create the agent workspace
mkdir -p ~/agents/indy-categorize/tools
mkdir -p ~/agents/indy-categorize/skills

# 3. Copy ONLY the tools this agent needs
cp bin/indy-categorize-count.js      ~/agents/indy-categorize/tools/
cp bin/indy-list-pending-transactions.js ~/agents/indy-categorize/tools/
cp bin/indy-get-similar-transactions.js  ~/agents/indy-categorize/tools/
cp bin/indy-get-category-groups.js       ~/agents/indy-categorize/tools/
cp bin/indy-set-accounting-account.js    ~/agents/indy-categorize/tools/
cp bin/indy-set-transaction-vat.js       ~/agents/indy-categorize/tools/
# ... etc — only what's needed

# 4. Copy ONLY the skill files this agent needs
cp skills/indy/SKILL.md                                ~/agents/indy-categorize/skills/
cp skills/indy/skills.check-obligations.md             ~/agents/indy-categorize/skills/
cp skills/indy/skills.categorize-transaction.md        ~/agents/indy-categorize/skills/
cp skills/indy/skills.categorize-transaction.edge-cases.md ~/agents/indy-categorize/skills/
cp skills/indy/skills.receipts.upload-and-match.md     ~/agents/indy-categorize/skills/

# 5. The agent's workspace now looks like:
# ~/agents/indy-categorize/
#   tools/
#     indy-categorize-count.js
#     indy-list-pending-transactions.js
#     indy-get-similar-transactions.js
#     indy-get-category-groups.js
#     indy-set-accounting-account.js
#     indy-set-transaction-vat.js
#   skills/
#     SKILL.md
#     skills.check-obligations.md
#     skills.categorize-transaction.md
#     skills.categorize-transaction.edge-cases.md
#     skills.receipts.upload-and-match.md
```

The agent sees 6 tools and 5 skill files. It cannot access the other 59 tools. It cannot submit VAT declarations, delete transactions, or modify company settings — those binaries don't exist in its world.

### Principles

1. **One job per agent.** A categorization agent shouldn't also do VAT teletransmission. Separate concerns into separate workspaces.
2. **Skill files are the instructions.** Tools without skills = an agent calling random endpoints. Always copy the matching skill files alongside the tools.
3. **Read-only by default.** Start with only read-tool binaries. Add write-tool binaries after the agent works correctly with reads. If a write tool isn't in the workspace, it can't be called.
4. **`skills.context.md` is for setup, not runtime.** Read it once to understand the domain when designing your agent. Don't copy it into the agent workspace — it's 250 lines of background context that wastes tokens at runtime.

### Data conventions the agent system prompt must include

- Amounts: **cents** (÷100 for €). Negative = expense, positive = income.
- Dates: `YYYY-MM-DD`. Fiscal periods: `YYYY-MM` (e.g. `2026-01`).
- VAT rates: basis points — `2000` = 20%, `1000` = 10%, `550` = 5.5%, `0` = exempt.
- `subdivisions[].id` is the `subdivisionId` needed for write operations.
- ⚠️ Always confirm with the user before any write operation.

---

### Example 1: Daily Categorization Agent

The most common agent. Checks for uncategorized transactions, classifies them, attaches receipts.

**Tools to copy** (15 binaries):
```bash
# Read (11)
indy-list-pending-transactions.js
indy-categorize-count.js
indy-get-similar-transactions.js
indy-get-category-groups.js
indy-get-receipts.js
indy-get-receipt.js
indy-list-unpaired-receipts.js
indy-get-receipt-matching-transactions.js
indy-get-matching-receipts.js
indy-get-matching-invoices.js
indy-get-company-settings.js

# Write (4)
indy-set-accounting-account.js
indy-set-transaction-vat.js
indy-annotate-transaction.js
indy-upload-receipt.js
```

**Skills to copy** (5 files):
```
SKILL.md
skills.check-obligations.md
skills.categorize-transaction.md
skills.categorize-transaction.edge-cases.md
skills.receipts.upload-and-match.md
```

**System prompt:**
```
You are an accounting assistant for Indy.fr. Your job is to categorize
uncategorized bank transactions.

Start every session by calling indy-categorize-count. If 0, tell the user
everything is up to date. If > 0, call indy-list-pending-transactions and
work through them one at a time.

For each transaction:
1. Check indy-get-similar-transactions — reuse the same category if found
2. If no match, use indy-get-category-groups to find the right PCG code
3. Set the account with indy-set-accounting-account
4. Set VAT with indy-set-transaction-vat (most French expenses = 2000 = 20%)
5. Check for matching receipts and attach if found

Always confirm with the user before writing. When unsure about a category,
ask — do not guess. See your skill files for edge cases (personal payments,
cash, non-EU suppliers).
```

**Setup:**
```bash
AGENT=~/agents/indy-categorize
mkdir -p $AGENT/{tools,skills}
for f in indy-list-pending-transactions indy-categorize-count \
  indy-get-similar-transactions indy-get-category-groups \
  indy-get-receipts indy-get-receipt indy-list-unpaired-receipts \
  indy-get-receipt-matching-transactions indy-get-matching-receipts \
  indy-get-matching-invoices indy-get-company-settings \
  indy-set-accounting-account indy-set-transaction-vat \
  indy-annotate-transaction indy-upload-receipt; do
  cp bin/$f.js $AGENT/tools/
done
cp skills/indy/SKILL.md skills/indy/skills.check-obligations.md \
   skills/indy/skills.categorize-transaction.md \
   skills/indy/skills.categorize-transaction.edge-cases.md \
   skills/indy/skills.receipts.upload-and-match.md $AGENT/skills/
```

---

### Example 2: Read-Only Financial Overview Agent

Cannot modify anything. Answers questions about finances, shows statements, checks obligations.

**Tools to copy** (30 binaries — all reads):
```bash
indy-get-user.js  indy-get-dashboard.js  indy-get-accounting-summary.js
indy-list-transactions.js  indy-get-transaction-filters.js  indy-get-transaction.js
indy-list-bank-accounts.js  indy-list-activities.js  indy-get-closing-state.js
indy-categorize-count.js  indy-list-unpaired-receipts.js  indy-get-vat-config.js
indy-get-balance-sheet.js  indy-get-income-statement.js  indy-get-general-balance.js
indy-get-period-choices.js  indy-get-company-settings.js  indy-get-associates.js
indy-list-client-invoices.js  indy-list-draft-invoices.js  indy-get-invoice-kpis.js
indy-list-products.js  indy-get-cfe-declaration.js  indy-get-das2.js
indy-get-is-advance-payment.js  indy-get-ag-pv.js  indy-get-vehicle-taxes.js
indy-list-documents.js  indy-list-invoices.js  indy-get-payment.js
```

**Skills to copy** (7 files):
```
SKILL.md
skills.check-obligations.md
skills.other.md
skills.other.financial-statements.md
skills.other.invoicing.md
skills.other.company-info.md
skills.investigate-numbers.md
```

**System prompt:**
```
You are a financial assistant for Indy.fr. You can read all accounting data
but cannot modify anything.

When the user asks a question:
- "What needs doing?" → indy-list-activities + indy-categorize-count
- "How's my year?" → indy-get-dashboard or indy-get-income-statement
- "Show me my balance sheet" → indy-get-period-choices then indy-get-balance-sheet
- "Any late invoices?" → indy-list-client-invoices with status LATE
- "Why is this number wrong?" → follow skills.investigate-numbers.md

Present amounts in euros (divide cents by 100). Use the skill files to know
which tools to call and in what order.
```

Zero write binaries in the workspace = zero risk of accidental modification.

---

### Example 3: VAT Filing Agent

Has write access to closing steps and teletransmission. **The most dangerous agent** — only create this when you specifically need VAT filing.

**Tools to copy** (22 binaries):
```bash
# Read (17)
indy-list-activities.js  indy-get-vat-config.js  indy-get-closing-state.js
indy-refresh-closing.js  indy-get-categorize-all-status.js
indy-get-vat-rate-verification.js  indy-get-vat-company-identity.js
indy-get-vat-declaration-summary.js  indy-get-vat-credit-refund.js
indy-get-vat-validation.js  indy-get-pending-teletransmission.js
indy-get-last-teletransmission-state.js  indy-categorize-count.js
indy-list-unpaired-receipts.js  indy-get-company-settings.js
indy-get-general-balance.js  indy-list-transactions.js

# Write — closing steps (4)
indy-set-closing-step-status.js  indy-set-vat-company-identity.js
indy-validate-vat-declaration-summary.js  indy-validate-vat-credit-refund.js

# Write — IRREVERSIBLE (1)
indy-submit-vat-teletransmission.js
```

**Skills to copy** (4 files):
```
SKILL.md
skills.check-obligations.md
skills.submit-vat-declaration.md
skills.investigate-numbers.md
```

**System prompt:**
```
You are a VAT declaration assistant for Indy.fr. Walk the user through VAT
closing step by step.

CRITICAL: indy-submit-vat-teletransmission is IRREVERSIBLE. It sends the
declaration to French tax authorities. NEVER call it without explicit user
confirmation. Present the full declaration summary first.

Follow skills.submit-vat-declaration.md exactly. If uncategorized transactions
block the closing, tell the user to run the categorization agent first.
```

**Security note:** If you want an agent that can *prepare* a VAT declaration but not *submit* it, simply don't copy `indy-submit-vat-teletransmission.js` into the workspace. The agent can walk through all steps but physically cannot teletransmit.

---

### Building your own agent

1. **Read `skills.context.md`** once to understand the domain (daily loop vs periodic loop, what matters, what's safe to automate). Do NOT copy this into the agent workspace.
2. **Pick a job** — categorize, file VAT, answer questions, process receipts, etc.
3. **Select tools** from the inventory above — only the binaries needed for that job.
4. **Select skill files** — the parent + relevant sub-files. Match skills to tools.
5. **Create the workspace** — `mkdir -p agent/{tools,skills}`, copy binaries + skills.
6. **Write a system prompt** — reference the skill files, explain the role, include data conventions.
7. **Start read-only** — omit write binaries until the agent works correctly with reads. Then add them one at a time.

### Security model summary

```
Source (openclaw-indy/)
  bin/                      ← all 65 binaries (master copy)
  skills/indy/              ← all skill files (master copy)

Agent workspace (~/agents/my-agent/)
  tools/                    ← only the binaries this agent needs
  skills/                   ← only the skill files this agent needs
```

- **The agent can only run tools present in its `tools/` directory.**
- No binary = no capability. Period.
- Write tools are opt-in by physical presence, not by configuration.
- To revoke a capability, `rm` the binary. To grant one, `cp` it in.
- Each agent is fully self-contained — you can `tar` the workspace and deploy it anywhere that has `bun`.

---

## CLI Commands (OpenClaw plugin mode)

When installed as an OpenClaw plugin, these commands are available:

```bash
openclaw indy login [token]         # Setup authentication
openclaw indy status                 # Check login status
openclaw indy me                     # Show user info
openclaw indy transactions           # List transactions
openclaw indy bank                   # List bank accounts
openclaw indy receipts [txId]        # List receipts for a transaction
openclaw indy upload <txId> <file>   # Upload a receipt
openclaw indy dashboard [year]       # Show fiscal dashboard
openclaw indy fetch <path>           # Raw API request
```

## Config Reference

```yaml
plugins:
  entries:
    indy:
      enabled: true
      config:
        refreshToken: "..."                    # Required — Firebase refresh token
        firebaseApiKey: "..."                  # Optional — custom Firebase API key
        baseUrl: "https://app.indy.fr/api"     # Optional — API base URL
```

For standalone binaries, the equivalent config is environment variables:

```bash
export INDY_REFRESH_TOKEN="..."              # Required
export INDY_FIREBASE_API_KEY="..."           # Optional
export INDY_BASE_URL="https://app.indy.fr/api"  # Optional
```

## License

MIT