Skip to content

Add balance history download and upload tools #7

Closed

Description

@krisrowe

Summary

Monarch has REST endpoints for downloading and uploading account balance history as CSV. These are needed for loan servicer transfers, correcting stale balances, and maintaining accurate net worth history. Currently no SDK, MCP, or CLI support exists.

API Details

Download Balance History

POST https://api.monarch.com/download-balances/
Content-Type: application/json
Authorization: Token <token>

{"account_ids": ["<account_id>"]}

Returns CSV with columns: Date,Balance,Account

  • One row per day from account creation through today
  • Negative values for liabilities
  • Account name as display name (not ID)

Upload Balance History

POST https://api.monarch.com/account-balance-history/upload/
Content-Type: multipart/form-data
Authorization: Token <token>

Parts:
  files: <CSV file binary>
  account_files_mapping: {"<filename>": "<account_id>"}
  files_column_mapping: {"<filename>": {"date": 0, "balance": 1}}

Returns: {"session_key": "wrapper-upload-balance-history-session-...", "previews": []}

Then trigger processing via GraphQL:

mutation Web_ParseUploadBalanceHistorySession($input: ParseBalanceHistoryInput!) {
  parseBalanceHistory(input: $input) {
    uploadBalanceHistorySession {
      sessionKey
      status
    }
  }
}

Variables: {"input": {"sessionKey": "<session_key>"}}

Then poll for completion:

query Web_GetUploadBalanceHistorySession($sessionKey: String!) {
  uploadBalanceHistorySession(sessionKey: $sessionKey) {
    sessionKey
    status
  }
}

Poll until status is "completed". Typical completion time: < 2 seconds.

Reading Balance History (existing)

Common_AccountDetails_getAccount already returns snapshots — array of {date, signedBalance} objects. This is the read-only equivalent.

Behavior Notes (verified through testing)

Behavior Result
Upload replaces entire history Yes — all previous snapshots are overwritten
Upload updates currentBalance Yes — final row's balance becomes the account's currentBalance
Works on linked (non-manual) accounts Yes
Works on manual accounts Yes — verified
Creates any transactions No — balance history and transactions are independent
Affects income/expense reports No — only balance snapshots
create_transaction(update_balance=true) on linked accounts Does NOT update balance — silently ignored

Proposed Tools

download_balance_history(account_id) -> CSV string or list of snapshots

Returns the daily balance history for an account. Could return raw CSV or parsed list of {date, balance} objects.

upload_balance_history(account_id, snapshots)

Accepts a list of {date, balance} objects (or a CSV file path) and uploads as the account's balance history. Handles the full workflow: upload file → parse mutation → poll for completion.

WARNING in tool docs: This replaces the entire balance history. The tool should automatically download and cache the existing history before uploading, in case a rollback is needed.

CLI

monarch balances download <account_id> [-o file.csv]
monarch balances upload <account_id> <file.csv>

Use Cases

  • Loan servicer transfers: Zero out old account from transfer date, populate new account from transfer date
  • Correcting stale balances: When a linked account stops syncing, fill in correct balances from statements
  • Manual account history: Import historical balances for manually-tracked accounts
  • Balance migration: Move balance history between accounts (download from one, modify, upload to another)

Related

Activity

  1. krisrowe commented on May 25, 2026

    @krisrowe
    CollaboratorAuthor

    Additional motivation — real-world drift case

    Hit this exact gap in practice working with a manual liability account (~20 historical transactions, low-five-figure balance). The path through the dysfunction:

    1. Bulk-created the transactions via create_transaction with the API's default update_balance=false — transactions registered in the audit trail but the account's balance field didn't move. This is the opposite of Monarch's UI default, where the "adjust balance" toggle is checked by default for transactions added via the UI.
    2. Observed that delete_transaction unconditionally reverses the transaction amount from the balance — regardless of how the original create was flagged. So the asymmetry (create-with-false is a no-op on balance vs delete-always-reverses) means a delete-then-recreate round trip on transactions originally created with update_balance=false leaves accumulated balance drift in the account's balance field, even though the transaction-sum reconciles.
    3. After cycling through deletes + recreates to fix the original flag mistake, the account ended up with the correct sum-of-transactions but a residual balance offset from the earlier asymmetric ops. No combination of further API-only operations could clear that residual without either (a) adding a hack "balance correction" transaction, or (b) doing the UI-side balance-history upload that this issue would automate.

    Suggestion for the future upload_balance_history tool docstring: explicitly recommend it as the canonical reset path after any delete+recreate cycle on a manual account — cheaper and cleaner than chasing residual drift with offset transactions. Worth mentioning alongside the loan-servicer-transfer and stale-balance-correction use cases already in the issue.

    Adding as additional context for prioritization, not raising urgency.

  2. krisrowe commented on May 30, 2026

    @krisrowe
    CollaboratorAuthor

    Implemented in 9a30a22 (upload safety interlock added in aea350a), released in 0.5.0.

    • download_balance_history (REST /download-balances/) and upload_balance_history (multipart upload → parse mutation → session poll). Upload is a full-history replace (no append), and sets currentBalance to the final row.
    • Read-before-write interlock: download_balance_history returns a history_token digest of the current history; upload_balance_history requires it as expected_token, re-reads the live history, and refuses (changing nothing) on mismatch. This guarantees the prior history was captured (so the replace is reversible) and unchanged. The replaced history is returned as previous_snapshots for rollback.
    • MCP tools and monarch balances download / upload CLI (CLI reads current history, supports a pre-upload backup, and prompts before replacing).
    • Unit tests cover download, full replace, currentBalance update, the token interlock (wrong/stale token rejected with no mutation), and round-trip rollback. README updated.

    Cloud-verified: download_balance_history was invoked end-to-end against the deployed HTTP MCP instance — snapshots returned and history_token surfaced. The token guard was additionally validated against the live API (a bad token is refused with no mutation). A live upload was not executed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions