{
  "schemaVersion": "1.0",
  "item": {
    "slug": "actual-budget",
    "name": "Actual Budget",
    "source": "tencent",
    "type": "skill",
    "category": "开发工具",
    "sourceUrl": "https://clawhub.ai/ThisIsJeron/actual-budget",
    "canonicalUrl": "https://clawhub.ai/ThisIsJeron/actual-budget",
    "targetPlatform": "OpenClaw"
  },
  "install": {
    "downloadMode": "redirect",
    "downloadUrl": "/downloads/actual-budget",
    "sourceDownloadUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=actual-budget",
    "sourcePlatform": "tencent",
    "targetPlatform": "OpenClaw",
    "installMethod": "Manual import",
    "extraction": "Extract archive",
    "prerequisites": [
      "OpenClaw"
    ],
    "packageFormat": "ZIP package",
    "includedAssets": [
      "SKILL.md",
      "_meta.json"
    ],
    "primaryDoc": "SKILL.md",
    "quickSetup": [
      "Download the package from Yavira.",
      "Extract the archive and review SKILL.md first.",
      "Import or place the package into your OpenClaw setup."
    ],
    "agentAssist": {
      "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
      "steps": [
        "Download the package from Yavira.",
        "Extract it into a folder your agent can access.",
        "Paste one of the prompts below and point your agent at the extracted folder."
      ],
      "prompts": [
        {
          "label": "New install",
          "body": "I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Tell me what you changed and call out any manual steps you could not complete."
        },
        {
          "label": "Upgrade existing",
          "body": "I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Summarize what changed and any follow-up checks I should run."
        }
      ]
    },
    "sourceHealth": {
      "source": "tencent",
      "status": "healthy",
      "reason": "direct_download_ok",
      "recommendedAction": "download",
      "checkedAt": "2026-04-23T16:43:11.935Z",
      "expiresAt": "2026-04-30T16:43:11.935Z",
      "httpStatus": 200,
      "finalUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=4claw-imageboard",
      "contentType": "application/zip",
      "probeMethod": "head",
      "details": {
        "probeUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=4claw-imageboard",
        "contentDisposition": "attachment; filename=\"4claw-imageboard-1.0.1.zip\"",
        "redirectLocation": null,
        "bodySnippet": null
      },
      "scope": "source",
      "summary": "Source download looks usable.",
      "detail": "Yavira can redirect you to the upstream package for this source.",
      "primaryActionLabel": "Download for OpenClaw",
      "primaryActionHref": "/downloads/actual-budget"
    },
    "validation": {
      "installChecklist": [
        "Use the Yavira download entry.",
        "Review SKILL.md after the package is downloaded.",
        "Confirm the extracted package contains the expected setup assets."
      ],
      "postInstallChecks": [
        "Confirm the extracted package includes the expected docs or setup files.",
        "Validate the skill or prompts are available in your target agent workspace.",
        "Capture any manual follow-up steps the agent could not complete."
      ]
    },
    "downloadPageUrl": "https://openagent3.xyz/downloads/actual-budget",
    "agentPageUrl": "https://openagent3.xyz/skills/actual-budget/agent",
    "manifestUrl": "https://openagent3.xyz/skills/actual-budget/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/actual-budget/agent.md"
  },
  "agentAssist": {
    "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
    "steps": [
      "Download the package from Yavira.",
      "Extract it into a folder your agent can access.",
      "Paste one of the prompts below and point your agent at the extracted folder."
    ],
    "prompts": [
      {
        "label": "New install",
        "body": "I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Tell me what you changed and call out any manual steps you could not complete."
      },
      {
        "label": "Upgrade existing",
        "body": "I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Summarize what changed and any follow-up checks I should run."
      }
    ]
  },
  "documentation": {
    "source": "clawhub",
    "primaryDoc": "SKILL.md",
    "sections": [
      {
        "title": "Actual Budget API",
        "body": "Official Node.js API for Actual Budget. Runs headless — works on local budget data synced from your server."
      },
      {
        "title": "Installation",
        "body": "npm install @actual-app/api"
      },
      {
        "title": "Environment Variables",
        "body": "VariableRequiredDescriptionACTUAL_SERVER_URLYesServer URL (e.g., https://actual.example.com)ACTUAL_PASSWORDYesServer passwordACTUAL_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 enabledNODE_EXTRA_CA_CERTSNoPath to CA certificate file for self-signed certs"
      },
      {
        "title": "Self-Signed Certificates",
        "body": "If your Actual Budget server uses a self-signed certificate:\n\nRecommended: Add your CA to the system trust store, or\nAlternative: Set NODE_EXTRA_CA_CERTS=/path/to/your-ca.pem to trust your specific CA\n\nAvoid disabling TLS verification entirely — it exposes you to man-in-the-middle attacks."
      },
      {
        "title": "Quick Start",
        "body": "const api = require('@actual-app/api');\n\nawait api.init({\n  dataDir: process.env.ACTUAL_DATA_DIR || '/tmp/actual-cache',\n  serverURL: process.env.ACTUAL_SERVER_URL,\n  password: process.env.ACTUAL_PASSWORD,\n});\n\nawait api.downloadBudget(\n  process.env.ACTUAL_SYNC_ID,\n  process.env.ACTUAL_ENCRYPTION_PASSWORD ? { password: process.env.ACTUAL_ENCRYPTION_PASSWORD } : undefined\n);\n\n// ... do work ...\n\nawait api.shutdown();"
      },
      {
        "title": "Core Concepts",
        "body": "Amounts are integers in cents: $50.00 = 5000, -1200 = expense of $12.00\nDates use YYYY-MM-DD, months use YYYY-MM\nIDs are UUIDs — use getIDByName(type, name) to look up by name\nConvert with api.utils.amountToInteger(123.45) → 12345"
      },
      {
        "title": "Get Budget Overview",
        "body": "const months = await api.getBudgetMonths();        // ['2026-01', '2026-02', ...]\nconst jan = await api.getBudgetMonth('2026-01');   // { categoryGroups, incomeAvailable, ... }"
      },
      {
        "title": "Accounts",
        "body": "const accounts = await api.getAccounts();\nconst balance = await api.getAccountBalance(accountId);\nconst newId = await api.createAccount({ name: 'Checking', type: 'checking' }, 50000); // $500 initial\nawait api.closeAccount(id, transferToAccountId);  // transfer remaining balance"
      },
      {
        "title": "Transactions",
        "body": "// Get transactions for date range\nconst txns = await api.getTransactions(accountId, '2026-01-01', '2026-01-31');\n\n// Import with deduplication + rules (preferred for bank imports)\nconst { added, updated } = await api.importTransactions(accountId, [\n  { date: '2026-01-15', amount: -2500, payee_name: 'Grocery Store', notes: 'Weekly run' },\n  { date: '2026-01-16', amount: -1200, payee_name: 'Coffee Shop', imported_id: 'bank-123' },\n]);\n\n// Update a transaction\nawait api.updateTransaction(txnId, { category: categoryId, cleared: true });"
      },
      {
        "title": "Categories & Payees",
        "body": "const categories = await api.getCategories();\nconst groups = await api.getCategoryGroups();\nconst payees = await api.getPayees();\n\n// Create\nconst catId = await api.createCategory({ name: 'Subscriptions', group_id: groupId });\nconst payeeId = await api.createPayee({ name: 'Netflix', category: catId });"
      },
      {
        "title": "Budget Amounts",
        "body": "await api.setBudgetAmount('2026-01', categoryId, 30000);  // budget $300\nawait api.setBudgetCarryover('2026-01', categoryId, true);"
      },
      {
        "title": "Rules",
        "body": "const rules = await api.getRules();\nawait api.createRule({\n  stage: 'pre',\n  conditionsOp: 'and',\n  conditions: [{ field: 'payee', op: 'is', value: payeeId }],\n  actions: [{ op: 'set', field: 'category', value: categoryId }],\n});"
      },
      {
        "title": "Schedules",
        "body": "const schedules = await api.getSchedules();\nawait api.createSchedule({\n  payee: payeeId,\n  account: accountId,\n  amount: -1500,\n  date: { frequency: 'monthly', start: '2026-01-01', interval: 1, endMode: 'never' },\n});"
      },
      {
        "title": "Bank Sync",
        "body": "await api.runBankSync({ accountId });  // GoCardless/SimpleFIN"
      },
      {
        "title": "Sync & Shutdown",
        "body": "await api.sync();      // push/pull changes to server\nawait api.shutdown();  // always call when done"
      },
      {
        "title": "ActualQL Queries",
        "body": "For complex queries, use ActualQL:\n\nconst { q, runQuery } = require('@actual-app/api');\n\n// Sum expenses by category this month\nconst { data } = await runQuery(\n  q('transactions')\n    .filter({\n      date: [{ $gte: '2026-01-01' }, { $lte: '2026-01-31' }],\n      amount: { $lt: 0 },\n    })\n    .groupBy('category.name')\n    .select(['category.name', { total: { $sum: '$amount' } }])\n);\n\n// Search transactions\nconst { data } = await runQuery(\n  q('transactions')\n    .filter({ 'payee.name': { $like: '%grocery%' } })\n    .select(['date', 'amount', 'payee.name', 'category.name'])\n    .orderBy({ date: 'desc' })\n    .limit(20)\n);\n\nOperators: $eq, $lt, $lte, $gt, $gte, $ne, $oneof, $regex, $like, $notlike\nSplits: .options({ splits: 'inline' | 'grouped' | 'all' })"
      },
      {
        "title": "Helpers",
        "body": "// Look up ID by name\nconst acctId = await api.getIDByName('accounts', 'Checking');\nconst catId = await api.getIDByName('categories', 'Food');\nconst payeeId = await api.getIDByName('payees', 'Amazon');\n\n// List budgets\nconst budgets = await api.getBudgets();  // local + remote files"
      },
      {
        "title": "Transfers",
        "body": "Transfers use special payees. Find transfer payee by transfer_acct field:\n\nconst payees = await api.getPayees();\nconst transferPayee = payees.find(p => p.transfer_acct === targetAccountId);\nawait api.importTransactions(fromAccountId, [\n  { date: '2026-01-15', amount: -10000, payee: transferPayee.id }\n]);"
      },
      {
        "title": "Split Transactions",
        "body": "await api.importTransactions(accountId, [{\n  date: '2026-01-15',\n  amount: -5000,\n  payee_name: 'Costco',\n  subtransactions: [\n    { amount: -3000, category: groceryCatId },\n    { amount: -2000, category: householdCatId },\n  ]\n}]);"
      },
      {
        "title": "Bulk Import (New Budget)",
        "body": "For migrating from another app:\n\nawait api.runImport('My-New-Budget', async () => {\n  for (const acct of myData.accounts) {\n    const id = await api.createAccount(acct);\n    await api.addTransactions(id, myData.transactions.filter(t => t.acctId === id));\n  }\n});"
      },
      {
        "title": "Reference",
        "body": "Full API: https://actualbudget.org/docs/api/reference\nActualQL: https://actualbudget.org/docs/api/actual-ql"
      }
    ],
    "body": "Actual Budget API\n\nOfficial Node.js API for Actual Budget. Runs headless — works on local budget data synced from your server.\n\nInstallation\nnpm install @actual-app/api\n\nEnvironment Variables\nVariable\tRequired\tDescription\nACTUAL_SERVER_URL\tYes\tServer URL (e.g., https://actual.example.com)\nACTUAL_PASSWORD\tYes\tServer password\nACTUAL_SYNC_ID\tYes\tBudget Sync ID (Settings → Advanced → Sync ID)\nACTUAL_DATA_DIR\tNo\tLocal cache directory for budget data (defaults to cwd)\nACTUAL_ENCRYPTION_PASSWORD\tNo\tE2E encryption password, if enabled\nNODE_EXTRA_CA_CERTS\tNo\tPath to CA certificate file for self-signed certs\nSelf-Signed Certificates\n\nIf your Actual Budget server uses a self-signed certificate:\n\nRecommended: Add your CA to the system trust store, or\nAlternative: Set NODE_EXTRA_CA_CERTS=/path/to/your-ca.pem to trust your specific CA\n\nAvoid disabling TLS verification entirely — it exposes you to man-in-the-middle attacks.\n\nQuick Start\nconst api = require('@actual-app/api');\n\nawait api.init({\n  dataDir: process.env.ACTUAL_DATA_DIR || '/tmp/actual-cache',\n  serverURL: process.env.ACTUAL_SERVER_URL,\n  password: process.env.ACTUAL_PASSWORD,\n});\n\nawait api.downloadBudget(\n  process.env.ACTUAL_SYNC_ID,\n  process.env.ACTUAL_ENCRYPTION_PASSWORD ? { password: process.env.ACTUAL_ENCRYPTION_PASSWORD } : undefined\n);\n\n// ... do work ...\n\nawait api.shutdown();\n\nCore Concepts\nAmounts are integers in cents: $50.00 = 5000, -1200 = expense of $12.00\nDates use YYYY-MM-DD, months use YYYY-MM\nIDs are UUIDs — use getIDByName(type, name) to look up by name\nConvert with api.utils.amountToInteger(123.45) → 12345\nCommon Operations\nGet Budget Overview\nconst months = await api.getBudgetMonths();        // ['2026-01', '2026-02', ...]\nconst jan = await api.getBudgetMonth('2026-01');   // { categoryGroups, incomeAvailable, ... }\n\nAccounts\nconst accounts = await api.getAccounts();\nconst balance = await api.getAccountBalance(accountId);\nconst newId = await api.createAccount({ name: 'Checking', type: 'checking' }, 50000); // $500 initial\nawait api.closeAccount(id, transferToAccountId);  // transfer remaining balance\n\nTransactions\n// Get transactions for date range\nconst txns = await api.getTransactions(accountId, '2026-01-01', '2026-01-31');\n\n// Import with deduplication + rules (preferred for bank imports)\nconst { added, updated } = await api.importTransactions(accountId, [\n  { date: '2026-01-15', amount: -2500, payee_name: 'Grocery Store', notes: 'Weekly run' },\n  { date: '2026-01-16', amount: -1200, payee_name: 'Coffee Shop', imported_id: 'bank-123' },\n]);\n\n// Update a transaction\nawait api.updateTransaction(txnId, { category: categoryId, cleared: true });\n\nCategories & Payees\nconst categories = await api.getCategories();\nconst groups = await api.getCategoryGroups();\nconst payees = await api.getPayees();\n\n// Create\nconst catId = await api.createCategory({ name: 'Subscriptions', group_id: groupId });\nconst payeeId = await api.createPayee({ name: 'Netflix', category: catId });\n\nBudget Amounts\nawait api.setBudgetAmount('2026-01', categoryId, 30000);  // budget $300\nawait api.setBudgetCarryover('2026-01', categoryId, true);\n\nRules\nconst rules = await api.getRules();\nawait api.createRule({\n  stage: 'pre',\n  conditionsOp: 'and',\n  conditions: [{ field: 'payee', op: 'is', value: payeeId }],\n  actions: [{ op: 'set', field: 'category', value: categoryId }],\n});\n\nSchedules\nconst schedules = await api.getSchedules();\nawait api.createSchedule({\n  payee: payeeId,\n  account: accountId,\n  amount: -1500,\n  date: { frequency: 'monthly', start: '2026-01-01', interval: 1, endMode: 'never' },\n});\n\nBank Sync\nawait api.runBankSync({ accountId });  // GoCardless/SimpleFIN\n\nSync & Shutdown\nawait api.sync();      // push/pull changes to server\nawait api.shutdown();  // always call when done\n\nActualQL Queries\n\nFor complex queries, use ActualQL:\n\nconst { q, runQuery } = require('@actual-app/api');\n\n// Sum expenses by category this month\nconst { data } = await runQuery(\n  q('transactions')\n    .filter({\n      date: [{ $gte: '2026-01-01' }, { $lte: '2026-01-31' }],\n      amount: { $lt: 0 },\n    })\n    .groupBy('category.name')\n    .select(['category.name', { total: { $sum: '$amount' } }])\n);\n\n// Search transactions\nconst { data } = await runQuery(\n  q('transactions')\n    .filter({ 'payee.name': { $like: '%grocery%' } })\n    .select(['date', 'amount', 'payee.name', 'category.name'])\n    .orderBy({ date: 'desc' })\n    .limit(20)\n);\n\n\nOperators: $eq, $lt, $lte, $gt, $gte, $ne, $oneof, $regex, $like, $notlike Splits: .options({ splits: 'inline' | 'grouped' | 'all' })\n\nHelpers\n// Look up ID by name\nconst acctId = await api.getIDByName('accounts', 'Checking');\nconst catId = await api.getIDByName('categories', 'Food');\nconst payeeId = await api.getIDByName('payees', 'Amazon');\n\n// List budgets\nconst budgets = await api.getBudgets();  // local + remote files\n\nTransfers\n\nTransfers use special payees. Find transfer payee by transfer_acct field:\n\nconst payees = await api.getPayees();\nconst transferPayee = payees.find(p => p.transfer_acct === targetAccountId);\nawait api.importTransactions(fromAccountId, [\n  { date: '2026-01-15', amount: -10000, payee: transferPayee.id }\n]);\n\nSplit Transactions\nawait api.importTransactions(accountId, [{\n  date: '2026-01-15',\n  amount: -5000,\n  payee_name: 'Costco',\n  subtransactions: [\n    { amount: -3000, category: groceryCatId },\n    { amount: -2000, category: householdCatId },\n  ]\n}]);\n\nBulk Import (New Budget)\n\nFor migrating from another app:\n\nawait api.runImport('My-New-Budget', async () => {\n  for (const acct of myData.accounts) {\n    const id = await api.createAccount(acct);\n    await api.addTransactions(id, myData.transactions.filter(t => t.acctId === id));\n  }\n});\n\nReference\n\nFull API: https://actualbudget.org/docs/api/reference ActualQL: https://actualbudget.org/docs/api/actual-ql"
  },
  "trust": {
    "sourceLabel": "tencent",
    "provenanceUrl": "https://clawhub.ai/ThisIsJeron/actual-budget",
    "publisherUrl": "https://clawhub.ai/ThisIsJeron/actual-budget",
    "owner": "ThisIsJeron",
    "version": "1.0.2",
    "license": null,
    "verificationStatus": "Indexed source record"
  },
  "links": {
    "detailUrl": "https://openagent3.xyz/skills/actual-budget",
    "downloadUrl": "https://openagent3.xyz/downloads/actual-budget",
    "agentUrl": "https://openagent3.xyz/skills/actual-budget/agent",
    "manifestUrl": "https://openagent3.xyz/skills/actual-budget/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/actual-budget/agent.md"
  }
}