Skip to main content
Glama
actual-api.ts6.34 kB
import api from '@actual-app/api'; import fs from 'fs'; import path from 'path'; import os from 'os'; import { BudgetFile } from './types.js'; import { APIAccountEntity, APICategoryEntity, APICategoryGroupEntity, APIPayeeEntity, } from '@actual-app/api/@types/loot-core/src/server/api-models.js'; import { RuleEntity, TransactionEntity } from '@actual-app/api/@types/loot-core/src/types/models/index.js'; const DEFAULT_DATA_DIR: string = path.resolve(os.homedir() || '.', '.actual'); // API initialization state let initialized = false; let initializing = false; let initializationError: Error | null = null; /** * Initialize the Actual Budget API */ export async function initActualApi(): Promise<void> { if (initialized) return; if (initializing) { // Wait for initialization to complete if already in progress while (initializing) { await new Promise((resolve) => setTimeout(resolve, 100)); } if (initializationError) throw initializationError; return; } try { console.error('Initializing Actual Budget API...'); const dataDir = process.env.ACTUAL_DATA_DIR || DEFAULT_DATA_DIR; if (!fs.existsSync(dataDir)) { fs.mkdirSync(dataDir, { recursive: true }); } await api.init({ dataDir, serverURL: process.env.ACTUAL_SERVER_URL, password: process.env.ACTUAL_PASSWORD, }); const budgets: BudgetFile[] = await api.getBudgets(); if (!budgets || budgets.length === 0) { throw new Error('No budgets found. Please create a budget in Actual first.'); } // Use specified budget or the first one const budgetId: string = process.env.ACTUAL_BUDGET_SYNC_ID || budgets[0].cloudFileId || budgets[0].id || ''; console.error(`Loading budget: ${budgetId}`); await api.downloadBudget( budgetId, process.env.ACTUAL_BUDGET_ENCRYPTION_PASSWORD ? { password: process.env.ACTUAL_BUDGET_ENCRYPTION_PASSWORD, } : undefined ); initialized = true; console.error('Actual Budget API initialized successfully'); } catch (error) { console.error('Failed to initialize Actual Budget API:', error); initializationError = error instanceof Error ? error : new Error(String(error)); throw initializationError; } finally { initializing = false; } } /** * Shutdown the Actual Budget API */ export async function shutdownActualApi(): Promise<void> { if (!initialized) return; await api.shutdown(); initialized = false; } // ---------------------------- // FETCH // ---------------------------- /** * Get all accounts (ensures API is initialized) */ export async function getAccounts(): Promise<APIAccountEntity[]> { await initActualApi(); return api.getAccounts(); } /** * Get all categories (ensures API is initialized) */ export async function getCategories(): Promise<APICategoryEntity[]> { await initActualApi(); return api.getCategories(); } /** * Get all category groups (ensures API is initialized) */ export async function getCategoryGroups(): Promise<APICategoryGroupEntity[]> { await initActualApi(); return api.getCategoryGroups(); } /** * Get all payees (ensures API is initialized) */ export async function getPayees(): Promise<APIPayeeEntity[]> { await initActualApi(); return api.getPayees(); } /** * Get transactions for a specific account and date range (ensures API is initialized) */ export async function getTransactions(accountId: string, start: string, end: string): Promise<TransactionEntity[]> { await initActualApi(); return api.getTransactions(accountId, start, end); } /** * Get all rules (ensures API is initialized) */ export async function getRules(): Promise<RuleEntity[]> { await initActualApi(); return api.getRules(); } // ---------------------------- // ACTION // ---------------------------- /** * Create a new payee (ensures API is initialized) */ export async function createPayee(args: Record<string, unknown>): Promise<string> { await initActualApi(); return api.createPayee(args); } /** * Update a payee (ensures API is initialized) */ export async function updatePayee(id: string, args: Record<string, unknown>): Promise<unknown> { await initActualApi(); return api.updatePayee(id, args); } /** * Delete a payee (ensures API is initialized) */ export async function deletePayee(id: string): Promise<unknown> { await initActualApi(); return api.deletePayee(id); } /** * Create a new rule (ensures API is initialized) */ export async function createRule(args: Record<string, unknown>): Promise<RuleEntity> { await initActualApi(); return api.createRule(args); } /** * Update a rule (ensures API is initialized) */ export async function updateRule(args: Record<string, unknown>): Promise<RuleEntity> { await initActualApi(); return api.updateRule(args); } /** * Delete a rule (ensures API is initialized) */ export async function deleteRule(id: string): Promise<boolean> { await initActualApi(); return api.deleteRule(id); } /** * Create a new category (ensures API is initialized) */ export async function createCategory(args: Record<string, unknown>): Promise<string> { await initActualApi(); return api.createCategory(args); } /** * Update a category (ensures API is initialized) */ export async function updateCategory(id: string, args: Record<string, unknown>): Promise<unknown> { await initActualApi(); return api.updateCategory(id, args); } /** * Delete a category (ensures API is initialized) */ export async function deleteCategory(id: string): Promise<{ error?: string }> { await initActualApi(); return api.deleteCategory(id); } /** * Create a new category group (ensures API is initialized) */ export async function createCategoryGroup(args: Record<string, unknown>): Promise<string> { await initActualApi(); return api.createCategoryGroup(args); } /** * Update a category group (ensures API is initialized) */ export async function updateCategoryGroup(id: string, args: Record<string, unknown>): Promise<unknown> { await initActualApi(); return api.updateCategoryGroup(id, args); } /** * Delete a category group (ensures API is initialized) */ export async function deleteCategoryGroup(id: string): Promise<unknown> { await initActualApi(); return api.deleteCategoryGroup(id); }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/s-stefanov/actual-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server