{"openapi":"3.0.0","paths":{"/households/{householdId}/accounts":{"post":{"operationId":"AccountsController_create","parameters":[],"responses":{"201":{"description":""}},"security":[{"bearer":[]}],"summary":"Create a financial account","tags":["accounts"],"x-required-scope":["write:accounts"]},"get":{"operationId":"AccountsController_list","parameters":[{"name":"status","required":false,"in":"query","schema":{"enum":["open","closed"],"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"List accounts in this household","tags":["accounts"],"x-required-scope":["read:accounts"]}},"/households/{householdId}/accounts/reorder":{"post":{"operationId":"AccountsController_reorder","parameters":[],"responses":{"204":{"description":""}},"security":[{"bearer":[]}],"summary":"Atomically set the manual position of accounts within their groups","tags":["accounts"],"x-required-scope":["write:accounts"]}},"/households/{householdId}/accounts/{accountId}":{"get":{"operationId":"AccountsController_getOne","parameters":[{"name":"accountId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Get a single account with its computed balance","tags":["accounts"],"x-required-scope":["read:accounts"]},"patch":{"operationId":"AccountsController_update","parameters":[{"name":"accountId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Update an account","tags":["accounts"],"x-required-scope":["write:accounts"]},"delete":{"operationId":"AccountsController_remove","parameters":[{"name":"accountId","required":true,"in":"path","schema":{"type":"string"}},{"name":"force","required":false,"in":"query","schema":{"type":"boolean"}}],"responses":{"204":{"description":""}},"security":[{"bearer":[]}],"summary":"Soft-delete an account (rejects non-zero balance unless force)","tags":["accounts"],"x-required-scope":["write:accounts"]}},"/households/{householdId}/accounts/{accountId}/reconcile":{"post":{"operationId":"AccountsController_reconcile","parameters":[{"name":"accountId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Reconcile account against the real bank balance: books an adjustment for any gap, then locks cleared transactions as reconciled","tags":["accounts"],"x-required-scope":["write:accounts"]}},"/households/{householdId}/accounts/{accountId}/convert":{"post":{"operationId":"AccountsController_convert","parameters":[{"name":"accountId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Convert an account to another type (safe moves only: same group, cash<->credit, loan<->tracking; crossing the budget boundary is refused with guidance)","tags":["accounts"],"x-required-scope":["write:accounts"]}},"/households/{householdId}/accounts/{accountId}/close":{"post":{"operationId":"AccountsController_close","parameters":[{"name":"accountId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Close an account; any remaining balance is booked as a balance adjustment dated on the caller's local day","tags":["accounts"],"x-required-scope":["write:accounts"]}},"/households/{householdId}/accounts/{accountId}/reopen":{"post":{"operationId":"AccountsController_reopen","parameters":[{"name":"accountId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Reopen a previously closed account","tags":["accounts"],"x-required-scope":["write:accounts"]}},"/households/{householdId}/accounts/{accountId}/balance-history":{"get":{"operationId":"AccountsController_balanceHistory","parameters":[{"name":"accountId","required":true,"in":"path","schema":{"type":"string"}},{"name":"fromDate","required":false,"in":"query","description":"YYYY-MM-DD","schema":{"type":"string"}},{"name":"toDate","required":false,"in":"query","description":"YYYY-MM-DD","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Daily running balance for an account","tags":["accounts"],"x-required-scope":["read:accounts"]}},"/households/{householdId}/accounts/{accountId}/loan-projection":{"get":{"operationId":"AccountsController_loanProjection","parameters":[{"name":"accountId","required":true,"in":"path","schema":{"type":"string"}},{"name":"extraMonthlyCents","required":false,"in":"query","description":"Extra principal paid every month, integer cents","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Loan payoff planner: amortization with the current terms, optional extra monthly principal, and this month’s ledger split","tags":["accounts"],"x-required-scope":["read:accounts"]}},"/households/{householdId}/accounts/{accountId}/loan-simulation":{"get":{"operationId":"AccountsController_loanSimulation","parameters":[{"name":"accountId","required":true,"in":"path","schema":{"type":"string"}},{"name":"targetPayoffYearMonth","required":false,"in":"query","description":"Solve the minimal payment that pays off by this YYYY-MM","schema":{}},{"name":"oneTimeExtraCents","required":false,"in":"query","description":"One-off extra principal this month, integer cents","schema":{}},{"name":"extraMonthlyCents","required":false,"in":"query","description":"Extra principal every month, integer cents","schema":{}},{"name":"paymentCents","required":false,"in":"query","description":"Alternative full monthly payment, integer cents","schema":{}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Loan payoff simulator: what-if projection for an alternative payment, recurring/one-off extras, or a target payoff month (solved to the required payment)","tags":["accounts"],"x-required-scope":["read:accounts"]}},"/households/{householdId}/accounts/{accountId}/loan-terms-history":{"get":{"operationId":"AccountsController_loanTermsHistory","parameters":[{"name":"accountId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Dated changes of a loan’s rate, payment and escrow (newest first)","tags":["accounts"],"x-required-scope":["read:accounts"]}},"/households/{householdId}/ai/chat":{"post":{"operationId":"AiController_chatStream","parameters":[],"responses":{"201":{"description":""}},"security":[{"bearer":[]}],"summary":"Stream a chat turn (SSE). Returns text/event-stream. 403 AI_CONSENT_REQUIRED until the ai_processing consent is granted.","tags":["ai"],"x-required-scope":["ai:chat"]}},"/households/{householdId}/ai/conversations/{id}/stop":{"post":{"operationId":"AiController_stop","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Stop further turns on this conversation.","tags":["ai"],"x-required-scope":["ai:chat"]}},"/households/{householdId}/ai/conversations":{"get":{"operationId":"AiController_listConversations","parameters":[{"name":"limit","required":false,"in":"query","schema":{"type":"string"}},{"name":"cursor","required":false,"in":"query","schema":{"type":"string"}},{"name":"q","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"List conversations in the household visible to the user. Ordered by updatedAt (most recent first); cursor-paginated; optional q title search.","tags":["ai"],"x-required-scope":["ai:chat"]}},"/households/{householdId}/ai/conversations/{id}":{"get":{"operationId":"AiController_getConversation","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Fetch a single conversation (creator + household owners only).","tags":["ai"],"x-required-scope":["ai:chat"]},"patch":{"operationId":"AiController_rename","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"tags":["ai"],"x-required-scope":["ai:chat"]},"delete":{"operationId":"AiController_deleteConversation","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":""}},"security":[{"bearer":[]}],"tags":["ai"],"x-required-scope":["ai:chat"]}},"/households/{householdId}/ai/conversations/bulk-delete":{"post":{"operationId":"AiController_bulkDeleteConversations","parameters":[],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Bulk-delete the current user's conversations (ids or all).","tags":["ai"],"x-required-scope":["ai:chat"]}},"/households/{householdId}/ai/actions":{"get":{"operationId":"AiController_listActions","parameters":[],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"List pending AI actions awaiting user confirmation. Paginated with opaque cursor.","tags":["ai"],"x-required-scope":["ai:chat"]}},"/households/{householdId}/ai/actions/{id}/confirm":{"post":{"operationId":"AiController_confirmAction","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Confirm and execute a pending assistant action.","tags":["ai"],"x-required-scope":["ai:chat"]}},"/households/{householdId}/ai/actions/{id}/cancel":{"post":{"operationId":"AiController_cancelAction","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Cancel a pending destructive action.","tags":["ai"],"x-required-scope":["ai:chat"]}},"/households/{householdId}/ai/usage":{"get":{"operationId":"AiController_usage","parameters":[],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Current user AI usage for today + daily cap + household monthly cap.","tags":["ai"],"x-required-scope":["ai:tools"]}},"/households/{householdId}/ai/tools":{"get":{"operationId":"AiController_tools","parameters":[],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Tools available to the current member's role (count + names).","tags":["ai"],"x-required-scope":["ai:tools"]}},"/households/{householdId}/ai/insights":{"get":{"operationId":"InsightsController_list","parameters":[{"name":"includeDismissed","required":false,"in":"query","schema":{"type":"string"}},{"name":"kind","required":false,"in":"query","schema":{"type":"string"}},{"name":"periodStart","required":false,"in":"query","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"type":"string"}},{"name":"cursor","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"List insights in this household. By default returns undismissed only; pass includeDismissed=true to see the full archive.","tags":["ai-insights"],"x-required-scope":["read:insights"]}},"/households/{householdId}/ai/insights/{insightId}/dismiss":{"post":{"operationId":"InsightsController_dismiss","parameters":[{"name":"insightId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Dismiss an insight (idempotent)","tags":["ai-insights"],"x-required-scope":["write:insights"]}},"/households/{householdId}":{"get":{"operationId":"HouseholdsController_getOne","parameters":[],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Get a household by id (must be a member)","tags":["households"],"x-required-scope":["read:households"]},"patch":{"operationId":"HouseholdsController_update","parameters":[],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Update a household (owner only)","tags":["households"],"x-required-scope":["write:households"]}},"/users/me":{"get":{"operationId":"UsersController_getMe","parameters":[],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Current user profile","tags":["users"],"x-required-scope":["read:profile"]},"patch":{"operationId":"UsersController_updateMe","parameters":[],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Update current user profile","tags":["users"],"x-required-scope":["write:profile"]}},"/users/me/preferences":{"patch":{"operationId":"UsersController_updatePreferences","parameters":[],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Replace the preferences blob","tags":["users"],"x-required-scope":["write:profile"]}},"/households/{householdId}/members":{"get":{"operationId":"MembershipsController_list","parameters":[],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"List members of a household","tags":["memberships"],"x-required-scope":["read:households"]}},"/households/{householdId}/transactions":{"post":{"operationId":"TransactionsController_create","parameters":[],"responses":{"201":{"description":""}},"security":[{"bearer":[]}],"summary":"Create a transaction (with optional splits and tags)","tags":["transactions"],"x-required-scope":["write:transactions"]},"get":{"operationId":"TransactionsController_list","parameters":[{"name":"limit","required":false,"in":"query","description":"1..100, default 50","schema":{"example":50,"type":"number"}},{"name":"cursor","required":false,"in":"query","description":"Opaque cursor from the previous page (nextCursor)","schema":{"type":"string"}},{"name":"maxAmountCents","required":false,"in":"query","description":"Cents","schema":{"type":"string"}},{"name":"minAmountCents","required":false,"in":"query","description":"Cents","schema":{"type":"string"}},{"name":"search","required":false,"in":"query","schema":{"type":"string"}},{"name":"dateTo","required":false,"in":"query","description":"YYYY-MM-DD","schema":{"type":"string"}},{"name":"dateFrom","required":false,"in":"query","description":"YYYY-MM-DD","schema":{"type":"string"}},{"name":"source","required":false,"in":"query","schema":{"enum":["manual","imported","ai","recurring","split"],"type":"string"}},{"name":"statuses","required":false,"in":"query","description":"Comma-separated statuses (any-of); supersedes `status` when both are sent","schema":{"type":"string"}},{"name":"status","required":false,"in":"query","schema":{"enum":["pending","cleared","reconciled"],"type":"string"}},{"name":"attention","required":false,"in":"query","description":"Rows waiting on the user: needs-review, plus actionable uncategorized outflows that are not reconciled (the account attention count’s predicate)","schema":{"type":"boolean"}},{"name":"uncategorized","required":false,"in":"query","description":"Only rows in the budget’s uncategorized bucket: bare outflows on on-budget accounts (cleared or reconciled), never transfer legs or system rows","schema":{"type":"boolean"}},{"name":"needsReview","required":false,"in":"query","description":"true = rows awaiting a category decision, false = the rest","schema":{"type":"boolean"}},{"name":"hasFlag","required":false,"in":"query","description":"Only transactions that carry a flag","schema":{"type":"boolean"}},{"name":"flagIds","required":false,"in":"query","description":"Comma-separated flag ULIDs (any-of), max 20","schema":{"type":"string"}},{"name":"hasTag","required":false,"in":"query","schema":{"type":"boolean"}},{"name":"tagMode","required":false,"in":"query","schema":{"enum":["any","all"],"type":"string"}},{"name":"tagIds","required":false,"in":"query","description":"Comma-separated tag ULIDs, max 20","schema":{"type":"string"}},{"name":"tagId","required":false,"in":"query","schema":{"type":"string"}},{"name":"payeeId","required":false,"in":"query","schema":{"type":"string"}},{"name":"categoryId","required":false,"in":"query","schema":{"type":"string"}},{"name":"accountId","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"List transactions, newest date first (date DESC, id DESC), with cursor pagination and filters","tags":["transactions"],"x-required-scope":["read:transactions"]}},"/households/{householdId}/transactions/transfer":{"post":{"operationId":"TransactionsController_createTransfer","parameters":[],"responses":{"201":{"description":""}},"security":[{"bearer":[]}],"summary":"Create a double-entry transfer between two accounts (both legs share a transfer group)","tags":["transactions"],"x-required-scope":["write:transactions"]}},"/households/{householdId}/transactions/bulk":{"post":{"operationId":"TransactionsController_bulkCreate","parameters":[],"responses":{"201":{"description":""}},"security":[{"bearer":[]}],"summary":"Bulk-create transactions (up to 500 in one shot)","tags":["transactions"],"x-required-scope":["write:transactions"]}},"/households/{householdId}/transactions/bulk-update":{"patch":{"operationId":"TransactionsController_bulkUpdate","parameters":[],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Bulk-update a subset of transactions with the same patch","tags":["transactions"],"x-required-scope":["write:transactions"]}},"/households/{householdId}/transactions/daily-summary":{"get":{"operationId":"TransactionsController_dailySummaryFor","parameters":[],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Per-day money in, money out and count for a window of up to 92 days (the calendar): converted to the base currency, plus the face-value totals per currency","tags":["transactions"],"x-required-scope":["read:transactions"]}},"/households/{householdId}/transactions/stale-pending-count":{"get":{"operationId":"TransactionsController_stalePendingCount","parameters":[],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"How many hand-entered rows are still pending at least N days after their date on bank-fed accounts (the stale pending sweep, ADR 0105)","tags":["transactions"],"x-required-scope":["read:transactions"]}},"/households/{householdId}/transactions/{transactionId}":{"get":{"operationId":"TransactionsController_getOne","parameters":[{"name":"transactionId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Get one transaction with its splits","tags":["transactions"],"x-required-scope":["read:transactions"]},"patch":{"operationId":"TransactionsController_update","parameters":[{"name":"transactionId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Update a transaction (replaces splits and tagIds when provided)","tags":["transactions"],"x-required-scope":["write:transactions"]},"delete":{"operationId":"TransactionsController_remove","parameters":[{"name":"transactionId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":""}},"security":[{"bearer":[]}],"summary":"Soft-delete a transaction","tags":["transactions"],"x-required-scope":["write:transactions"]}},"/households/{householdId}/transactions/{transactionId}/tags/{tagId}":{"delete":{"operationId":"TransactionsController_removeTag","parameters":[{"name":"transactionId","required":true,"in":"path","schema":{"type":"string"}},{"name":"tagId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":""}},"security":[{"bearer":[]}],"summary":"Remove a single tag from a transaction","tags":["transactions"],"x-required-scope":["write:transactions"]}},"/households/{householdId}/transactions/{transactionId}/accept-suggestion":{"post":{"operationId":"TransactionsController_acceptSuggestion","parameters":[{"name":"transactionId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Accept the AI-suggested category and clear the suggestion fields. The accepted row becomes ground truth for future kNN predictions.","tags":["transactions"],"x-required-scope":["write:transactions"]}},"/households/{householdId}/transactions/{transactionId}/reject-suggestion":{"post":{"operationId":"TransactionsController_rejectSuggestion","parameters":[{"name":"transactionId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Reject the AI-suggested category. The row stays uncategorized.","tags":["transactions"],"x-required-scope":["write:transactions"]}},"/households/{householdId}/transactions/{transactionId}/transfer-candidates":{"get":{"operationId":"TransactionsController_transferCandidates","parameters":[{"name":"transactionId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Counterpart rows this transaction could be linked to as one transfer: exact opposite amount, same currency, another account, within 7 days (ADR 0099)","tags":["transactions"],"x-required-scope":["read:transactions"]}},"/households/{householdId}/transactions/{transactionId}/link-transfer":{"post":{"operationId":"TransactionsController_linkTransfer","parameters":[{"name":"transactionId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Link this transaction and a counterpart as the two legs of one transfer. The budget engine then recognises card payments through the shared group (ADR 0099).","tags":["transactions"],"x-required-scope":["write:transactions"]}},"/households/{householdId}/transactions/{transactionId}/unlink-transfer":{"post":{"operationId":"TransactionsController_unlinkTransfer","parameters":[{"name":"transactionId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Unlink a two-leg transfer: both legs keep payee and category but drop the shared group. Forbidden while a leg is reconciled.","tags":["transactions"],"x-required-scope":["write:transactions"]}},"/households/{householdId}/transactions/{transactionId}/match-candidates":{"get":{"operationId":"TransactionsController_matchCandidates","parameters":[{"name":"transactionId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Rows this transaction could be merged with as the same purchase: a bank import lists hand-entered twins, a hand-entered row lists bank twins (same account, amount and currency, within 7 days; ADR 0105)","tags":["transactions"],"x-required-scope":["read:transactions"]}},"/households/{householdId}/transactions/{transactionId}/match":{"post":{"operationId":"TransactionsController_matchTransaction","parameters":[{"name":"transactionId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Merge a bank import with the hand-entered row it duplicates: the hand-entered row survives (payee, category, memo, tags, splits) and takes the bank date; the bank row is folded into it. Undoable (ADR 0105).","tags":["transactions"],"x-required-scope":["write:transactions"]}},"/households/{householdId}/transactions/{transactionId}/unmatch":{"post":{"operationId":"TransactionsController_unmatchTransaction","parameters":[{"name":"transactionId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Separate a merged pair again: the bank import comes back as its own row and the hand-entered row gets its previous date and status. Forbidden while reconciled.","tags":["transactions"],"x-required-scope":["write:transactions"]}},"/households/{householdId}/transactions/{transactionId}/dismiss-match":{"post":{"operationId":"TransactionsController_dismissMatch","parameters":[{"name":"transactionId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"\"Not a duplicate\": clear the possible-duplicate flag an ambiguous auto-match left on a bank import.","tags":["transactions"],"x-required-scope":["write:transactions"]}},"/households/{householdId}/rules":{"post":{"operationId":"RulesController_create","parameters":[],"responses":{"201":{"description":""}},"security":[{"bearer":[]}],"summary":"Create a classification rule","tags":["rules"],"x-required-scope":["write:rules"]},"get":{"operationId":"RulesController_list","parameters":[],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"List rules (ordered by priority then created_at)","tags":["rules"],"x-required-scope":["read:rules"]}},"/households/{householdId}/rules/{id}":{"get":{"operationId":"RulesController_getById","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Fetch a single rule","tags":["rules"],"x-required-scope":["read:rules"]},"patch":{"operationId":"RulesController_update","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Update a rule (priority, enabled, predicate, action)","tags":["rules"],"x-required-scope":["write:rules"]},"delete":{"operationId":"RulesController_softDelete","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":""}},"security":[{"bearer":[]}],"summary":"Soft-delete a rule","tags":["rules"],"x-required-scope":["write:rules"]}},"/households/{householdId}/rules/{id}/apply":{"post":{"operationId":"RulesController_apply","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Apply this rule to existing transactions (manual back-fill; trigger=manual)","tags":["rules"],"x-required-scope":["write:rules"]}},"/households/{householdId}/rules/{id}/preview":{"post":{"operationId":"RulesController_preview","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Preview which existing transactions this rule matches (dry-run, applies nothing)","tags":["rules"],"x-required-scope":["read:rules"]}},"/households/{householdId}/rules/apply-all":{"post":{"operationId":"RulesController_applyAll","parameters":[],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Apply every enabled rule (priority order, first-match-wins) to matching transactions","tags":["rules"],"x-required-scope":["write:rules"]}},"/households/{householdId}/categories":{"post":{"operationId":"CategoriesController_create","parameters":[],"responses":{"201":{"description":""}},"security":[{"bearer":[]}],"summary":"Create a category in a group","tags":["categories"],"x-required-scope":["write:categories"]},"get":{"operationId":"CategoriesController_list","parameters":[],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"List categories in this household","tags":["categories"],"x-required-scope":["read:categories"]}},"/households/{householdId}/categories/{categoryId}":{"patch":{"operationId":"CategoriesController_update","parameters":[{"name":"categoryId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Update a category","tags":["categories"],"x-required-scope":["write:categories"]},"delete":{"operationId":"CategoriesController_remove","parameters":[{"name":"categoryId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":""}},"security":[{"bearer":[]}],"summary":"Soft-delete a category","tags":["categories"],"x-required-scope":["write:categories"]}},"/households/{householdId}/categories/reorder":{"post":{"operationId":"CategoriesController_reorder","parameters":[],"responses":{"204":{"description":""}},"security":[{"bearer":[]}],"summary":"Atomically reorder categories across groups","tags":["categories"],"x-required-scope":["write:categories"]}},"/households/{householdId}/category-groups":{"post":{"operationId":"CategoryGroupsController_create","parameters":[],"responses":{"201":{"description":""}},"security":[{"bearer":[]}],"summary":"Create a category group","tags":["category-groups"],"x-required-scope":["write:categories"]},"get":{"operationId":"CategoryGroupsController_list","parameters":[],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"List category groups in this household","tags":["category-groups"],"x-required-scope":["read:categories"]}},"/households/{householdId}/category-groups/{groupId}":{"patch":{"operationId":"CategoryGroupsController_update","parameters":[{"name":"groupId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Update a category group","tags":["category-groups"],"x-required-scope":["write:categories"]},"delete":{"operationId":"CategoryGroupsController_remove","parameters":[{"name":"groupId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":""}},"security":[{"bearer":[]}],"summary":"Soft-delete a category group","tags":["category-groups"],"x-required-scope":["write:categories"]}},"/households/{householdId}/starter-pack":{"post":{"operationId":"StarterPacksController_apply","parameters":[],"responses":{"201":{"description":""}},"security":[{"bearer":[]}],"summary":"Seed a starter category pack into this household. Idempotent by name: existing groups are reused and existing categories skipped, so re-running never duplicates the structure. Names are created in the household data language.","tags":["categories"],"x-required-scope":["write:categories"]}},"/households/{householdId}/payees":{"post":{"operationId":"PayeesController_create","parameters":[],"responses":{"201":{"description":""}},"security":[{"bearer":[]}],"summary":"Create a payee","tags":["payees"],"x-required-scope":["write:payees"]},"get":{"operationId":"PayeesController_list","parameters":[],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"List payees in this household","tags":["payees"],"x-required-scope":["read:payees"]}},"/households/{householdId}/payees/page":{"get":{"operationId":"PayeesController_listPage","parameters":[{"name":"sort","required":false,"in":"query","schema":{"enum":["nameAsc","nameDesc"],"type":"string"}},{"name":"categoryId","required":false,"in":"query","schema":{"type":"string"}},{"name":"segment","required":false,"in":"query","schema":{"enum":["all","noCategory"],"type":"string"}},{"name":"q","required":false,"in":"query","schema":{"type":"string"}},{"name":"pageSize","required":false,"in":"query","schema":{"enum":[10,25,50,100],"type":"number"}},{"name":"page","required":false,"in":"query","schema":{"example":1,"type":"number"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Search, filter, sort and paginate the payee register","tags":["payees"],"x-required-scope":["read:payees"]}},"/households/{householdId}/payees/duplicate-groups":{"get":{"operationId":"PayeesController_duplicateGroups","parameters":[],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Household-wide near-duplicate payee clusters (vector cosine >= 0.9), for the duplicates review tray.","tags":["payees"],"x-required-scope":["read:payees"]}},"/households/{householdId}/payees/{payeeId}":{"get":{"operationId":"PayeesController_getOne","parameters":[{"name":"payeeId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Get a payee","tags":["payees"],"x-required-scope":["read:payees"]},"patch":{"operationId":"PayeesController_update","parameters":[{"name":"payeeId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Update a payee (recomputes normalized_name on rename)","tags":["payees"],"x-required-scope":["write:payees"]},"delete":{"operationId":"PayeesController_remove","parameters":[{"name":"payeeId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":""}},"security":[{"bearer":[]}],"summary":"Soft-delete a payee","tags":["payees"],"x-required-scope":["write:payees"]}},"/households/{householdId}/payees/{payeeId}/aliases":{"get":{"operationId":"PayeesController_listAliases","parameters":[{"name":"payeeId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"List aliases of a payee","tags":["payees"],"x-required-scope":["read:payees"]},"post":{"operationId":"PayeesController_createAlias","parameters":[{"name":"payeeId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"security":[{"bearer":[]}],"summary":"Add an alias to a payee (dedup by normalized form)","tags":["payees"],"x-required-scope":["write:payees"]}},"/households/{householdId}/payees/{payeeId}/aliases/{aliasId}":{"delete":{"operationId":"PayeesController_removeAlias","parameters":[{"name":"payeeId","required":true,"in":"path","schema":{"type":"string"}},{"name":"aliasId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":""}},"security":[{"bearer":[]}],"summary":"Remove an alias","tags":["payees"],"x-required-scope":["write:payees"]}},"/households/{householdId}/payees/{payeeId}/merge":{"post":{"operationId":"PayeesController_merge","parameters":[{"name":"payeeId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Merge this payee into another: reassign transactions, dedupe aliases, soft-delete source","tags":["payees"],"x-required-scope":["write:payees"]}},"/households/{householdId}/payees/{payeeId}/unmerge":{"post":{"operationId":"PayeesController_unmerge","parameters":[{"name":"payeeId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Undo a merge: resurrect this (soft-deleted) payee and move the listed transactions back from the target","tags":["payees"],"x-required-scope":["write:payees"]}},"/households/{householdId}/payees/{payeeId}/dedupe-candidates":{"get":{"operationId":"PayeesController_dedupeCandidates","parameters":[{"name":"payeeId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"List payees in this household whose vector embedding is near-duplicate of :payeeId (cosine similarity >= 0.9).","tags":["payees"],"x-required-scope":["read:payees"]}},"/households/{householdId}/tags":{"post":{"operationId":"TagsController_create","parameters":[],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Tag"}}}}},"security":[{"bearer":[]}],"summary":"Create a tag","tags":["tags"],"x-required-scope":["write:tags"]},"get":{"operationId":"TagsController_list","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Tag"}}}}}},"security":[{"bearer":[]}],"summary":"List tags in this household","tags":["tags"],"x-required-scope":["read:tags"]}},"/households/{householdId}/tags/{tagId}":{"patch":{"operationId":"TagsController_update","parameters":[{"name":"tagId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Tag"}}}}},"security":[{"bearer":[]}],"summary":"Update a tag","tags":["tags"],"x-required-scope":["write:tags"]},"delete":{"operationId":"TagsController_remove","parameters":[{"name":"tagId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":""}},"security":[{"bearer":[]}],"summary":"Delete a tag (cascades to transaction_tags junction)","tags":["tags"],"x-required-scope":["write:tags"]}},"/households/{householdId}/forecasts/{yearMonth}":{"get":{"operationId":"ForecastingController_forecast","parameters":[{"name":"yearMonth","required":true,"in":"path","schema":{"type":"string"}},{"name":"refine","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Per-category projected spend for :yearMonth. ?refine=true engages the LLM adjustment pass.","tags":["forecasts"],"x-required-scope":["read:forecasting"]}},"/households/{householdId}/budgets/age-of-money":{"get":{"operationId":"BudgetsController_ageOfMoney","parameters":[],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Age of Money: mean days the money you spend sat in on-budget cash accounts (last 10 outflows, FIFO); null until 10 outflows exist","tags":["budgets"],"x-required-scope":["read:budgets"]}},"/households/{householdId}/budgets/{yearMonth}":{"get":{"operationId":"BudgetsController_getMonth","parameters":[{"name":"yearMonth","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Budget snapshot for the given month (YYYY-MM) including ABT and per-category state","tags":["budgets"],"x-required-scope":["read:budgets"]}},"/households/{householdId}/budgets/{yearMonth}/categories/{categoryId}":{"get":{"operationId":"BudgetsController_getCategory","parameters":[{"name":"yearMonth","required":true,"in":"path","schema":{"type":"string"}},{"name":"categoryId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Snapshot for a single category in the given month","tags":["budgets"],"x-required-scope":["read:budgets"]},"patch":{"operationId":"BudgetsController_assignCategory","parameters":[{"name":"yearMonth","required":true,"in":"path","schema":{"type":"string"}},{"name":"categoryId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Assign or update the amount a category receives this month","tags":["budgets"],"x-required-scope":["write:budgets"]}},"/households/{householdId}/budgets/{yearMonth}/categories/{categoryId}/history":{"get":{"operationId":"BudgetsController_getCategoryHistory","parameters":[{"name":"yearMonth","required":true,"in":"path","schema":{"type":"string"}},{"name":"categoryId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Assignment history for one category in the given month: every assign and every money move in or out, newest first","tags":["budgets"],"x-required-scope":["read:budgets"]}},"/households/{householdId}/budgets/{yearMonth}/auto-assign-from-goals":{"post":{"operationId":"BudgetsController_autoAssignFromGoals","parameters":[{"name":"yearMonth","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Raise every category with a goal to its suggested monthly amount, capped by ABT","tags":["budgets"],"x-required-scope":["write:budgets"]}},"/households/{householdId}/budgets/{yearMonth}/move":{"post":{"operationId":"BudgetsController_move","parameters":[{"name":"yearMonth","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Move money from one category to another within a single month (atomic, conservative)","tags":["budgets"],"x-required-scope":["write:budgets"]}},"/households/{householdId}/budgets/{yearMonth}/copy-from/{sourceYearMonth}":{"post":{"operationId":"BudgetsController_copyFrom","parameters":[{"name":"yearMonth","required":true,"in":"path","schema":{"type":"string"}},{"name":"sourceYearMonth","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Copy every assignment from sourceYearMonth into yearMonth (overwrites destination)","tags":["budgets"],"x-required-scope":["write:budgets"]}},"/households/{householdId}/budgets/{yearMonth}/reset":{"post":{"operationId":"BudgetsController_reset","parameters":[{"name":"yearMonth","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Clear every assignment in this month (owner only)","tags":["budgets"],"x-required-scope":["write:budgets"]}},"/households/{householdId}/recurrences":{"post":{"operationId":"RecurrencesController_create","parameters":[],"responses":{"201":{"description":""}},"security":[{"bearer":[]}],"summary":"Create a recurring transaction schedule","tags":["recurrences"],"x-required-scope":["write:recurrences"]},"get":{"operationId":"RecurrencesController_list","parameters":[],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"List recurrences in the household","tags":["recurrences"],"x-required-scope":["read:recurrences"]}},"/households/{householdId}/recurrences/upcoming":{"get":{"operationId":"RecurrencesController_listUpcoming","parameters":[],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"What is coming in a window: recurrence occurrences, pending recurring rows, future one-off rows and card payment due days","tags":["recurrences"],"x-required-scope":["read:recurrences"]}},"/households/{householdId}/recurrences/suggestions":{"get":{"operationId":"RecurrencesController_listSuggestions","parameters":[],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Recurring charges detected in the household activity that are not scheduled yet, plus price changes on tracked ones (ADR 0109)","tags":["recurrences"],"x-required-scope":["read:recurrences"]}},"/households/{householdId}/recurrences/suggestions/{key}/accept":{"post":{"operationId":"RecurrencesController_acceptSuggestion","parameters":[{"name":"key","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Accept a suggestion: create the recurrence (origin detected) or apply the price change","tags":["recurrences"],"x-required-scope":["write:recurrences"]}},"/households/{householdId}/recurrences/suggestions/{key}/dismiss":{"post":{"operationId":"RecurrencesController_dismissSuggestion","parameters":[{"name":"key","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":""}},"security":[{"bearer":[]}],"summary":"Dismiss a suggestion (\"not recurring\"); it stays hidden across recomputations","tags":["recurrences"],"x-required-scope":["write:recurrences"]},"delete":{"operationId":"RecurrencesController_undismissSuggestion","parameters":[{"name":"key","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":""}},"security":[{"bearer":[]}],"summary":"Undo a dismissal","tags":["recurrences"],"x-required-scope":["write:recurrences"]}},"/households/{householdId}/recurrences/{id}":{"get":{"operationId":"RecurrencesController_getById","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Fetch a single recurrence","tags":["recurrences"],"x-required-scope":["read:recurrences"]},"patch":{"operationId":"RecurrencesController_update","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Update a recurrence (recomputes nextRunDate if schedule changes)","tags":["recurrences"],"x-required-scope":["write:recurrences"]},"delete":{"operationId":"RecurrencesController_softDelete","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":""}},"security":[{"bearer":[]}],"summary":"Soft-delete a recurrence","tags":["recurrences"],"x-required-scope":["write:recurrences"]}},"/households/{householdId}/recurrences/{id}/pause":{"post":{"operationId":"RecurrencesController_pause","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Pause a recurrence (status → paused, no further runs)","tags":["recurrences"],"x-required-scope":["write:recurrences"]}},"/households/{householdId}/recurrences/{id}/resume":{"post":{"operationId":"RecurrencesController_resume","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Resume a paused recurrence","tags":["recurrences"],"x-required-scope":["write:recurrences"]}},"/households/{householdId}/recurrences/{id}/materialize-now":{"post":{"operationId":"RecurrencesController_materializeNow","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Force immediate materialization of the next due run (idempotent on external_id)","tags":["recurrences"],"x-required-scope":["write:recurrences"]}},"/households/{householdId}/reports/net-worth":{"get":{"operationId":"ReportsController_netWorth","parameters":[],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Per-month-end net worth in base currency. Optional accountIds CSV (only these accounts) and excludeAccountIds CSV filters. Max 5-year range.","tags":["reports"],"x-required-scope":["read:reports"]}},"/households/{householdId}/reports/cash-flow":{"get":{"operationId":"ReportsController_cashFlow","parameters":[],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Per-month income vs expense vs net (in base currency) over the range. Optional accountIds CSV (only these accounts). Max 5-year range.","tags":["reports"],"x-required-scope":["read:reports"]}},"/households/{householdId}/reports/spending-by-category":{"get":{"operationId":"ReportsController_spendingByCategory","parameters":[],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Spend per category over the range, sorted most-negative first. Optional accountIds CSV (only these accounts), accountId + categoryIds CSV filters. Income rows excluded; income lives in /cash-flow.","tags":["reports"],"x-required-scope":["read:reports"]}},"/households/{householdId}/reports/spending-by-payee":{"get":{"operationId":"ReportsController_spendingByPayee","parameters":[],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Top payees by spend over the range. Default limit=50, sort=totalDesc; optional accountIds CSV (only these accounts) and accountId filters. Free-text transactions (no payeeId) are excluded.","tags":["reports"],"x-required-scope":["read:reports"]}},"/households/{householdId}/reports/subscriptions":{"get":{"operationId":"ReportsController_subscriptions","parameters":[],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Current subscriptions rollup: outflow recurrences normalised to a per-month cost in base currency. status=active (default) | paused | all; optional accountIds CSV (only these accounts).","tags":["reports"],"x-required-scope":["read:reports"]}},"/households/{householdId}/reports/income-by-source":{"get":{"operationId":"ReportsController_incomeBySource","parameters":[],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Income by source over the range (ADR 0107): unlabelled inflows plus every income category, month by month in the base currency. Optional accountIds CSV. Max 5-year range.","tags":["reports"],"x-required-scope":["read:reports"]}},"/households/{householdId}/reports/savings-rate":{"get":{"operationId":"ReportsController_savingsRate","parameters":[],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Per-month savings rate (net/income) over the range, plus the overall rate. Months with no income report null. Optional accountIds CSV (only these accounts). Max 5-year range.","tags":["reports"],"x-required-scope":["read:reports"]}},"/households/{householdId}/reports/budget-adherence":{"get":{"operationId":"ReportsController_budgetAdherence","parameters":[],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Plan vs reality: per-month assigned vs spent totals, adherence share of budgeted categories, and the biggest overruns of the range. Max 5-year range.","tags":["reports"],"x-required-scope":["read:reports"]}},"/households/{householdId}/reports/category-trends":{"get":{"operationId":"ReportsController_categoryTrends","parameters":[],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Month-over-month spend per category (dense, zero-filled series in base currency). Optional accountIds CSV (only these accounts) and categoryIds CSV filters. Max 5-year range.","tags":["reports"],"x-required-scope":["read:reports"]}},"/exchange-rates":{"get":{"operationId":"ExchangeRatesController_getRate","parameters":[{"name":"date","required":false,"in":"query","description":"YYYY-MM-DD; defaults to today","schema":{}},{"name":"to","required":true,"in":"query","schema":{}},{"name":"from","required":true,"in":"query","schema":{}}],"responses":{"200":{"description":""}},"summary":"Resolve an exchange rate (direct, inverse, or via USD)","tags":["exchange-rates"],"x-required-scope":["read:exchange-rates"]}},"/exchange-rates/history":{"get":{"operationId":"ExchangeRatesController_getHistory","parameters":[{"name":"toDate","required":true,"in":"query","description":"YYYY-MM-DD (inclusive)","schema":{}},{"name":"fromDate","required":true,"in":"query","description":"YYYY-MM-DD (inclusive)","schema":{}},{"name":"to","required":true,"in":"query","schema":{}},{"name":"from","required":true,"in":"query","schema":{}}],"responses":{"200":{"description":""}},"summary":"List daily rates between two dates (inclusive)","tags":["exchange-rates"],"x-required-scope":["read:exchange-rates"]}},"/households/{householdId}/goals":{"get":{"operationId":"GoalsController_list","parameters":[],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"List every goal in the household","tags":["goals"],"x-required-scope":["read:goals"]}},"/households/{householdId}/categories/{categoryId}/goal":{"get":{"operationId":"GoalsController_get","parameters":[{"name":"categoryId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Get the goal attached to a category","tags":["goals"],"x-required-scope":["read:goals"]},"put":{"operationId":"GoalsController_upsert","parameters":[{"name":"categoryId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Create or replace the goal on a category (one goal per category)","tags":["goals"],"x-required-scope":["write:goals"]},"delete":{"operationId":"GoalsController_remove","parameters":[{"name":"categoryId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":""}},"security":[{"bearer":[]}],"summary":"Remove the goal from a category","tags":["goals"],"x-required-scope":["write:goals"]}},"/households/{householdId}/history":{"get":{"operationId":"HistoryController_list","parameters":[],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Change history, newest first: every recorded change with its performer; filter by member, family, kind, time window, group or explicit ids","tags":["history"],"x-required-scope":["read:history"]}},"/households/{householdId}/history/{eventId}/undo":{"post":{"operationId":"HistoryController_undo","parameters":[{"name":"eventId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Undo a recorded change: applies its exact inverse, writes the reversal to the history and marks the change undone (409 HISTORY_STALE when the data moved on, HISTORY_ALREADY_UNDONE when another client got there first)","tags":["history"],"x-required-scope":["write:history"]}},"/households/{householdId}/history/{eventId}/redo":{"post":{"operationId":"HistoryController_redo","parameters":[{"name":"eventId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Redo an undone change: reverts its active reversal so the change is in force again (409 HISTORY_NOT_UNDONE when it is not undone, HISTORY_STALE when the data moved on)","tags":["history"],"x-required-scope":["write:history"]}},"/households/{householdId}/receipts":{"post":{"operationId":"ReceiptsController_upload","parameters":[],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["file"],"properties":{"file":{"type":"string","format":"binary"},"transactionId":{"type":"string","description":"Attach to this existing transaction right away (ADR 0108)."},"priority":{"type":"string","enum":["interactive","background"],"description":"Extraction lane (ADR 0111): interactive (default) for a scan you are waiting on, background for a batch."}}}}}},"responses":{"201":{"description":""}},"security":[{"bearer":[]}],"summary":"Upload a receipt image (multipart/form-data); optionally attach it to a transaction","tags":["receipts"],"x-required-scope":["write:receipts"]},"get":{"operationId":"ReceiptsController_list","parameters":[{"name":"cursor","required":false,"in":"query","description":"nextCursor of the previous page; only valid for the same sort and dateBasis","schema":{}},{"name":"limit","required":false,"in":"query","schema":{}},{"name":"sort","required":false,"in":"query","description":"date_desc (default) | date_asc | amount_desc | amount_asc | merchant_asc; dates follow dateBasis, ties break on id, receipts without a total or merchant come last","schema":{}},{"name":"inboxOnly","required":false,"in":"query","description":"true = not yet linked to a transaction","schema":{}},{"name":"linked","required":false,"in":"query","description":"true = linked to a transaction, false = still in the tray","schema":{}},{"name":"transactionId","required":false,"in":"query","schema":{}},{"name":"status","required":false,"in":"query","description":"Comma-separated: pending, processing, extracted, failed","schema":{}},{"name":"kind","required":false,"in":"query","description":"receipt | invoice | product | other","schema":{}},{"name":"q","required":false,"in":"query","description":"Free text over what was read (merchant, items, tags, summary); terms are ANDed","schema":{}},{"name":"dateBasis","required":false,"in":"query","description":"Which day the date filters and sorts use: receipt (default; the day printed on it, the upload day until it is read) or upload","schema":{}},{"name":"dateFrom","required":false,"in":"query","description":"Inclusive (YYYY-MM-DD) on the chosen dateBasis","schema":{}},{"name":"dateTo","required":false,"in":"query","description":"Inclusive (YYYY-MM-DD) on the chosen dateBasis","schema":{}},{"name":"amountMin","required":false,"in":"query","description":"Inclusive lower bound on the read total, integer cents","schema":{"type":"string"}},{"name":"amountMax","required":false,"in":"query","description":"Inclusive upper bound on the read total, integer cents","schema":{"type":"string"}},{"name":"merchant","required":false,"in":"query","description":"Comma-separated merchant keys (from the facets); any of them","schema":{}},{"name":"tag","required":false,"in":"query","description":"Comma-separated tags; all of them","schema":{}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"List receipts in this household; filter by link state, extraction status, kind, day, amount, merchant, tags or free text; sort by date, amount or merchant.","tags":["receipts"],"x-required-scope":["read:receipts"]}},"/households/{householdId}/receipts/facets":{"get":{"operationId":"ReceiptsController_facets","parameters":[{"name":"inboxOnly","required":false,"in":"query","description":"true = not yet linked to a transaction","schema":{}},{"name":"linked","required":false,"in":"query","description":"true = linked to a transaction, false = still in the tray","schema":{}},{"name":"transactionId","required":false,"in":"query","schema":{}},{"name":"status","required":false,"in":"query","description":"Comma-separated: pending, processing, extracted, failed","schema":{}},{"name":"kind","required":false,"in":"query","description":"receipt | invoice | product | other","schema":{}},{"name":"q","required":false,"in":"query","description":"Free text over what was read (merchant, items, tags, summary); terms are ANDed","schema":{}},{"name":"dateBasis","required":false,"in":"query","description":"Which day the date filters and sorts use: receipt (default; the day printed on it, the upload day until it is read) or upload","schema":{}},{"name":"dateFrom","required":false,"in":"query","description":"Inclusive (YYYY-MM-DD) on the chosen dateBasis","schema":{}},{"name":"dateTo","required":false,"in":"query","description":"Inclusive (YYYY-MM-DD) on the chosen dateBasis","schema":{}},{"name":"amountMin","required":false,"in":"query","description":"Inclusive lower bound on the read total, integer cents","schema":{"type":"string"}},{"name":"amountMax","required":false,"in":"query","description":"Inclusive upper bound on the read total, integer cents","schema":{"type":"string"}},{"name":"merchant","required":false,"in":"query","description":"Comma-separated merchant keys (from the facets); any of them","schema":{}},{"name":"tag","required":false,"in":"query","description":"Comma-separated tags; all of them","schema":{}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Facets of the receipts matching the same filters as the list: total, per-month counts and sums (on dateBasis), top merchants, top tags, status and link split.","tags":["receipts"],"x-required-scope":["read:receipts"]}},"/households/{householdId}/receipts/counts":{"get":{"operationId":"ReceiptsController_counts","parameters":[],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"How many receipts are unlinked, still being read, or failed (for the receipts page header).","tags":["receipts"],"x-required-scope":["read:receipts"]}},"/households/{householdId}/receipts/empty-tray":{"post":{"operationId":"ReceiptsController_emptyTray","parameters":[],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Empty the tray: soft-delete every receipt not linked to a transaction. Returns how many were deleted.","tags":["receipts"],"x-required-scope":["write:receipts"]}},"/households/{householdId}/receipts/{receiptId}":{"get":{"operationId":"ReceiptsController_getOne","parameters":[{"name":"receiptId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Get a receipt: extraction, normalised line items and pre-signed URLs (300s TTL) for the image.","tags":["receipts"],"x-required-scope":["read:receipts"]},"patch":{"operationId":"ReceiptsController_update","parameters":[{"name":"receiptId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Correct what was read (ADR 0111): merchant, date, total, currency, kind, tags, summary merge; items replace. A failed receipt becomes usable this way.","tags":["receipts"],"x-required-scope":["write:receipts"]},"delete":{"operationId":"ReceiptsController_remove","parameters":[{"name":"receiptId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":""}},"security":[{"bearer":[]}],"summary":"Soft-delete a receipt","tags":["receipts"],"x-required-scope":["write:receipts"]}},"/households/{householdId}/receipts/{receiptId}/file":{"get":{"operationId":"ReceiptsController_file","parameters":[{"name":"receiptId","required":true,"in":"path","schema":{"type":"string"}},{"name":"download","required":false,"in":"query","description":"Set to 1 to receive an attachment instead of an inline image.","schema":{}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"The receipt image, served inline (or as a download with ?download=1).","tags":["receipts"],"x-required-scope":["read:receipts"]}},"/households/{householdId}/receipts/{receiptId}/confirm":{"post":{"operationId":"ReceiptsController_confirm","parameters":[{"name":"receiptId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Materialise the receipt into a Transaction. Body fields override the extraction.","tags":["receipts"],"x-required-scope":["write:receipts"]}},"/households/{householdId}/receipts/{receiptId}/attach":{"post":{"operationId":"ReceiptsController_attach","parameters":[{"name":"receiptId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Attach an unlinked receipt to an existing transaction (ADR 0108)","tags":["receipts"],"x-required-scope":["write:receipts"]}},"/households/{householdId}/receipts/{receiptId}/detach":{"post":{"operationId":"ReceiptsController_detach","parameters":[{"name":"receiptId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Detach a receipt from its transaction; it returns to the tray (ADR 0108)","tags":["receipts"],"x-required-scope":["write:receipts"]}},"/households/{householdId}/receipts/{receiptId}/retry":{"post":{"operationId":"ReceiptsController_retry","parameters":[{"name":"receiptId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Read the receipt again (failed or extracted, attached or not). 409 while a read is in progress, or over a manual correction unless force=true. Returns the receipt with status=\"pending\".","tags":["receipts"],"x-required-scope":["write:receipts"]}},"/households/{householdId}/receipt-items":{"get":{"operationId":"ReceiptItemsController_list","parameters":[{"name":"cursor","required":false,"in":"query","schema":{}},{"name":"limit","required":false,"in":"query","schema":{}},{"name":"sort","required":false,"in":"query","description":"date (default, paged) | unitPrice | total","schema":{}},{"name":"transactionId","required":false,"in":"query","schema":{}},{"name":"receiptId","required":false,"in":"query","schema":{}},{"name":"dateTo","required":false,"in":"query","description":"Purchase day, inclusive (YYYY-MM-DD)","schema":{}},{"name":"dateFrom","required":false,"in":"query","description":"Purchase day, inclusive (YYYY-MM-DD)","schema":{}},{"name":"merchant","required":false,"in":"query","schema":{}},{"name":"q","required":false,"in":"query","description":"Matches product name, description and brand; terms are ANDed","schema":{}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Line items read from receipts, newest purchase first; unit prices are also given per base unit (kg, l, unit).","tags":["receipts"],"x-required-scope":["read:receipts"]}},"/households/{householdId}/receipt-items/summary":{"get":{"operationId":"ReceiptItemsController_summary","parameters":[{"name":"limit","required":false,"in":"query","schema":{}},{"name":"sort","required":false,"in":"query","description":"spend (default) | unitPrice | frequency | name","schema":{}},{"name":"dateTo","required":false,"in":"query","schema":{}},{"name":"dateFrom","required":false,"in":"query","schema":{}},{"name":"merchant","required":false,"in":"query","schema":{}},{"name":"q","required":false,"in":"query","schema":{}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Per-product summary across receipts: purchases, total spend, quantity-weighted average / min / max / latest unit price over the base unit, where it was bought.","tags":["receipts"],"x-required-scope":["read:receipts"]}},"/households/{householdId}/receipt-items/price-history":{"get":{"operationId":"ReceiptItemsController_priceHistory","parameters":[{"name":"merchant","required":false,"in":"query","schema":{}},{"name":"dateTo","required":false,"in":"query","schema":{}},{"name":"dateFrom","required":false,"in":"query","schema":{}},{"name":"bucket","required":false,"in":"query","description":"month (default) | week","schema":{}},{"name":"q","required":true,"in":"query","description":"Product to follow, e.g. \"manzana\"","schema":{}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Unit price of a product over time (per base unit, quantity-weighted per bucket) with the merchants it was bought at.","tags":["receipts"],"x-required-scope":["read:receipts"]}},"/households/{householdId}/search":{"get":{"operationId":"SearchController_run","parameters":[],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Cross-entity full-text search (transactions, payees, categories, tags, receipts; recurrences on request) ranked by trigram similarity; transactions also match what their receipts say was bought","tags":["search"],"x-required-scope":["read:search"]}},"/households/{householdId}/banking/institutions":{"get":{"operationId":"BankConnectionsController_listInstitutions","parameters":[],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"List available bank institutions for a country (region-routed provider, ADR 0048)","tags":["banking"],"x-required-scope":["read:banking"]}},"/households/{householdId}/bank-connections":{"post":{"operationId":"BankConnectionsController_start","parameters":[],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Start a bank connection (row is `pending` until completed): returns a redirect URL (redirect flow) or a link token (widget flow); institution is optional for Plaid, required for Enable Banking; optional clientPlatform (web by default, ios, android) picks the Plaid OAuth return","tags":["banking"],"x-required-scope":["write:banking"]},"get":{"operationId":"BankConnectionsController_list","parameters":[],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"List bank connections in the household","tags":["banking"],"x-required-scope":["read:banking"]}},"/households/{householdId}/bank-connections/{id}/complete":{"post":{"operationId":"BankConnectionsController_complete","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Finalize a connection after the provider auth flow; optional exchangeToken (Enable Banking code / Plaid public_token); persists accounts","tags":["banking"],"x-required-scope":["write:banking"]}},"/households/{householdId}/bank-connections/{id}":{"get":{"operationId":"BankConnectionsController_getById","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Fetch a single bank connection","tags":["banking"],"x-required-scope":["read:banking"]},"delete":{"operationId":"BankConnectionsController_softDelete","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":""}},"security":[{"bearer":[]}],"summary":"Soft-delete a bank connection","tags":["banking"],"x-required-scope":["write:banking"]}},"/households/{householdId}/bank-connections/{id}/reauth":{"post":{"operationId":"BankConnectionsController_reauth","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Re-authorize the same institution on this connection: Plaid Link update mode when the connection already holds a credential, otherwise a fresh requisition (90-day consent renewal); optional redirectUrl on our origin and clientPlatform (web by default, ios, android)","tags":["banking"],"x-required-scope":["write:banking"]}},"/households/{householdId}/bank-connections/{id}/sync":{"post":{"operationId":"BankConnectionsController_runSync","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"On-demand sync for this connection (rate-limited to 1/day per connection; 409 while a sync is running or the link is still pending)","tags":["banking"],"x-required-scope":["write:banking"]}},"/households/{householdId}/bank-connections/{id}/sync-runs":{"get":{"operationId":"BankConnectionsController_listSyncRuns","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"List sync run history for this connection","tags":["banking"],"x-required-scope":["read:banking"]}},"/households/{householdId}/bank-connections/{id}/quota":{"get":{"operationId":"BankConnectionsController_quota","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Quota usage snapshot for this connection (today, UTC)","tags":["banking"],"x-required-scope":["read:banking"]}},"/households/{householdId}/bank-accounts":{"get":{"operationId":"BankAccountsController_list","parameters":[],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"List bank accounts in the household","tags":["banking"],"x-required-scope":["read:banking"]}},"/households/{householdId}/bank-accounts/{id}/map":{"post":{"operationId":"BankAccountsController_map","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Map a bank account to an existing financial_account or create a new one","tags":["banking"],"x-required-scope":["write:banking"]}},"/households/{householdId}/bank-accounts/{id}/relink":{"patch":{"operationId":"BankAccountsController_relink","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Move the financial_account link to a different one","tags":["banking"],"x-required-scope":["write:banking"]}},"/households/{householdId}/bank-accounts/{id}/link":{"delete":{"operationId":"BankAccountsController_unlink","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Unlink a bank account from its vokse account (the account and imported transactions stay; new movements stop arriving until mapped again)","tags":["banking"],"x-required-scope":["write:banking"]}},"/households/{householdId}/bank-accounts/{id}":{"patch":{"operationId":"BankAccountsController_update","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Pause / resume sync for one bank account","tags":["banking"],"x-required-scope":["write:banking"]}},"/households/{householdId}/icons/suggest":{"post":{"operationId":"IconsController_suggest","parameters":[],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Suggest catalogue icons for category names. Layered resolver (emoji hint → multilingual dictionary → persistent cache → AI); never fails: unknown names get the default icon with confidence 0.","tags":["categories"],"x-required-scope":["read:categories"]}},"/currencies":{"get":{"operationId":"CurrenciesController_list","parameters":[],"responses":{"200":{"description":""}},"summary":"List all supported ISO 4217 currencies","tags":["currencies"],"x-required-scope":["read:currencies"]}},"/currencies/{code}":{"get":{"operationId":"CurrenciesController_getOne","parameters":[{"name":"code","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Get a single currency by ISO 4217 code","tags":["currencies"],"x-required-scope":["read:currencies"]}},"/notifications":{"get":{"operationId":"NotificationsController_list","parameters":[{"name":"includeRead","required":false,"in":"query","schema":{"type":"string"}},{"name":"includeDismissed","required":false,"in":"query","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"type":"string"}},{"name":"cursor","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"List the current user's notifications, newest first. Defaults: undismissed + unread only.","tags":["notifications"],"x-required-scope":["read:notifications"]}},"/notifications/{id}/read":{"post":{"operationId":"NotificationsController_markRead","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Mark a single notification read (idempotent)","tags":["notifications"],"x-required-scope":["write:notifications"]}},"/notifications/mark-all-read":{"post":{"operationId":"NotificationsController_markAllRead","parameters":[],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Mark every unread notification as read for the current user","tags":["notifications"],"x-required-scope":["write:notifications"]}},"/notifications/{id}":{"delete":{"operationId":"NotificationsController_dismiss","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":""}},"security":[{"bearer":[]}],"summary":"Dismiss a notification (hides from the active feed)","tags":["notifications"],"x-required-scope":["write:notifications"]}},"/notifications/preferences":{"get":{"operationId":"PreferencesController_list","parameters":[],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"List the current user's notification preferences. Auto-seeds defaults on first call.","tags":["notifications"],"x-required-scope":["read:notifications"]},"put":{"operationId":"PreferencesController_update","parameters":[],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Upsert (kind, channel, enabled, lockScreenDetails) preferences for the current user. lockScreenDetails is accepted on the push channel only.","tags":["notifications"],"x-required-scope":["write:notifications"]}},"/households/{householdId}/transaction-flags":{"get":{"operationId":"TransactionFlagsController_list","parameters":[],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"List the household colour flags (six built-ins, created on first read), by position","tags":["transaction-flags"],"x-required-scope":["read:transactions"]}},"/households/{householdId}/transaction-flags/{flagId}":{"patch":{"operationId":"TransactionFlagsController_rename","parameters":[{"name":"flagId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Rename a flag for the whole household; name null (or blank) restores the default name","tags":["transaction-flags"],"x-required-scope":["write:transactions"]}},"/households/{householdId}/transactions/import":{"post":{"operationId":"ImportController_run","parameters":[],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary"},"metadata":{"type":"string","description":"JSON encoded ImportMetadata: { accountId, format?, dryRun?, mapping?, currency?, dateFormat? }"}},"required":["file","metadata"]}}}},"responses":{"201":{"description":""}},"security":[{"bearer":[]}],"summary":"Import transactions from CSV/OFX/QFX/JSON (multipart/form-data)","tags":["transactions-import"],"x-required-scope":["write:import"]}},"/households/{householdId}/voice":{"post":{"operationId":"VoiceController_upload","parameters":[],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["file"],"properties":{"file":{"type":"string","format":"binary"}}}}}},"responses":{"201":{"description":""}},"security":[{"bearer":[]}],"summary":"Upload a voice memo (mp3/m4a/wav/webm/ogg, max 25 MB)","tags":["voice"],"x-required-scope":["write:voice"]},"get":{"operationId":"VoiceController_list","parameters":[{"name":"cursor","required":false,"in":"query","schema":{}},{"name":"limit","required":false,"in":"query","schema":{}},{"name":"status","required":false,"in":"query","schema":{}},{"name":"inboxOnly","required":false,"in":"query","schema":{}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"List voice memos in this household","tags":["voice"],"x-required-scope":["read:voice"]}},"/households/{householdId}/voice/{voiceMemoId}":{"get":{"operationId":"VoiceController_getOne","parameters":[{"name":"voiceMemoId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Get a voice memo + 300s presigned URL for the audio","tags":["voice"],"x-required-scope":["read:voice"]},"delete":{"operationId":"VoiceController_remove","parameters":[{"name":"voiceMemoId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":""}},"security":[{"bearer":[]}],"summary":"Soft-delete a voice memo","tags":["voice"],"x-required-scope":["write:voice"]}},"/households/{householdId}/voice/{voiceMemoId}/confirm":{"post":{"operationId":"VoiceController_confirm","parameters":[{"name":"voiceMemoId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"security":[{"bearer":[]}],"summary":"Materialise the parsed voice memo into a Transaction. Overrides win over the parse.","tags":["voice"],"x-required-scope":["write:voice"]}},"/households/{householdId}/voice/{voiceMemoId}/retry":{"post":{"operationId":"VoiceController_retry","parameters":[{"name":"voiceMemoId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"security":[{"bearer":[]}],"summary":"Re-enqueue a failed transcription","tags":["voice"],"x-required-scope":["write:voice"]}},"/households/{householdId}/webhooks/subscriptions":{"post":{"operationId":"WebhooksController_create","parameters":[],"responses":{"201":{"description":""}},"security":[{"bearer":[]}],"summary":"Register an outbound webhook subscription. Secret returned ONCE in the response.","tags":["webhooks"],"x-required-scope":["write:webhooks"]},"get":{"operationId":"WebhooksController_list","parameters":[],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"List webhook subscriptions for this household (no secrets)","tags":["webhooks"],"x-required-scope":["read:webhooks"]}},"/households/{householdId}/webhooks/subscriptions/{id}":{"patch":{"operationId":"WebhooksController_update","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Update url / eventTypes / status of a subscription","tags":["webhooks"],"x-required-scope":["write:webhooks"]},"delete":{"operationId":"WebhooksController_remove","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":""}},"security":[{"bearer":[]}],"summary":"Delete a webhook subscription + every associated delivery","tags":["webhooks"],"x-required-scope":["write:webhooks"]}},"/households/{householdId}/webhooks/subscriptions/{id}/rotate-secret":{"post":{"operationId":"WebhooksController_rotateSecret","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Rotate the HMAC signing secret for this subscription (returned ONCE)","tags":["webhooks"],"x-required-scope":["write:webhooks"]}},"/households/{householdId}/webhooks/subscriptions/{id}/test":{"post":{"operationId":"WebhooksController_sendTest","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"202":{"description":""}},"security":[{"bearer":[]}],"summary":"Send a signed test delivery to this subscription through the real delivery path","tags":["webhooks"],"x-required-scope":["write:webhooks"]}},"/households/{householdId}/webhooks/subscriptions/{id}/deliveries/{deliveryId}/retry":{"post":{"operationId":"WebhooksController_retryDelivery","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"deliveryId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"202":{"description":""}},"security":[{"bearer":[]}],"summary":"Queue a past delivery again with its original payload","tags":["webhooks"],"x-required-scope":["write:webhooks"]}},"/households/{householdId}/webhooks/subscriptions/{id}/deliveries":{"get":{"operationId":"WebhooksController_listDeliveries","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}},{"name":"limit","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Recent delivery log for this subscription","tags":["webhooks"],"x-required-scope":["read:webhooks"]}}},"info":{"title":"vokse API","description":"Personal finance backend with native AI capabilities","version":"0.1.0","contact":{}},"tags":[],"servers":[{"url":"https://api.vokse.ai","description":"Production"}],"components":{"securitySchemes":{"bearer":{"scheme":"bearer","bearerFormat":"JWT","type":"http"}},"schemas":{"Tag":{"type":"object","properties":{"id":{"type":"string","pattern":"^[0-9A-HJKMNP-TV-Z]{26}$"},"householdId":{"type":"string","pattern":"^[0-9A-HJKMNP-TV-Z]{26}$"},"name":{"type":"string"},"color":{"nullable":true,"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","householdId","name","color","createdAt","updatedAt"],"additionalProperties":false}}}}