Actual Budget

v1.0.2

Query and manage personal finances via the official Actual Budget Node.js API. Use for budget queries, transaction imports/exports, account management, categorization, rules, schedules, and bank sync with self-hosted Actual Budget instances.

2· 3.8k·11 current·12 all-time

Install

OpenClaw Prompt Flow

Install with OpenClaw

Best for remote or guided setup. Copy the exact prompt, then paste it into OpenClaw for thisisjeron/actual-budget.

Previewing Install & Setup.
Prompt PreviewInstall & Setup
Install the skill "Actual Budget" (thisisjeron/actual-budget) from ClawHub.
Skill page: https://clawhub.ai/thisisjeron/actual-budget
Keep the work scoped to this skill only.
After install, inspect the skill metadata and help me finish setup.
Use only the metadata you can verify from ClawHub; do not invent missing requirements.
Ask before making any broader environment changes.

Command Line

CLI Commands

Use the direct CLI path if you want to install manually and keep every step visible.

OpenClaw CLI

Bare skill slug

openclaw skills install actual-budget

ClawHub CLI

Package manager switcher

npx clawhub@latest install actual-budget
Security Scan
VirusTotalVirusTotal
Benign
View report →
OpenClawOpenClaw
Suspicious
medium confidence
!
Purpose & Capability
The skill's description and SKILL.md clearly require access to an Actual Budget server (ACTUAL_SERVER_URL, ACTUAL_PASSWORD, ACTUAL_SYNC_ID, etc.) which is coherent with the stated purpose. HOWEVER the registry metadata claims no required environment variables or credentials. The absence of a declared primary credential and no homepage/source for verification is inconsistent with a skill that needs sensitive server credentials.
Instruction Scope
The SKILL.md stays within the stated purpose: it documents how to install and call @actual-app/api, which operations to run (sync, import, bank sync, queries), and which env vars and paths are used. It does not instruct indiscriminate file reads or exfiltration. Minor concerns: the default data dir can be the current working directory (cwd) which could expose more local files than intended; and it instructs use of NODE_EXTRA_CA_CERTS (a path to a cert file) which means the agent/process will read that file.
Install Mechanism
This is an instruction-only skill (no install spec), which is low-risk for skill bundle installation. However, SKILL.md tells users/agents to run `npm install @actual-app/api` — that will fetch code from npm (remote dependency). Because the skill's source/homepage are unknown, you cannot verify the upstream npm package or its integrity from the skill metadata alone. Users should validate the npm package origin before installing.
!
Credentials
The environment variables documented in SKILL.md (ACTUAL_SERVER_URL, ACTUAL_PASSWORD, ACTUAL_SYNC_ID, optional ACTUAL_ENCRYPTION_PASSWORD, NODE_EXTRA_CA_CERTS) are appropriate for connecting to a self-hosted Actual Budget server, but they are sensitive (server password, sync id). The skill metadata did not declare any required env vars or a primary credential — a mismatch that could lead to accidental exposure of secrets if the platform provides environment access in ways the user doesn't expect.
Persistence & Privilege
The skill does not request persistent installation or elevated platform privileges (always:false). It is instruction-only and has no code files, so it will not by itself write binaries to the agent environment. The agent-autonomy defaults are unchanged; combine that with other concerns if the platform gives this skill runtime access to environment variables.
What to consider before installing
Before installing or enabling this skill: 1) Note the metadata/manifest mismatch — SKILL.md requires sensitive env vars (server URL, password, sync id) but the registry metadata lists none. Ask the author to provide a source repository or homepage and to update the manifest to declare required credentials. 2) Verify the npm package @actual-app/api on npmjs.org or the official Actual Budget GitHub repo; inspect the package code or its provenance before running npm install. 3) Limit risk: run the package in an isolated environment or container, and prefer a dedicated, least-privilege service account or short-lived credential rather than your main password. 4) Be aware that ACTUAL_PASSWORD and ACTUAL_SYNC_ID are sensitive; ensure the platform only exposes environment variables explicitly declared and necessary. 5) If you cannot verify the author or upstream package, do not supply production credentials — ask for a vetted implementation or provide a read-only/test account. 6) If you proceed, consider setting ACTUAL_DATA_DIR explicitly to a safe location (not cwd) and carefully manage NODE_EXTRA_CA_CERTS to avoid trusting unexpected certificates.

Like a lobster shell, security has layers — review code before you run it.

latestvk9712v5r0950h4hc63w9p1dktx80sqd0
3.8kdownloads
2stars
3versions
Updated 2mo ago
v1.0.2
MIT-0

Actual Budget API

Official Node.js API for Actual Budget. Runs headless — works on local budget data synced from your server.

Installation

npm install @actual-app/api

Environment Variables

