3.1k★by thisisjeron
actual-budget – OpenClaw Skill
actual-budget is an OpenClaw Skills integration for coding workflows. 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.
Skill Snapshot
| name | actual-budget |
| description | 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. OpenClaw Skills integration. |
| owner | thisisjeron |
| repository | thisisjeron/actual-budget |
| language | Markdown |
| license | MIT |
| topics | |
| security | L1 |
| install | openclaw add @thisisjeron/actual-budget |
| last updated | Feb 7, 2026 |
Maintainer

name: actual-budget description: 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.
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
| Variable | Required | Description |
|---|---|---|
ACTUAL_SERVER_URL | Yes | Server URL (e.g., https://actual.example.com) |
ACTUAL_PASSWORD | Yes | Server password |
ACTUAL_SYNC_ID | Yes | Budget Sync ID (Settings → Advanced → Sync ID) |
ACTUAL_DATA_DIR | No | Local cache directory for budget data (defaults to cwd) |
ACTUAL_ENCRYPTION_PASSWORD | No | E2E encryption password, if enabled |
NODE_TLS_REJECT_UNAUTHORIZED | No | Set to 0 for self-signed certs |
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 useYYYY-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
No README available.
Permissions & Security
Security level L1: Low-risk skills with minimal permissions. Review inputs and outputs before running in production.
Requirements
- OpenClaw CLI installed and configured.
- Language: Markdown
- License: MIT
- Topics:
FAQ
How do I install actual-budget?
Run openclaw add @thisisjeron/actual-budget in your terminal. This installs actual-budget into your OpenClaw Skills catalog.
Does this skill run locally or in the cloud?
OpenClaw Skills execute locally by default. Review the SKILL.md and permissions before running any skill.
Where can I verify the source code?
The source repository is available at https://github.com/openclaw/skills/tree/main/skills/thisisjeron/actual-budget. Review commits and README documentation before installing.
