Skip to content
Automation
Skill

/merit-lhv

How to pull an LHV bank statement straight into Merit Aktiva without touching a file export — LHV's own MCP server returns camt.053 XML, which is exactly what Merit's bank-statement import accepts. Covers connecting LHV (OAuth2 + Smart-ID, read-only), the 31-day window, the

From plugin
merit-aktiva-workspace
818 skills3 agents5 commands1 MCP
Install
$ npx -y skills add Elnora-AI/elnora-merit-aktiva --skill merit-lhv --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/merit-lhv

Context preview

The summary Claude sees to decide when to auto-load this skill.

How to pull an LHV bank statement straight into Merit Aktiva without touching a file export — LHV's own MCP server returns camt.053 XML, which is exactly what Merit's bank-statement import accepts. Covers connecting LHV (OAuth2 + Smart-ID, read-only), the 31-day window, the

SKILL.md

merit-lhv.SKILL.md
name: merit-lhv
version: 1.0.0
description: >
  How to pull an LHV bank statement straight into Merit Aktiva without touching a file
  export — LHV's own MCP server returns camt.053 XML, which is exactly what Merit's
  bank-statement import accepts. Covers connecting LHV (OAuth2 + Smart-ID, read-only),
  the 31-day window, the multi-currency trap, and where the statement stops being the
  agent's job and becomes a match-and-confirm in the Merit UI.
  Use when: importing an LHV statement, reconciling the LHV bank in Merit, pulling
  transactions or balances from LHV, connecting LHV to Claude, or backfilling several
  months of LHV history.
  TRIGGERS: "lhv", "lhv.ai", "lhv pank", "lhv statement", "lhv bank", "import lhv",
  "pull from lhv", "lhv transactions", "lhv balance", "connect lhv", "bank statement
  from lhv", "reconcile lhv", "lhv to merit", "camt from lhv", "lhv backfill".

Merit + LHV

LHV publishes an official MCP server ([lhv.ai](https://lhv.ai/)) that returns **raw camt.053 XML** — the exact format `elnora-merit payments import-statement` already accepts. So there is no exporter, no parser, and no bank client in this repo: the bank hands you a statement and Merit eats it. This skill is the *procedure* and the *traps*.

For the transaction-type rules once rows are in Merit, see **merit-payments-bank**. That skill owns the booking model; this one only owns getting the statement across.

Connect LHV (once, ~2 minutes)

This plugin ships the LHV MCP server in its `.mcp.json`. It appears as `lhv` and is **unauthenticated until the user connects it** — the plugin holds no bank credentials.

1. Run `/mcp`, pick **lhv**, choose **Authenticate**. 2. The browser opens LHV's own sign-in. Authenticate with **Smart-ID, Mobile-ID, ID-card, or biometrics** — the same way as the internet bank. 3. On the consent screen pick scopes: `accounts:read`, `transactions:read`, or both. Importing statements needs **both**.

Nothing is stored by this plugin. The token lives in the user's MCP client, stays valid for **30 days**, and is revoked from the internet bank under **Settings → Active sessions**.

> **Read-only, structurally.** LHV exposes no write scope at all — an assistant can read > accounts and transactions and can never move money. Do not add bank credentials to > `.env` for this flow; it does not need them. > > Non-Estonian Merit users (`MERIT_LOCALIZATION=pl`) have no LHV account; the server will > simply sit unauthenticated and unused.

The four LHV tools

| Tool | Scope | Returns | |---|---|---| | `list_accounts` | `accounts:read` | IBAN, currency, availableBalance, type, name | | `get_balances(iban)` | `accounts:read` | available + **settled** + **reserved** | | `get_transactions(iban, dateFrom, dateTo)` | `transactions:read` | **raw camt.053 XML**, max 31 days | | `get_transactions_summary(iban, dateFrom, dateTo)` | `transactions:read` | totals, top counterparties, `truncated` flag |

Only `get_transactions` feeds Merit. The other three answer questions ("what's the balance", "what did we spend on X") and should never be used to *book* anything — summaries are aggregates, not statement rows.

STEP 0 — Is this period already booked? (never skip this)

**The most likely outcome of an LHV import is that it was not needed.** The books are usually already done by the time anyone reaches for a statement. Importing an already-booked period and matching the rows **double-books it**. Establish that the period is genuinely unbooked *before* pulling anything.

**Merit's idempotency is not a safety net — do not lean on it.** In a live run it skipped **2 rows out of 19 while all 19 were already booked**. It recognises its own prior imports and some hand-entered payments, but payments posted through the API are invisible to it. The reassuring `Imporditi 17 makserida` meant "17 duplicates queued", not "17 rows were needed".

Compare the bank account's GL balance against the bank's own closing balance:

elnora-merit banks list                                              # bankId, IBAN, CurrencyCode, AccountCode
elnora-merit reports balance-sheet --end-date <dateTo> --per-count 1 # that AccountCode's balance
  • **GL balance == the bank's real closing balance → ALREADY BOOKED. STOP.** There is nothing

to import. This is the normal case, not an edge case.

  • They differ → the gap is what is genuinely unbooked. Import only that window.

Then read what is already posted, which also shows *how* it was booked:

elnora-merit gl list-full --period-start <from> --period-end <to> --with-lines 1

Inspect `Entries[].AccountCode` (note: `Entries`, not `Lines`). `PA` batches crediting the bank account are bank payments that **already exist** — if they cover these dates, the statement is already in. `OA` batches debiting an expense account are the purchase invoices behind them.

Import an LHV statement into Merit

Only once Step 0 says the period is genuinely unbooked:

1. list_accounts                          → pick the IBAN
2. get_transactions(iban, from, to)       → max 31 days per call
3. write the .xml FIELD to a file         → not the JSON envelope (see traps)
4. verify the file against its own totals → see below
5. elnora-merit banks list                → bankId for that IBAN + currency
6. elnora-merit payments import-statement --file statement.xml
7. elnora-merit payments list-imports <bankId> --booking-date-from <from>
8. match + confirm in the Merit UI        → UI-only; read "Muud vs Võlgnevused" FIRST

**What the import does — and does not do.** It queues rows in *Maksed* and posts **no GL entries**. The success string ends `koostati pearaamatu kanded` ("general ledger entries were created"), but that is boilerplate describing the feature in general; with `Kinnitati 0 makserida` nothing is posted. Verified: after importing 17 rows, **zero** GL batches carried that timestamp, and the bank account's balance did not move. The GL entry appears when you *

Read more
Ships withmerit-aktiva-workspace

Do your Merit Aktiva accounting from the command line — or let Claude Code do it for you. Merit Aktiva is Estonia's cloud accounting platform.

Get the whole plugin

Other skills on merit-aktiva-workspace.