VariableRequiredDescription
ACTUAL_SERVER_URLYesServer URL (e.g., https://actual.example.com)
ACTUAL_PASSWORDYesServer password
ACTUAL_SYNC_IDYesBudget Sync ID (Settings → Advanced → Sync ID)
ACTUAL_DATA_DIRNoLocal cache directory for budget data (defaults to cwd)
ACTUAL_ENCRYPTION_PASSWORDNoE2E encryption password, if enabled
NODE_EXTRA_CA_CERTSNoPath to CA certificate file for self-signed certs

Self-Signed Certificates

If your Actual Budget server uses a self-signed certificate:

  1. Recommended: Add your CA to the system trust store, or
  2. Alternative: Set NODE_EXTRA_CA_CERTS=/path/to/your-ca.pem to trust your specific CA

Avoid disabling TLS verification entirely — it exposes you to man-in-the-middle attacks.

Quick Start

const api = require('@actual-app/api');

await api.init({
  dataDir: process.env.ACTUAL_DATA_DIR || '/tmp/actual-cache',
  serverURL: process.env.ACTUAL_SERVER_URL,
  password: process.env.ACTUAL_PASSWORD,
});

await api.downloadBudget(
  process.env.ACTUAL_SYNC_ID,
  process.env.ACTUAL_ENCRYPTION_PASSWORD ? { password: process.env.ACTUAL_ENCRYPTION_PASSWORD } : undefined
);

// ... do work ...

await api.shutdown();

Core Concepts

  • Amounts are integers in cents: $50.00 = 5000, -1200 = expense of $12.00
  • Dates use YYYY-MM-DD, months use YYYY-MM
  • IDs are UUIDs — use getIDByName(type, name) to look up by name
  • Convert with api.utils.amountToInteger(123.45)12345

Common Operations

Get Budget Overview

const months = await api.getBudgetMonths();        // ['2026-01', '2026-02', ...]
const jan = await api.getBudgetMonth('2026-01');   // { categoryGroups, incomeAvailable, ... }

Accounts

const accounts = await api.getAccounts();
const balance = await api.getAccountBalance(accountId);
const newId = await api.createAccount({ name: 'Checking', type: 'checking' }, 50000); // $500 initial
await api.closeAccount(id, transferToAccountId);  // transfer remaining balance

Transactions

// Get transactions for date range
const txns = await api.getTransactions(accountId, '2026-01-01', '2026-01-31');

// Import with deduplication + rules (preferred for bank imports)
const { added, updated } = await api.importTransactions(accountId, [
  { date: '2026-01-15', amount: -2500, payee_name: 'Grocery Store', notes: 'Weekly run' },
  { date: '2026-01-16', amount: -1200, payee_name: 'Coffee Shop', imported_id: 'bank-123' },
]);

// Update a transaction
await api.updateTransaction(txnId, { category: categoryId, cleared: true });

Categories & Payees

const categories = await api.getCategories();
const groups = await api.getCategoryGroups();
const payees = await api.getPayees();

// Create
const catId = await api.createCategory({ name: 'Subscriptions', group_id: groupId });
const payeeId = await api.createPayee({ name: 'Netflix', category: catId });

Budget Amounts

await api.setBudgetAmount('2026-01', categoryId, 30000);  // budget $300
await api.setBudgetCarryover('2026-01', categoryId, true);

Rules

const rules = await api.getRules();
await api.createRule({
  stage: 'pre',
  conditionsOp: 'and',
  conditions: [{ field: 'payee', op: 'is', value: payeeId }],
  actions: [{ op: 'set', field: 'category', value: categoryId }],
});

Schedules

const schedules = await api.getSchedules();
await api.createSchedule({
  payee: payeeId,
  account: accountId,
  amount: -1500,
  date: { frequency: 'monthly', start: '2026-01-01', interval: 1, endMode: 'never' },
});

Bank Sync

await api.runBankSync({ accountId });  // GoCardless/SimpleFIN

Sync & Shutdown

await api.sync();      // push/pull changes to server
await api.shutdown();  // always call when done

ActualQL Queries

For complex queries, use ActualQL:

const { q, runQuery } = require('@actual-app/api');

// Sum expenses by category this month
const { data } = await runQuery(
  q('transactions')
    .filter({
      date: [{ $gte: '2026-01-01' }, { $lte: '2026-01-31' }],
      amount: { $lt: 0 },
    })
    .groupBy('category.name')
    .select(['category.name', { total: { $sum: '$amount' } }])
);

// Search transactions
const { data } = await runQuery(
  q('transactions')
    .filter({ 'payee.name': { $like: '%grocery%' } })
    .select(['date', 'amount', 'payee.name', 'category.name'])
    .orderBy({ date: 'desc' })
    .limit(20)
);

Operators: $eq, $lt, $lte, $gt, $gte, $ne, $oneof, $regex, $like, $notlike Splits: .options({ splits: 'inline' | 'grouped' | 'all' })

Helpers

// Look up ID by name
const acctId = await api.getIDByName('accounts', 'Checking');
const catId = await api.getIDByName('categories', 'Food');
const payeeId = await api.getIDByName('payees', 'Amazon');

// List budgets
const budgets = await api.getBudgets();  // local + remote files

Transfers

Transfers use special payees. Find transfer payee by transfer_acct field:

const payees = await api.getPayees();
const transferPayee = payees.find(p => p.transfer_acct === targetAccountId);
await api.importTransactions(fromAccountId, [
  { date: '2026-01-15', amount: -10000, payee: transferPayee.id }
]);

Split Transactions

await api.importTransactions(accountId, [{
  date: '2026-01-15',
  amount: -5000,
  payee_name: 'Costco',
  subtransactions: [
    { amount: -3000, category: groceryCatId },
    { amount: -2000, category: householdCatId },
  ]
}]);

Bulk Import (New Budget)

For migrating from another app:

await api.runImport('My-New-Budget', async () => {
  for (const acct of myData.accounts) {
    const id = await api.createAccount(acct);
    await api.addTransactions(id, myData.transactions.filter(t => t.acctId === id));
  }
});

Reference

Full API: https://actualbudget.org/docs/api/reference ActualQL: https://actualbudget.org/docs/api/actual-ql

Comments

Loading comments...