docs/addons/addon-api-reference.md
Complete reference for Wealthfolio addon APIs. All functions require appropriate
permissions in manifest.json.
The AddonContext is provided to your addon's enable function:
export interface AddonContext {
api: HostAPI;
sidebar: SidebarAPI;
router: RouterAPI;
onDisable: (callback: () => void) => void;
}
Basic usage:
export default function enable(ctx: AddonContext) {
// Access APIs
const accounts = await ctx.api.accounts.getAll();
#### `onDropHover(callback: EventCallback): Promise<UnlistenFn>`
Fires when files are hovered over for import.
```typescript
const unlistenHover = await ctx.api.events.import.onDropHover((event) => {
console.log('File hover detected');
showDropZone();
});
onDrop(callback: EventCallback): Promise<UnlistenFn>Fires when files are dropped for import.
const unlistenImport = await ctx.api.events.import.onDrop((event) => {
console.log("File dropped:", event.payload);
// Trigger import workflow
handleFileImport(event.payload.files);
});
onDropCancelled(callback: EventCallback): Promise<UnlistenFn>Fires when file drop is cancelled.
const unlistenCancel = await ctx.api.events.import.onDropCancelled(() => {
console.log("File drop cancelled");
hideDropZone();
});
Navigate programmatically within the Wealthfolio application.
navigate(route: string): Promise<void>Navigate to a specific route in the application.
// Navigate to a specific account
await ctx.api.navigation.navigate("/accounts/account-123");
// Navigate to portfolio overview
await ctx.api.navigation.navigate("/portfolio");
// Navigate to activities page
await ctx.api.navigation.navigate("/activities");
// Navigate to settings
await ctx.api.navigation.navigate("/settings");
Info Navigation Routes: The navigation API uses the same route structure as the main application. Common routes include
/accounts,/portfolio,/activities,/goals, and/settings.
Access and manipulate the shared React Query client for efficient data management.
getClient(): QueryClientGets the shared QueryClient instance from the main application.
const queryClient = ctx.api.query.getClient();
// Use standard React Query methods
const accounts = await queryClient.fetchQuery({
queryKey: ["accounts"],
queryFn: () => ctx.api.accounts.getAll(),
});
invalidateQueries(queryKey: string | string[]): voidInvalidates queries to trigger refetch.
// Invalidate specific query
ctx.api.query.invalidateQueries(["accounts"]);
// Invalidate multiple related queries
ctx.api.query.invalidateQueries(["portfolio", "holdings"]);
// Invalidate all account-related queries
ctx.api.query.invalidateQueries(["accounts"]);
refetchQueries(queryKey: string | string[]): voidTriggers immediate refetch of queries.
// Refetch portfolio data
ctx.api.query.refetchQueries(["portfolio"]);
// Refetch multiple queries
ctx.api.query.refetchQueries(["accounts", "holdings"]);
Combine Query API with event listeners for reactive data updates:
export default function enable(ctx: AddonContext) {
// Invalidate relevant queries when portfolio updates
const unlistenPortfolio = await ctx.api.events.portfolio.onUpdateComplete(
() => {
ctx.api.query.invalidateQueries(["portfolio", "holdings", "performance"]);
},
);
// Invalidate market data queries when sync completes
const unlistenMarket = await ctx.api.events.market.onSyncComplete(() => {
ctx.api.query.invalidateQueries(["quotes", "assets"]);
});
ctx.onDisable(() => {
unlistenPortfolio();
unlistenMarket();
});
}
Add navigation items to the main application sidebar.
addItem(item: SidebarItem): SidebarItemHandleconst sidebarItem = ctx.sidebar.addItem({
id: "my-addon",
label: "My Addon",
route: "/addon/my-addon",
icon: MyAddonIcon, // Optional React component
order: 100, // Lower numbers appear first
});
// Remove when addon is disabled
ctx.onDisable(() => {
sidebarItem.remove();
});
Register routes for your addon's pages.
add(route: RouteConfig): voidctx.router.add({
path: "/addon/my-addon",
component: React.lazy(() => Promise.resolve({ default: MyAddonComponent })),
});
// Multiple routes
ctx.router.add({
path: "/addon/my-addon/settings",
component: React.lazy(() => Promise.resolve({ default: MyAddonSettings })),
});
interface APIError {
code: string;
message: string;
details?: any;
}
Common error codes:
PERMISSION_DENIED - Insufficient permissionsNOT_FOUND - Resource not foundVALIDATION_ERROR - Invalid data providedNETWORK_ERROR - Connection issuesRATE_LIMITED - Too many requeststry {
const accounts = await ctx.api.accounts.getAll();
page: number, pageSize: number, filters: ActivitySearchFilters, searchKeyword: string, sort?: ActivitySort, ): Promise<ActivitySearchResponse>`
Search activities with pagination, filters, and a single optional sort.
page: Zero-based page index. Use 0 for the first page (e.g., for exports
you can pass 0 with a large pageSize such as 1000).pageSize: Number of rows per page.filters: Accepts single strings or arrays for accountIds and
activityTypes. Empty strings/arrays are ignored.searchKeyword: Free-form keyword search; pass an empty string when unused.sort: Optional sort object (defaults to { id: "date", desc: true }). Only
one sort is supported.const { data, meta } = await ctx.api.activities.search(
0,
50,
{
accountIds: "account-1", // single string or string[] both work
activityTypes: ["BUY", "DIVIDEND"],
symbol: "AAPL", // optional exact symbol filter
},
"", // optional search keyword
{ id: "date", desc: true },
);
console.log(meta.totalRowCount);
update(activity: ActivityUpdate): Promise<Activity>Updates an existing activity with conflict detection.
const updated = await ctx.api.activities.update({
...existingActivity,
quantity: 150,
unitPrice: 145.75,
});
saveMany(activities: ActivityUpdate[]): Promise<Activity[]>Efficiently creates multiple activities in a single transaction.
const activities = await ctx.api.activities.saveMany([
{ accountId: "account-123", activityType: "BUY" /* ... */ },
{ accountId: "account-123", activityType: "DIVIDEND" /* ... */ },
]);
delete(activityId: string): Promise<void>Deletes an activity and updates portfolio calculations.
await ctx.api.activities.delete("activity-456");
import(activities: ActivityImport[]): Promise<ActivityImport[]>Imports validated activities with duplicate detection.
const imported = await ctx.api.activities.import(checkedActivities);
checkImport(accountId: string, activities: ActivityImport[]): Promise<ActivityImport[]>Validates activities before import with error reporting.
const validated = await ctx.api.activities.checkImport(
"account-123",
activities,
);
getImportMapping(accountId: string): Promise<ImportMappingData>Get import mapping configuration for an account.
const mapping = await ctx.api.activities.getImportMapping("account-123");
saveImportMapping(mapping: ImportMappingData): Promise<ImportMappingData>Save import mapping configuration.
const savedMapping = await ctx.api.activities.saveImportMapping(mapping);
try {
const accounts = await ctx.api.accounts.getAll();
} catch (error) {
if (error.code === "PERMISSION_DENIED") {
ctx.api.logger.error("Missing account permissions");
// Show user-friendly message
} else if (error.code === "NETWORK_ERROR") {
ctx.api.logger.warn("Network issue, retrying...");
// Implement retry logic
} else {
ctx.api.logger.error("Unexpected error:", error);
// General error handling
}
}
// Efficient batch processing
const activities = await Promise.all([
ctx.api.activities.getAll("account-1"),
ctx.api.activities.getAll("account-2"),
ctx.api.activities.getAll("account-3"),
]);
// You can also scope results to a single account by passing its ID as a string.
// The host normalizes this for both desktop (Tauri) and web modes—no need to
// wrap the value in an array.
// Activity search accepts single values or arrays for filters.
const { data, meta } = await ctx.api.activities.search(
0,
50,
{
accountIds: "account-1", // single string or string[] both work
activityTypes: ["BUY", "DIVIDEND"],
symbol: "AAPL", // optional exact symbol filter
},
"", // optional search keyword
{ id: "date", desc: true },
);
console.log(meta.totalRowCount);
// Batch create
const newActivities = await ctx.api.activities.saveMany([
{
/* activity 1 */
},
{
/* activity 2 */
},
{
/* activity 3 */
},
]);
export default function enable(ctx: AddonContext) {
// Listen for multiple events
const unsubscribers = [
await ctx.api.events.portfolio.onUpdateComplete(() => refreshData()),
await ctx.api.events.market.onSyncComplete(() => updatePrices()),
await ctx.api.events.import.onDrop((event) => handleImport(event)),
];
// Clean up all listeners
ctx.onDisable(() => {
unsubscribers.forEach((unsub) => unsub());
});
}
// Simple in-memory cache
const cache = new Map();
async function getCachedAccounts() {
if (cache.has("accounts")) {
return cache.get("accounts");
}
const accounts = await ctx.api.accounts.getAll();
cache.set("accounts", accounts);
// Invalidate cache on updates
const unlisten = await ctx.api.events.portfolio.onUpdateComplete(() => {
cache.delete("accounts");
});
return accounts;
}
Full TypeScript definitions are provided for all APIs:
import type {
AddonContext,
Account,
Activity,
Holding,
PerformanceHistory,
PerformanceSummary,
// ... and many more
} from "@wealthfolio/addon-sdk";
// Type-safe API usage
const accounts: Account[] = await ctx.api.accounts.getAll();
const holdings: Holding[] = await ctx.api.portfolio.getHoldings(accounts[0].id);
Ready to build? Check out our examples to see these APIs in action! const history = await ctx.api.quotes.getHistory('AAPL');
---
## Performance API
Calculate portfolio and account performance metrics with historical analysis.
### Methods
#### `calculateHistory(itemType: 'account' | 'symbol', itemId: string, startDate: string, endDate: string): Promise<PerformanceMetrics>`
Calculates detailed performance history for charts and analysis.
```typescript
const history = await ctx.api.performance.calculateHistory(
'account',
'account-123',
'2024-01-01',
'2024-12-31'
);
calculateSummary(args: { itemType: 'account' | 'symbol'; itemId: string; startDate?: string | null; endDate?: string | null; }): Promise<PerformanceMetrics>Calculates comprehensive performance summary with key metrics.
const summary = await ctx.api.performance.calculateSummary({
itemType: "account",
itemId: "account-123",
startDate: "2024-01-01",
endDate: "2024-12-31",
});
calculateAccountsSimple(accountIds: string[]): Promise<SimplePerformanceMetrics[]>Calculates simple performance metrics for multiple accounts efficiently.
const performance = await ctx.api.performance.calculateAccountsSimple([
"account-123",
"account-456",
]);
Manage currency exchange rates for multi-currency portfolios.
getAll(): Promise<ExchangeRate[]>Gets all exchange rates.
const rates = await ctx.api.exchangeRates.getAll();
update(updatedRate: ExchangeRate): Promise<ExchangeRate>Updates an existing exchange rate.
const updatedRate = await ctx.api.exchangeRates.update({
id: "rate-123",
fromCurrency: "USD",
toCurrency: "EUR",
rate: 0.85,
// ... other rate data
});
add(newRate: Omit<ExchangeRate, 'id'>): Promise<ExchangeRate>Adds a new exchange rate.
const newRate = await ctx.api.exchangeRates.add({
fromCurrency: "USD",
toCurrency: "GBP",
rate: 0.75,
// ... other rate data
});
Manage investment contribution limits and calculations.
getAll(): Promise<ContributionLimit[]>Gets all contribution limits.
const limits = await ctx.api.contributionLimits.getAll();
create(newLimit: NewContributionLimit): Promise<ContributionLimit>Creates a new contribution limit.
const limit = await ctx.api.contributionLimits.create({
name: "RRSP 2024",
limitType: "RRSP",
maxAmount: 30000,
year: 2024,
// ... other limit data
});
update(id: string, updatedLimit: NewContributionLimit): Promise<ContributionLimit>Updates an existing contribution limit.
const updatedLimit = await ctx.api.contributionLimits.update("limit-123", {
name: "Updated RRSP 2024",
maxAmount: 31000,
// ... other updated data
});
calculateDeposits(limitId: string): Promise<DepositsCalculation>Calculates deposits for a specific contribution limit.
const deposits =
await ctx.api.contributionLimits.calculateDeposits("limit-123");
Manage financial goals and allocations.
getAll(): Promise<Goal[]>Gets all goals.
const goals = await ctx.api.goals.getAll();
create(goal: any): Promise<Goal>Creates a new goal.
const goal = await ctx.api.goals.create({
name: "Retirement Fund",
targetAmount: 500000,
targetDate: "2040-01-01",
// ... other goal data
});
update(goal: Goal): Promise<Goal>Updates an existing goal.
const updatedGoal = await ctx.api.goals.update({
...existingGoal,
targetAmount: 600000,
});
getFunding(goalId: string): Promise<GoalAllocation[]>Gets funding rules for a goal.
const allocations = await ctx.api.goals.getFunding("goal-123");
saveFunding(goalId: string, allocations: GoalAllocation[]): Promise<GoalAllocation[]>Saves funding rules for a goal.
await ctx.api.goals.saveFunding("goal-123", [
{ goalId: "goal-123", accountId: "account-456", sharePercent: 50 },
]);
updateAllocations(allocations: GoalAllocation[]): Promise<void>Updates goal allocations.
Deprecated: use saveFunding(goalId, allocations) instead.
await ctx.api.goals.updateAllocations([
{ goalId: "goal-123", accountId: "account-456", sharePercent: 50 },
// ... other allocations
]);
getAllocations(): Promise<GoalAllocation[]>Gets goal allocations.
Deprecated: use getAll() and getFunding(goalId) instead.
const allocations = await ctx.api.goals.getAllocations();
Manage application settings and configuration.
get(): Promise<Settings>Gets application settings.
const settings = await ctx.api.settings.get();
update(settingsUpdate: Settings): Promise<Settings>Updates application settings.
const updatedSettings = await ctx.api.settings.update({
...currentSettings,
baseCurrency: "EUR",
// ... other settings
});
backupDatabase(): Promise<{ filename: string; data: Uint8Array }>Creates a database backup.
const backup = await ctx.api.settings.backupDatabase();
Handle file operations and dialogs.
openCsvDialog(): Promise<null | string | string[]>Opens a CSV file selection dialog.
const files = await ctx.api.files.openCsvDialog();
if (files) {
// Process selected files
}
openSaveDialog(fileContent: Uint8Array | Blob | string, fileName: string): Promise<any>Opens a file save dialog.
const result = await ctx.api.files.openSaveDialog(fileContent, "export.csv");
Securely store and retrieve sensitive data like API keys and tokens. All data is scoped to your addon for security.
set(key: string, value: string): Promise<void>Stores a secret value encrypted and scoped to your addon.
// Store API key securely
await ctx.api.secrets.set("api-key", "your-secret-api-key");
// Store user credentials
await ctx.api.secrets.set("auth-token", userAuthToken);
get(key: string): Promise<string | null>Retrieves a secret value (returns null if not found).
const apiKey = await ctx.api.secrets.get("api-key");
if (apiKey) {
// Use the API key
const data = await fetch(`https://api.example.com/data?key=${apiKey}`);
}
delete(key: string): Promise<void>Permanently deletes a secret.
await ctx.api.secrets.delete("old-api-key");
Info Security Note: Secrets are encrypted at rest and scoped to your addon. Other addons cannot access your secrets, and you cannot access theirs.
Provides logging functionality with automatic addon prefix.
error(message: string): voidLogs an error message.
ctx.api.logger.error("Failed to fetch data from API");
info(message: string): voidLogs an informational message.
ctx.api.logger.info("Data sync completed successfully");
warn(message: string): voidLogs a warning message.
ctx.api.logger.warn("API rate limit approaching");
debug(message: string): voidLogs a debug message.
ctx.api.logger.debug("Processing 100 activities");
trace(message: string): voidLogs a trace message for detailed debugging.
ctx.api.logger.trace("Entering function processActivity");
Listen to real-time events for responsive addon behavior.
onUpdateStart(callback: EventCallback): Promise<UnlistenFn>Fires when portfolio update starts.
const unlistenStart = await ctx.api.events.portfolio.onUpdateStart((event) => {
console.log("Portfolio update started");
showLoadingIndicator();
});
onUpdateComplete(callback: EventCallback): Promise<UnlistenFn>Fires when portfolio calculations are updated.
const unlistenPortfolio = await ctx.api.events.portfolio.onUpdateComplete(
(event) => {
console.log("Portfolio updated:", event.payload);
// Refresh your addon's data
refreshPortfolioData();
},
);
// Clean up on disable
ctx.onDisable(() => {
unlistenPortfolio();
});
onUpdateError(callback: EventCallback): Promise<UnlistenFn>Fires when portfolio update encounters an error.
const unlistenError = await ctx.api.events.portfolio.onUpdateError((event) => {
console.error("Portfolio update failed:", event.payload);
showErrorMessage();
});
onSyncStart(callback: EventCallback): Promise<UnlistenFn>Fires when market data sync starts.
const unlistenSyncStart = await ctx.api.events.market.onSyncStart(() => {
console.log("Market sync started");
showSyncIndicator();
});
onSyncComplete(callback: EventCallback): Promise<UnlistenFn>Fires when market data sync is completed.
const unlistenMarket = await ctx.api.events.market.onSyncComplete(() => {
console.log("Market data updated!");
// Update price displays
updatePriceDisplays();
});