--- openapi: 3.0.3 info: title: Sure API version: v1 description: OpenAPI documentation generated from executable request specs. servers: - url: https://app.sure.am description: Production - url: http://localhost:3000 description: Local development components: securitySchemes: apiKeyAuth: type: apiKey name: X-Api-Key in: header description: API key for authentication. Generate one from your account settings. schemas: Pagination: type: object required: - page - per_page - total_count - total_pages properties: page: type: integer minimum: 1 per_page: type: integer minimum: 1 total_count: type: integer minimum: 0 total_pages: type: integer minimum: 0 FamilyExportFile: type: object required: - attached properties: attached: type: boolean byte_size: type: integer nullable: true minimum: 0 content_type: type: string nullable: true FamilyExport: type: object required: - id - status - filename - downloadable - file - created_at - updated_at properties: id: type: string format: uuid status: type: string enum: - pending - processing - completed - failed filename: type: string downloadable: type: boolean download_path: type: string nullable: true file: "$ref": "#/components/schemas/FamilyExportFile" created_at: type: string format: date-time updated_at: type: string format: date-time FamilyExportResponse: type: object required: - data properties: data: "$ref": "#/components/schemas/FamilyExport" FamilyExportCollection: type: object required: - data - meta properties: data: type: array maxItems: 100 items: "$ref": "#/components/schemas/FamilyExport" meta: "$ref": "#/components/schemas/Pagination" ErrorResponse: type: object required: - error properties: error: type: string message: type: string nullable: true details: oneOf: - type: array items: type: string - type: object nullable: true errors: type: array items: type: string nullable: true description: Validation error messages (alternative to details used by trades, valuations, etc.) ErrorResponseWithImportId: type: object required: - error - import_id properties: error: type: string message: type: string nullable: true import_id: type: string format: uuid description: Import ID preserved for retry or inspection after upload succeeds but publish fails MfaRequiredResponse: type: object required: - error - mfa_required properties: error: type: string mfa_required: type: boolean ToolCall: type: object required: - id - function_name - function_arguments - created_at properties: id: type: string format: uuid function_name: type: string function_arguments: type: object additionalProperties: true function_result: type: object additionalProperties: true nullable: true created_at: type: string format: date-time Message: type: object required: - id - type - role - content - created_at - updated_at properties: id: type: string format: uuid type: type: string enum: - user_message - assistant_message role: type: string enum: - user - assistant content: type: string model: type: string nullable: true created_at: type: string format: date-time updated_at: type: string format: date-time tool_calls: type: array items: "$ref": "#/components/schemas/ToolCall" nullable: true MessageResponse: allOf: - "$ref": "#/components/schemas/Message" - type: object required: - chat_id properties: chat_id: type: string format: uuid ai_response_status: type: string enum: - pending - complete - failed nullable: true ai_response_message: type: string nullable: true ChatResource: type: object required: - id - title - created_at - updated_at properties: id: type: string format: uuid title: type: string error: type: string nullable: true created_at: type: string format: date-time updated_at: type: string format: date-time ChatSummary: allOf: - "$ref": "#/components/schemas/ChatResource" - type: object required: - message_count properties: message_count: type: integer minimum: 0 last_message_at: type: string format: date-time nullable: true ChatDetail: allOf: - "$ref": "#/components/schemas/ChatResource" - type: object required: - messages properties: messages: type: array items: "$ref": "#/components/schemas/Message" pagination: "$ref": "#/components/schemas/Pagination" nullable: true ChatCollection: type: object required: - chats - pagination properties: chats: type: array items: "$ref": "#/components/schemas/ChatSummary" pagination: "$ref": "#/components/schemas/Pagination" RetryResponse: type: object required: - message - message_id properties: message: type: string message_id: type: string format: uuid Account: type: object required: - id - name - account_type properties: id: type: string format: uuid name: type: string account_type: type: string nullable: true status: type: string AccountDetail: type: object required: - id - name - balance - balance_cents - cash_balance - cash_balance_cents - currency - classification - account_type - status - created_at - updated_at properties: id: type: string format: uuid name: type: string balance: type: string balance_cents: type: integer description: Signed balance in minor currency units cash_balance: type: string cash_balance_cents: type: integer description: Signed cash balance in minor currency units currency: type: string classification: type: string account_type: type: string nullable: true subtype: type: string nullable: true status: type: string enum: - active - draft - disabled - pending_deletion institution_name: type: string nullable: true institution_domain: type: string nullable: true created_at: type: string format: date-time updated_at: type: string format: date-time AccountCollection: type: object required: - accounts - pagination properties: accounts: type: array items: "$ref": "#/components/schemas/AccountDetail" pagination: "$ref": "#/components/schemas/Pagination" FamilySettings: type: object required: - id - currency - locale - date_format - month_start_day - moniker - default_account_sharing - custom_enabled_currencies - enabled_currencies - created_at - updated_at properties: id: type: string format: uuid name: type: string nullable: true currency: type: string locale: type: string date_format: type: string country: type: string nullable: true timezone: type: string nullable: true month_start_day: type: integer minimum: 1 maximum: 28 moniker: type: string enum: - Family - Group default_account_sharing: type: string enum: - shared - private custom_enabled_currencies: type: boolean enabled_currencies: type: array items: type: string created_at: type: string format: date-time updated_at: type: string format: date-time Category: type: object required: - id - name - color - icon properties: id: type: string format: uuid name: type: string color: type: string icon: type: string CategoryParent: type: object required: - id - name properties: id: type: string format: uuid name: type: string CategoryDetail: type: object required: - id - name - color - icon - subcategories_count - created_at - updated_at properties: id: type: string format: uuid name: type: string color: type: string icon: type: string parent: "$ref": "#/components/schemas/CategoryParent" nullable: true subcategories_count: type: integer minimum: 0 created_at: type: string format: date-time updated_at: type: string format: date-time CategoryCollection: type: object required: - categories - pagination properties: categories: type: array items: "$ref": "#/components/schemas/CategoryDetail" pagination: "$ref": "#/components/schemas/Pagination" Merchant: type: object required: - id - name properties: id: type: string format: uuid name: type: string MerchantDetail: type: object required: - id - name - type - created_at - updated_at properties: id: type: string format: uuid name: type: string type: type: string enum: - FamilyMerchant - ProviderMerchant created_at: type: string format: date-time updated_at: type: string format: date-time Tag: type: object required: - id - name - color properties: id: type: string format: uuid name: type: string color: type: string TagDetail: type: object required: - id - name - color - created_at - updated_at properties: id: type: string format: uuid name: type: string color: type: string created_at: type: string format: date-time updated_at: type: string format: date-time TagCollection: type: array items: "$ref": "#/components/schemas/TagDetail" RuleAction: type: object required: - id - action_type - created_at - updated_at properties: id: type: string format: uuid action_type: type: string value: type: string nullable: true created_at: type: string format: date-time updated_at: type: string format: date-time RuleCondition: type: object required: - id - condition_type - operator - sub_conditions - created_at - updated_at properties: id: type: string format: uuid condition_type: type: string operator: type: string value: type: string nullable: true sub_conditions: type: array items: "$ref": "#/components/schemas/RuleCondition" created_at: type: string format: date-time updated_at: type: string format: date-time Rule: type: object required: - id - resource_type - active - conditions - actions - created_at - updated_at properties: id: type: string format: uuid name: type: string nullable: true resource_type: type: string enum: - transaction active: type: boolean effective_date: type: string format: date nullable: true conditions: type: array items: "$ref": "#/components/schemas/RuleCondition" actions: type: array items: "$ref": "#/components/schemas/RuleAction" created_at: type: string format: date-time updated_at: type: string format: date-time RuleResponse: type: object required: - data properties: data: "$ref": "#/components/schemas/Rule" RuleCollection: type: object required: - data - meta properties: data: type: array items: "$ref": "#/components/schemas/Rule" meta: type: object required: - current_page - total_pages - total_count - per_page properties: current_page: type: integer next_page: type: integer nullable: true prev_page: type: integer nullable: true total_pages: type: integer total_count: type: integer per_page: type: integer RuleRun: type: object required: - id - rule_id - rule_name - execution_type - status - transactions_queued - transactions_processed - transactions_modified - pending_jobs_count - executed_at - rule - created_at - updated_at properties: id: type: string format: uuid rule_id: type: string format: uuid rule_name: type: string nullable: true execution_type: type: string enum: - manual - scheduled status: type: string enum: - pending - success - failed transactions_queued: type: integer minimum: 0 transactions_processed: type: integer minimum: 0 transactions_modified: type: integer minimum: 0 pending_jobs_count: type: integer minimum: 0 executed_at: type: string format: date-time error_message: type: string nullable: true rule: type: object nullable: true required: - id - resource_type - active properties: id: type: string format: uuid name: type: string nullable: true resource_type: type: string active: type: boolean created_at: type: string format: date-time updated_at: type: string format: date-time RuleRunResponse: type: object required: - data properties: data: "$ref": "#/components/schemas/RuleRun" RuleRunCollection: type: object required: - data - meta properties: data: type: array items: "$ref": "#/components/schemas/RuleRun" meta: type: object required: - current_page - total_pages - total_count - per_page properties: current_page: type: integer next_page: type: integer nullable: true prev_page: type: integer nullable: true total_pages: type: integer total_count: type: integer per_page: type: integer Transfer: type: object required: - id - amount - currency properties: id: type: string format: uuid amount: type: string currency: type: string other_account: "$ref": "#/components/schemas/Account" nullable: true RecurringTransaction: type: object required: - id - amount - amount_cents - currency - expected_day_of_month - last_occurrence_date - next_expected_date - status - occurrence_count - manual - created_at - updated_at properties: id: type: string format: uuid amount: type: string amount_cents: type: integer description: Amount in currency minor units currency: type: string expected_day_of_month: type: integer minimum: 1 maximum: 31 last_occurrence_date: type: string format: date next_expected_date: type: string format: date status: type: string enum: - active - inactive occurrence_count: type: integer minimum: 0 name: type: string nullable: true manual: type: boolean expected_amount_min: type: string nullable: true expected_amount_min_cents: type: integer nullable: true description: Minimum expected amount in currency minor units expected_amount_max: type: string nullable: true expected_amount_max_cents: type: integer nullable: true description: Maximum expected amount in currency minor units expected_amount_avg: type: string nullable: true expected_amount_avg_cents: type: integer nullable: true description: Average expected amount in currency minor units account: "$ref": "#/components/schemas/Account" nullable: true merchant: "$ref": "#/components/schemas/Merchant" nullable: true created_at: type: string format: date-time updated_at: type: string format: date-time RecurringTransactionCollection: type: object required: - recurring_transactions - pagination properties: recurring_transactions: type: array items: "$ref": "#/components/schemas/RecurringTransaction" pagination: "$ref": "#/components/schemas/Pagination" Transaction: type: object required: - id - date - amount - currency - name - classification - account - tags - created_at - updated_at properties: id: type: string format: uuid date: type: string format: date amount: type: string currency: type: string name: type: string notes: type: string nullable: true classification: type: string account: "$ref": "#/components/schemas/Account" category: "$ref": "#/components/schemas/Category" nullable: true merchant: "$ref": "#/components/schemas/Merchant" nullable: true tags: type: array items: "$ref": "#/components/schemas/Tag" transfer: "$ref": "#/components/schemas/Transfer" nullable: true created_at: type: string format: date-time updated_at: type: string format: date-time TransactionCollection: type: object required: - transactions - pagination properties: transactions: type: array items: "$ref": "#/components/schemas/Transaction" pagination: "$ref": "#/components/schemas/Pagination" Valuation: type: object required: - id - date - amount - currency - kind - account - created_at - updated_at properties: id: type: string format: uuid date: type: string format: date amount: type: string currency: type: string notes: type: string nullable: true kind: type: string account: "$ref": "#/components/schemas/Account" created_at: type: string format: date-time updated_at: type: string format: date-time ValuationCollection: type: object required: - valuations - pagination properties: valuations: type: array items: "$ref": "#/components/schemas/Valuation" pagination: "$ref": "#/components/schemas/Pagination" DeleteResponse: type: object required: - message properties: message: type: string ImportConfiguration: type: object properties: date_col_label: type: string nullable: true amount_col_label: type: string nullable: true name_col_label: type: string nullable: true category_col_label: type: string nullable: true tags_col_label: type: string nullable: true notes_col_label: type: string nullable: true account_col_label: type: string nullable: true date_format: type: string nullable: true number_format: type: string nullable: true signage_convention: type: string nullable: true ImportStats: type: object required: - rows_count - valid_rows_count - invalid_rows_count - mappings_count - unassigned_mappings_count properties: rows_count: type: integer minimum: 0 valid_rows_count: type: integer minimum: 0 invalid_rows_count: type: integer minimum: 0 mappings_count: type: integer minimum: 0 unassigned_mappings_count: type: integer minimum: 0 ImportStatusSummary: type: object required: - uploaded - configured - terminal properties: uploaded: type: boolean configured: type: boolean terminal: type: boolean ImportStatusDetail: allOf: - "$ref": "#/components/schemas/ImportStatusSummary" - type: object required: - cleaned - publishable - revertable properties: cleaned: type: boolean publishable: type: boolean revertable: type: boolean ImportSummary: type: object required: - id - type - status - created_at - updated_at - status_detail properties: id: type: string format: uuid type: type: string enum: - TransactionImport - TradeImport - AccountImport - MintImport - CategoryImport - RuleImport - SureImport status: type: string enum: - pending - complete - importing - reverting - revert_failed - failed created_at: type: string format: date-time updated_at: type: string format: date-time account_id: type: string format: uuid nullable: true rows_count: type: integer minimum: 0 error: type: string nullable: true status_detail: "$ref": "#/components/schemas/ImportStatusSummary" ImportDetail: type: object required: - id - type - status - created_at - updated_at - status_detail - configuration - stats properties: id: type: string format: uuid type: type: string enum: - TransactionImport - TradeImport - AccountImport - MintImport - CategoryImport - RuleImport - SureImport status: type: string enum: - pending - complete - importing - reverting - revert_failed - failed created_at: type: string format: date-time updated_at: type: string format: date-time account_id: type: string format: uuid nullable: true error: type: string nullable: true status_detail: "$ref": "#/components/schemas/ImportStatusDetail" configuration: "$ref": "#/components/schemas/ImportConfiguration" stats: "$ref": "#/components/schemas/ImportStats" ImportCollection: type: object required: - data - meta properties: data: type: array items: "$ref": "#/components/schemas/ImportSummary" meta: type: object required: - current_page - total_pages - total_count - per_page properties: current_page: type: integer minimum: 1 next_page: type: integer nullable: true prev_page: type: integer nullable: true total_pages: type: integer minimum: 0 total_count: type: integer minimum: 0 per_page: type: integer minimum: 1 ImportResponse: type: object required: - data properties: data: "$ref": "#/components/schemas/ImportDetail" Trade: type: object required: - id - date - amount - currency - name - qty - price - account - created_at - updated_at properties: id: type: string format: uuid date: type: string format: date amount: type: string currency: type: string name: type: string notes: type: string nullable: true qty: type: string price: type: string investment_activity_label: type: string nullable: true account: "$ref": "#/components/schemas/Account" security: type: object nullable: true properties: id: type: string format: uuid ticker: type: string name: type: string nullable: true category: type: object nullable: true properties: id: type: string format: uuid name: type: string created_at: type: string format: date-time updated_at: type: string format: date-time TradeCollection: type: object required: - trades - pagination properties: trades: type: array items: "$ref": "#/components/schemas/Trade" pagination: "$ref": "#/components/schemas/Pagination" Holding: type: object required: - id - date - qty - price - amount - currency - account - security - created_at - updated_at properties: id: type: string format: uuid date: type: string format: date qty: type: string description: Quantity of shares held price: type: string description: Formatted price per share amount: type: string currency: type: string cost_basis_source: type: string nullable: true account: "$ref": "#/components/schemas/Account" security: type: object required: - id - ticker - name properties: id: type: string format: uuid ticker: type: string name: type: string nullable: true avg_cost: type: string nullable: true created_at: type: string format: date-time updated_at: type: string format: date-time HoldingCollection: type: object required: - holdings - pagination properties: holdings: type: array items: "$ref": "#/components/schemas/Holding" pagination: "$ref": "#/components/schemas/Pagination" Money: type: object required: - amount - currency - formatted properties: amount: type: string description: Numeric amount as string currency: type: string description: ISO 4217 currency code formatted: type: string description: Locale-formatted money string BalanceSheet: type: object required: - currency - net_worth - assets - liabilities properties: currency: type: string description: Family primary currency net_worth: "$ref": "#/components/schemas/Money" assets: "$ref": "#/components/schemas/Money" liabilities: "$ref": "#/components/schemas/Money" SuccessMessage: type: object required: - message properties: message: type: string ResetInitiatedResponse: type: object required: - message - status - job_id - family_id - status_url properties: message: type: string status: type: string enum: - queued job_id: type: string description: Informational Active Job identifier returned by the queue adapter; reset status is family-scoped, not job-scoped. family_id: type: string format: uuid description: UUID of the family being reset. status_url: type: string ResetStatusResponse: type: object required: - status - family_id - reset_complete - counts properties: status: type: string enum: - complete - data_remaining description: Counts-based family reset status at response time. family_id: type: string format: uuid description: UUID of the family whose reset target counts were checked. reset_complete: type: boolean description: True when all reset target counts are zero at response time. This is a family data snapshot, not a durable per-job completion record. counts: type: object required: - accounts - categories - tags - merchants - plaid_items - imports - budgets properties: accounts: type: integer minimum: 0 categories: type: integer minimum: 0 tags: type: integer minimum: 0 merchants: type: integer minimum: 0 plaid_items: type: integer minimum: 0 imports: type: integer minimum: 0 budgets: type: integer minimum: 0 paths: "/api/v1/accounts": get: summary: List accounts tags: - Accounts security: - apiKeyAuth: [] parameters: - name: page in: query required: false description: 'Page number (default: 1)' schema: type: integer - name: per_page in: query required: false description: 'Items per page (default: 25, max: 100)' schema: type: integer - name: include_disabled in: query required: false description: Include disabled accounts in the response. Defaults to false. schema: type: boolean responses: '200': description: accounts paginated content: application/json: schema: "$ref": "#/components/schemas/AccountCollection" "/api/v1/accounts/{id}": parameters: - name: id in: path required: true description: Account ID schema: type: string format: uuid get: summary: Retrieve an account tags: - Accounts security: - apiKeyAuth: [] parameters: - name: include_disabled in: query required: false description: Allow retrieving a disabled account. Defaults to false. schema: type: boolean responses: '200': description: account retrieved content: application/json: schema: "$ref": "#/components/schemas/AccountDetail" '401': description: unauthorized content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" '403': description: insufficient scope content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" '404': description: account not found content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" "/api/v1/auth/signup": post: summary: Sign up a new user tags: - Auth parameters: [] responses: '201': description: user created content: application/json: schema: type: object properties: access_token: type: string refresh_token: type: string token_type: type: string expires_in: type: integer created_at: type: integer user: type: object properties: id: type: string format: uuid email: type: string first_name: type: string last_name: type: string ui_layout: type: string enum: - dashboard - intro ai_enabled: type: boolean '422': description: validation error content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" '403': description: invite code required or invalid content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" requestBody: content: application/json: schema: type: object properties: user: type: object properties: email: type: string format: email description: User email address password: type: string description: Password (min 8 chars, mixed case, number, special char) first_name: type: string last_name: type: string required: - email - password device: type: object properties: device_id: type: string description: Unique device identifier device_name: type: string description: Human-readable device name device_type: type: string description: Device type (e.g. ios, android) os_version: type: string app_version: type: string required: - device_id - device_name - device_type - os_version - app_version invite_code: type: string nullable: true description: Invite code (required when invites are enforced) required: - user - device required: true "/api/v1/auth/login": post: summary: Log in with email and password tags: - Auth parameters: [] responses: '200': description: login successful content: application/json: schema: type: object properties: access_token: type: string refresh_token: type: string token_type: type: string expires_in: type: integer created_at: type: integer user: type: object properties: id: type: string format: uuid email: type: string first_name: type: string last_name: type: string ui_layout: type: string enum: - dashboard - intro ai_enabled: type: boolean '401': description: invalid credentials or MFA required content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" requestBody: content: application/json: schema: type: object properties: email: type: string format: email password: type: string otp_code: type: string nullable: true description: TOTP code if MFA is enabled device: type: object properties: device_id: type: string device_name: type: string device_type: type: string os_version: type: string app_version: type: string required: - device_id - device_name - device_type - os_version - app_version required: - email - password - device required: true "/api/v1/auth/sso_exchange": post: summary: Exchange mobile SSO authorization code for tokens tags: - Auth description: Exchanges a one-time authorization code (received via deep link after mobile SSO) for OAuth tokens. The code is single-use and expires after 5 minutes. parameters: [] responses: '200': description: tokens issued content: application/json: schema: type: object properties: access_token: type: string refresh_token: type: string token_type: type: string expires_in: type: integer created_at: type: integer user: type: object properties: id: type: string format: uuid email: type: string first_name: type: string last_name: type: string ui_layout: type: string enum: - dashboard - intro ai_enabled: type: boolean '401': description: invalid or expired code content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" requestBody: content: application/json: schema: type: object properties: code: type: string description: One-time authorization code from mobile SSO callback required: - code required: true "/api/v1/auth/refresh": post: summary: Refresh an access token tags: - Auth parameters: [] responses: '200': description: token refreshed content: application/json: schema: type: object properties: access_token: type: string refresh_token: type: string token_type: type: string expires_in: type: integer created_at: type: integer '401': description: invalid refresh token content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" '400': description: missing refresh token content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" requestBody: content: application/json: schema: type: object properties: refresh_token: type: string description: The refresh token from a previous login or refresh device: type: object properties: device_id: type: string required: - device_id required: - refresh_token - device required: true "/api/v1/auth/sso_link": post: summary: Link an existing account via SSO tags: - Auth description: Authenticates with email/password and links the SSO identity from a previously issued linking code. Creates an OidcIdentity, logs the link via SsoAuditLog, and issues mobile OAuth tokens. parameters: [] responses: '200': description: account linked and tokens issued content: application/json: schema: type: object properties: access_token: type: string refresh_token: type: string token_type: type: string expires_in: type: integer created_at: type: integer user: type: object properties: id: type: string format: uuid email: type: string first_name: type: string last_name: type: string ui_layout: type: string enum: - dashboard - intro ai_enabled: type: boolean '400': description: missing linking code content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" '401': description: invalid credentials or expired linking code content: application/json: schema: oneOf: - "$ref": "#/components/schemas/ErrorResponse" - "$ref": "#/components/schemas/MfaRequiredResponse" requestBody: content: application/json: schema: type: object properties: linking_code: type: string description: One-time linking code from mobile SSO onboarding redirect email: type: string format: email description: Email of the existing account to link password: type: string description: Password for the existing account required: - linking_code - email - password required: true "/api/v1/auth/sso_create_account": post: summary: Create a new account via SSO tags: - Auth description: Creates a new user and family from a previously issued linking code. Links the SSO identity via OidcIdentity, logs the JIT account creation via SsoAuditLog, and issues mobile OAuth tokens. The linking code must have allow_account_creation enabled. parameters: [] responses: '200': description: account created and tokens issued content: application/json: schema: type: object properties: access_token: type: string refresh_token: type: string token_type: type: string expires_in: type: integer created_at: type: integer user: type: object properties: id: type: string format: uuid email: type: string first_name: type: string last_name: type: string ui_layout: type: string enum: - dashboard - intro ai_enabled: type: boolean '400': description: missing linking code content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" '401': description: invalid or expired linking code content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" '403': description: account creation disabled content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" '422': description: user validation error content: application/json: schema: type: object properties: errors: type: array items: type: string requestBody: content: application/json: schema: type: object properties: linking_code: type: string description: One-time linking code from mobile SSO onboarding redirect first_name: type: string description: First name (overrides value from SSO provider if provided) last_name: type: string description: Last name (overrides value from SSO provider if provided) required: - linking_code required: true "/api/v1/auth/enable_ai": patch: summary: Enable AI features for the authenticated user tags: - Auth security: - apiKeyAuth: [] responses: '200': description: ai enabled content: application/json: schema: type: object properties: user: type: object properties: id: type: string format: uuid email: type: string first_name: type: string nullable: true last_name: type: string nullable: true ui_layout: type: string enum: - dashboard - intro ai_enabled: type: boolean '401': description: unauthorized content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" '403': description: insufficient scope content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" "/api/v1/balance_sheet": get: summary: Show balance sheet tags: - Balance Sheet description: Returns the family balance sheet including net worth, total assets, and total liabilities with amounts converted to the family's primary currency. security: - apiKeyAuth: [] responses: '200': description: balance sheet returned content: application/json: schema: "$ref": "#/components/schemas/BalanceSheet" '401': description: unauthorized content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" "/api/v1/categories": get: summary: List categories tags: - Categories security: - apiKeyAuth: [] parameters: - name: page in: query required: false description: 'Page number (default: 1)' schema: type: integer - name: per_page in: query required: false description: 'Items per page (default: 25, max: 100)' schema: type: integer - name: roots_only in: query required: false description: Return only root categories (no parent) schema: type: boolean - name: parent_id in: query required: false description: Filter by parent category ID schema: type: string format: uuid responses: '200': description: categories filtered by parent content: application/json: schema: "$ref": "#/components/schemas/CategoryCollection" "/api/v1/categories/{id}": parameters: - name: id in: path required: true description: Category ID schema: type: string get: summary: Retrieve a category tags: - Categories security: - apiKeyAuth: [] responses: '200': description: subcategory retrieved with parent content: application/json: schema: "$ref": "#/components/schemas/CategoryDetail" '404': description: category not found content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" "/api/v1/chats": get: summary: List chats tags: - Chats security: - apiKeyAuth: [] responses: '200': description: chats listed content: application/json: schema: "$ref": "#/components/schemas/ChatCollection" '403': description: AI features disabled content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" post: summary: Create chat tags: - Chats security: - apiKeyAuth: [] parameters: [] responses: '201': description: chat created content: application/json: schema: "$ref": "#/components/schemas/ChatDetail" '422': description: validation error content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" requestBody: content: application/json: schema: type: object properties: title: type: string example: Monthly budget review message: type: string description: Optional initial message in the chat model: type: string description: Optional OpenAI model identifier required: - title required: true "/api/v1/chats/{id}": parameters: - name: id in: path required: true description: Chat ID schema: type: string get: summary: Retrieve a chat tags: - Chats security: - apiKeyAuth: [] responses: '200': description: chat retrieved content: application/json: schema: "$ref": "#/components/schemas/ChatDetail" '404': description: chat not found content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" patch: summary: Update a chat tags: - Chats security: - apiKeyAuth: [] parameters: [] responses: '200': description: chat updated content: application/json: schema: "$ref": "#/components/schemas/ChatDetail" '404': description: chat not found content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" '422': description: validation error content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" requestBody: content: application/json: schema: type: object properties: title: type: string example: Updated chat title required: true delete: summary: Delete a chat tags: - Chats security: - apiKeyAuth: [] responses: '204': description: chat deleted '404': description: chat not found "/api/v1/chats/{chat_id}/messages": parameters: - name: chat_id in: path required: true description: Chat ID schema: type: string post: summary: Create a message tags: - Chat Messages security: - apiKeyAuth: [] parameters: [] responses: '201': description: message created content: application/json: schema: "$ref": "#/components/schemas/MessageResponse" '404': description: chat not found content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" '422': description: validation error content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" requestBody: content: application/json: schema: type: object properties: content: type: string model: type: string required: - content required: true "/api/v1/chats/{chat_id}/messages/retry": parameters: - name: chat_id in: path required: true description: Chat ID schema: type: string post: summary: Retry the last assistant response tags: - Chat Messages security: - apiKeyAuth: [] responses: '202': description: retry started content: application/json: schema: "$ref": "#/components/schemas/RetryResponse" '404': description: chat not found content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" '422': description: no assistant message available content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" "/api/v1/family_exports": get: summary: Lists family exports tags: - Family Exports security: - apiKeyAuth: [] parameters: - name: page in: query required: false description: 'Page number (default: 1)' schema: type: integer - name: per_page in: query required: false description: 'Items per page (default: 25, max: 100)' schema: type: integer responses: '200': description: family exports listed content: application/json: schema: "$ref": "#/components/schemas/FamilyExportCollection" '401': description: unauthorized content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" '403': description: forbidden content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" post: summary: Queues a family export tags: - Family Exports security: - apiKeyAuth: [] parameters: [] responses: '202': description: family export queued content: application/json: schema: "$ref": "#/components/schemas/FamilyExportResponse" '401': description: unauthorized content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" '403': description: forbidden content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" '422': description: invalid params content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" requestBody: content: application/json: schema: type: object additionalProperties: false description: Family export creation does not accept request parameters. "/api/v1/family_exports/{id}": parameters: - name: id in: path format: uuid required: true schema: type: string get: summary: Shows a family export tags: - Family Exports security: - apiKeyAuth: [] responses: '200': description: family export shown content: application/json: schema: "$ref": "#/components/schemas/FamilyExportResponse" '401': description: unauthorized content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" '403': description: forbidden content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" '404': description: not found content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" "/api/v1/family_exports/{id}/download": parameters: - name: id in: path format: uuid required: true schema: type: string get: summary: Downloads a completed family export tags: - Family Exports security: - apiKeyAuth: [] responses: '302': description: family export download redirected '401': description: unauthorized content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" '403': description: forbidden content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" '404': description: not found content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" '409': description: export not ready content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" "/api/v1/family_settings": get: summary: Retrieve family settings description: Retrieve a read-only snapshot of non-secret family configuration. tags: - Family Settings security: - apiKeyAuth: [] responses: '200': description: family settings retrieved content: application/json: schema: "$ref": "#/components/schemas/FamilySettings" '401': description: unauthorized content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" '403': description: insufficient scope content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" "/api/v1/holdings": get: summary: List holdings tags: - Holdings security: - apiKeyAuth: [] parameters: - name: page in: query required: false description: 'Page number (default: 1)' schema: type: integer - name: per_page in: query required: false description: 'Items per page (default: 25, max: 100)' schema: type: integer - name: account_id in: query required: false description: Filter by account ID schema: type: string - name: account_ids in: query required: false description: Filter by multiple account IDs schema: type: array items: type: string - name: date in: query required: false description: Filter by exact date schema: type: string format: date - name: start_date in: query required: false description: Filter holdings from this date (inclusive) schema: type: string format: date - name: end_date in: query required: false description: Filter holdings until this date (inclusive) schema: type: string format: date - name: security_id in: query required: false description: Filter by security ID schema: type: string responses: '200': description: holdings paginated content: application/json: schema: "$ref": "#/components/schemas/HoldingCollection" '401': description: unauthorized content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" '422': description: invalid date filter content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" "/api/v1/holdings/{id}": parameters: - name: id in: path required: true description: Holding ID schema: type: string get: summary: Retrieve holding tags: - Holdings security: - apiKeyAuth: [] responses: '200': description: holding retrieved content: application/json: schema: "$ref": "#/components/schemas/Holding" '401': description: unauthorized content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" '404': description: holding not found content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" "/api/v1/imports": get: summary: List imports description: List all imports for the user's family with pagination and filtering. tags: - Imports security: - apiKeyAuth: [] parameters: - name: page in: query required: false description: 'Page number (default: 1)' schema: type: integer - name: per_page in: query required: false description: 'Items per page (default: 25, max: 100)' schema: type: integer - name: status in: query required: false description: Filter by status schema: type: string enum: - pending - complete - importing - reverting - revert_failed - failed - name: type in: query required: false description: Filter by import type schema: type: string enum: - TransactionImport - TradeImport - AccountImport - MintImport - CategoryImport - RuleImport - SureImport responses: '200': description: imports filtered by type content: application/json: schema: "$ref": "#/components/schemas/ImportCollection" post: summary: Create import description: Create a new import from raw CSV content, inline Sure NDJSON content, or an uploaded Sure NDJSON file. tags: - Imports security: - apiKeyAuth: [] parameters: [] responses: '201': description: import created content: application/json: schema: "$ref": "#/components/schemas/ImportResponse" '422': description: validation error - file too large content: application/json: schema: oneOf: - "$ref": "#/components/schemas/ErrorResponse" - "$ref": "#/components/schemas/ErrorResponseWithImportId" '500': description: import uploaded but publish enqueue failed content: application/json: schema: "$ref": "#/components/schemas/ErrorResponseWithImportId" requestBody: content: application/json: schema: type: object properties: raw_file_content: type: string description: Raw CSV or Sure NDJSON content as a string. Required for SureImport unless a multipart file is uploaded. type: type: string enum: - TransactionImport - TradeImport - AccountImport - MintImport - CategoryImport - RuleImport - SureImport description: Import type (defaults to TransactionImport) account_id: type: string format: uuid description: Account ID to import into publish: type: string description: Set to "true" to automatically queue for processing if configuration is valid date_col_label: type: string description: CSV imports only. Header name for the date column amount_col_label: type: string description: CSV imports only. Header name for the amount column name_col_label: type: string description: CSV imports only. Header name for the transaction name column category_col_label: type: string description: CSV imports only. Header name for the category column tags_col_label: type: string description: CSV imports only. Header name for the tags column notes_col_label: type: string description: CSV imports only. Header name for the notes column account_col_label: type: string description: CSV imports only. Header name for the account column when importing rows across multiple accounts qty_col_label: type: string description: CSV trade imports only. Header name for the quantity column ticker_col_label: type: string description: CSV trade imports only. Header name for the ticker column price_col_label: type: string description: CSV trade imports only. Header name for the price column entity_type_col_label: type: string description: CSV imports only. Header name for the entity type column currency_col_label: type: string description: CSV imports only. Header name for the currency column exchange_operating_mic_col_label: type: string description: CSV trade imports only. Header name for the exchange operating MIC column date_format: type: string description: CSV imports only. Date format pattern (e.g., "%m/%d/%Y") number_format: type: string enum: - '1,234.56' - 1.234,56 - 1 234,56 - '1,234' description: CSV imports only. Number format for parsing amounts signage_convention: type: string enum: - inflows_positive - inflows_negative description: CSV imports only. How to interpret positive/negative amounts col_sep: type: string enum: - "," - ";" description: CSV imports only. Column separator amount_type_strategy: type: string enum: - signed_amount - custom_column description: CSV imports only. Amount parsing strategy amount_type_inflow_value: type: string description: CSV imports only. Column value that marks an amount as an inflow when using custom_column strategy multipart/form-data: schema: type: object properties: raw_file_content: type: string description: Raw CSV or Sure NDJSON content as a string. Required for SureImport unless a multipart file is uploaded. type: type: string enum: - TransactionImport - TradeImport - AccountImport - MintImport - CategoryImport - RuleImport - SureImport description: Import type (defaults to TransactionImport) account_id: type: string format: uuid description: Account ID to import into publish: type: string description: Set to "true" to automatically queue for processing if configuration is valid date_col_label: type: string description: CSV imports only. Header name for the date column amount_col_label: type: string description: CSV imports only. Header name for the amount column name_col_label: type: string description: CSV imports only. Header name for the transaction name column category_col_label: type: string description: CSV imports only. Header name for the category column tags_col_label: type: string description: CSV imports only. Header name for the tags column notes_col_label: type: string description: CSV imports only. Header name for the notes column account_col_label: type: string description: CSV imports only. Header name for the account column when importing rows across multiple accounts qty_col_label: type: string description: CSV trade imports only. Header name for the quantity column ticker_col_label: type: string description: CSV trade imports only. Header name for the ticker column price_col_label: type: string description: CSV trade imports only. Header name for the price column entity_type_col_label: type: string description: CSV imports only. Header name for the entity type column currency_col_label: type: string description: CSV imports only. Header name for the currency column exchange_operating_mic_col_label: type: string description: CSV trade imports only. Header name for the exchange operating MIC column date_format: type: string description: CSV imports only. Date format pattern (e.g., "%m/%d/%Y") number_format: type: string enum: - '1,234.56' - 1.234,56 - 1 234,56 - '1,234' description: CSV imports only. Number format for parsing amounts signage_convention: type: string enum: - inflows_positive - inflows_negative description: CSV imports only. How to interpret positive/negative amounts col_sep: type: string enum: - "," - ";" description: CSV imports only. Column separator amount_type_strategy: type: string enum: - signed_amount - custom_column description: CSV imports only. Amount parsing strategy amount_type_inflow_value: type: string description: CSV imports only. Column value that marks an amount as an inflow when using custom_column strategy "/api/v1/imports/{id}": parameters: - name: id in: path required: true description: Import ID schema: type: string get: summary: Retrieve an import description: Retrieve detailed information about a specific import, including configuration and row statistics. tags: - Imports security: - apiKeyAuth: [] responses: '200': description: import retrieved content: application/json: schema: "$ref": "#/components/schemas/ImportResponse" '404': description: import not found content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" "/api/v1/merchants": get: summary: List merchants tags: - Merchants security: - apiKeyAuth: [] responses: '200': description: merchants listed content: application/json: schema: type: array items: "$ref": "#/components/schemas/MerchantDetail" "/api/v1/merchants/{id}": parameters: - name: id in: path required: true description: Merchant ID schema: type: string get: summary: Retrieve a merchant tags: - Merchants security: - apiKeyAuth: [] responses: '200': description: merchant retrieved content: application/json: schema: "$ref": "#/components/schemas/MerchantDetail" '404': description: merchant not found content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" "/api/v1/recurring_transactions": get: summary: List recurring transactions tags: - Recurring Transactions security: - apiKeyAuth: [] parameters: - name: page in: query required: false description: 'Page number (default: 1)' schema: type: integer - name: per_page in: query required: false description: 'Items per page (default: 25, max: 100)' schema: type: integer - name: status in: query required: false description: Filter by recurring status schema: type: string enum: - active - inactive - name: account_id in: query required: false description: Filter by account ID schema: type: string format: uuid responses: '200': description: recurring transactions listed content: application/json: schema: "$ref": "#/components/schemas/RecurringTransactionCollection" '401': description: unauthorized content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" '422': description: validation error - malformed account filter content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" post: summary: Create recurring transaction tags: - Recurring Transactions security: - apiKeyAuth: [] parameters: [] responses: '201': description: recurring transaction created content: application/json: schema: "$ref": "#/components/schemas/RecurringTransaction" '401': description: unauthorized content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" '403': description: forbidden - requires read_write scope content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" '404': description: account not found content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" '422': description: validation error - negative occurrence count content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" requestBody: content: application/json: schema: type: object properties: recurring_transaction: type: object properties: account_id: type: string format: uuid nullable: true merchant_id: type: string format: uuid nullable: true name: type: string nullable: true amount: type: number currency: type: string expected_day_of_month: type: integer minimum: 1 maximum: 31 last_occurrence_date: type: string format: date next_expected_date: type: string format: date status: type: string enum: - active - inactive occurrence_count: type: integer minimum: 0 manual: type: boolean expected_amount_min: type: number nullable: true expected_amount_max: type: number nullable: true expected_amount_avg: type: number nullable: true required: - amount - currency - expected_day_of_month - last_occurrence_date - next_expected_date anyOf: - required: - name - required: - merchant_id required: - recurring_transaction required: true "/api/v1/recurring_transactions/{id}": parameters: - name: id in: path required: true description: Recurring transaction ID schema: type: string get: summary: Retrieve recurring transaction tags: - Recurring Transactions security: - apiKeyAuth: [] responses: '200': description: recurring transaction retrieved content: application/json: schema: "$ref": "#/components/schemas/RecurringTransaction" '401': description: unauthorized content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" '404': description: recurring transaction not found content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" patch: summary: Update recurring transaction tags: - Recurring Transactions security: - apiKeyAuth: [] parameters: [] responses: '200': description: recurring transaction updated content: application/json: schema: "$ref": "#/components/schemas/RecurringTransaction" '401': description: unauthorized content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" '403': description: forbidden - requires read_write scope content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" '404': description: recurring transaction not found content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" '422': description: validation error content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" requestBody: content: application/json: schema: type: object properties: recurring_transaction: type: object properties: status: type: string enum: - active - inactive expected_day_of_month: type: integer minimum: 1 maximum: 31 next_expected_date: type: string format: date required: true delete: summary: Delete recurring transaction tags: - Recurring Transactions security: - apiKeyAuth: [] responses: '200': description: recurring transaction deleted content: application/json: schema: "$ref": "#/components/schemas/SuccessMessage" '401': description: unauthorized content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" '403': description: forbidden - requires read_write scope content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" '404': description: recurring transaction not found content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" "/api/v1/rule_runs": get: summary: List rule runs description: List rule run history for the authenticated user family. tags: - Rule Runs security: - apiKeyAuth: [] parameters: - name: page in: query required: false description: 'Page number (default: 1)' schema: type: integer - name: per_page in: query required: false description: 'Items per page (default: 25, max: 100)' schema: type: integer - name: rule_id in: query required: false description: Filter by rule ID schema: type: string format: uuid - name: status in: query required: false description: Filter by run status schema: type: string enum: - pending - success - failed - name: execution_type in: query required: false description: Filter by execution type schema: type: string enum: - manual - scheduled - name: start_executed_at in: query required: false description: Filter runs executed at or after this timestamp schema: type: string format: date-time - name: end_executed_at in: query required: false description: Filter runs executed at or before this timestamp schema: type: string format: date-time responses: '200': description: rule runs listed content: application/json: schema: "$ref": "#/components/schemas/RuleRunCollection" '401': description: unauthorized content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" '403': description: insufficient scope content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" '422': description: invalid filter content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" "/api/v1/rule_runs/{id}": parameters: - name: id in: path required: true description: Rule run ID schema: type: string format: uuid get: summary: Retrieve a rule run description: Retrieve one rule run from the authenticated user family. tags: - Rule Runs security: - apiKeyAuth: [] responses: '200': description: rule run retrieved content: application/json: schema: "$ref": "#/components/schemas/RuleRunResponse" '401': description: unauthorized content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" '403': description: insufficient scope content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" '404': description: rule run not found content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" "/api/v1/rules": get: summary: List rules tags: - Rules security: - apiKeyAuth: [] parameters: - name: page in: query required: false description: 'Page number (default: 1)' schema: type: integer - name: per_page in: query required: false description: 'Items per page (default: 25, max: 100)' schema: type: integer - name: resource_type in: query required: false description: Filter by rule resource type schema: type: string enum: - transaction - name: active in: query required: false description: Filter by active status schema: type: boolean responses: '200': description: rules listed content: application/json: schema: "$ref": "#/components/schemas/RuleCollection" '401': description: unauthorized content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" '403': description: forbidden - requires read scope content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" '422': description: unsupported resource type content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" "/api/v1/rules/{id}": parameters: - name: id in: path required: true description: Rule ID schema: type: string get: summary: Retrieve a rule tags: - Rules security: - apiKeyAuth: [] responses: '200': description: rule retrieved content: application/json: schema: "$ref": "#/components/schemas/RuleResponse" '401': description: unauthorized content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" '403': description: forbidden - requires read scope content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" '404': description: rule not found content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" "/api/v1/tags": get: summary: List tags tags: - Tags security: - apiKeyAuth: [] responses: '200': description: tags listed content: application/json: schema: "$ref": "#/components/schemas/TagCollection" post: summary: Create tag tags: - Tags security: - apiKeyAuth: [] parameters: [] responses: '201': description: tag created with auto-assigned color content: application/json: schema: "$ref": "#/components/schemas/TagDetail" '422': description: validation error - missing name content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" requestBody: content: application/json: schema: type: object properties: tag: type: object properties: name: type: string description: Tag name (required) color: type: string description: Hex color code (optional, auto-assigned if not provided) required: - name required: - tag required: true "/api/v1/tags/{id}": parameters: - name: id in: path required: true description: Tag ID schema: type: string get: summary: Retrieve a tag tags: - Tags security: - apiKeyAuth: [] responses: '200': description: tag retrieved content: application/json: schema: "$ref": "#/components/schemas/TagDetail" '404': description: tag not found content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" patch: summary: Update a tag tags: - Tags security: - apiKeyAuth: [] parameters: [] responses: '200': description: tag updated content: application/json: schema: "$ref": "#/components/schemas/TagDetail" '404': description: tag not found content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" requestBody: content: application/json: schema: type: object properties: tag: type: object properties: name: type: string color: type: string required: true delete: summary: Delete a tag tags: - Tags security: - apiKeyAuth: [] responses: '204': description: tag deleted '404': description: tag not found "/api/v1/trades": get: summary: List trades tags: - Trades security: - apiKeyAuth: [] parameters: - name: page in: query required: false description: 'Page number (default: 1)' schema: type: integer - name: per_page in: query required: false description: 'Items per page (default: 25, max: 100)' schema: type: integer - name: account_id in: query required: false description: Filter by account ID schema: type: string - name: account_ids in: query required: false description: Filter by multiple account IDs schema: type: array items: type: string - name: start_date in: query required: false description: Filter trades from this date (inclusive) schema: type: string format: date - name: end_date in: query required: false description: Filter trades until this date (inclusive) schema: type: string format: date responses: '200': description: trades paginated content: application/json: schema: "$ref": "#/components/schemas/TradeCollection" '401': description: unauthorized content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" '422': description: invalid date filter content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" post: summary: Create trade tags: - Trades security: - apiKeyAuth: [] parameters: [] responses: '201': description: trade created content: application/json: schema: "$ref": "#/components/schemas/Trade" '422': description: validation error - missing security identifier content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" '404': description: account not found content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" requestBody: content: application/json: schema: type: object properties: trade: type: object properties: account_id: type: string format: uuid description: Account ID (required) date: type: string format: date description: Trade date (required) qty: type: number description: Quantity (required) price: type: number description: Price (required) type: type: string enum: - buy - sell description: Trade type (required) security_id: type: string format: uuid description: Security ID (one of security_id, ticker, manual_ticker required) ticker: type: string description: Ticker symbol manual_ticker: type: string description: Manual ticker for offline securities currency: type: string description: Currency (defaults to account currency) investment_activity_label: type: string description: Activity label (e.g. Buy, Sell) category_id: type: string format: uuid description: Category ID required: - account_id - date - qty - price - type required: - trade required: true "/api/v1/trades/{id}": parameters: - name: id in: path required: true description: Trade ID schema: type: string get: summary: Retrieve trade tags: - Trades security: - apiKeyAuth: [] responses: '200': description: trade retrieved content: application/json: schema: "$ref": "#/components/schemas/Trade" '401': description: unauthorized content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" '404': description: trade not found content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" patch: summary: Update trade tags: - Trades security: - apiKeyAuth: [] parameters: [] responses: '200': description: trade updated content: application/json: schema: "$ref": "#/components/schemas/Trade" '404': description: trade not found content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" requestBody: content: application/json: schema: type: object properties: trade: type: object properties: date: type: string format: date qty: type: number price: type: number type: type: string enum: - buy - sell nature: type: string enum: - inflow - outflow name: type: string notes: type: string currency: type: string investment_activity_label: type: string category_id: type: string format: uuid required: true delete: summary: Delete trade tags: - Trades security: - apiKeyAuth: [] responses: '200': description: trade deleted content: application/json: schema: "$ref": "#/components/schemas/DeleteResponse" '404': description: trade not found content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" "/api/v1/transactions": get: summary: List transactions tags: - Transactions security: - apiKeyAuth: [] parameters: - name: page in: query required: false description: 'Page number (default: 1)' schema: type: integer - name: per_page in: query required: false description: 'Items per page (default: 25, max: 100)' schema: type: integer - name: account_id in: query required: false description: Filter by account ID schema: type: string - name: category_id in: query required: false description: Filter by category ID schema: type: string - name: merchant_id in: query required: false description: Filter by merchant ID schema: type: string - name: start_date in: query required: false description: Filter transactions from this date schema: type: string format: date - name: end_date in: query required: false description: Filter transactions until this date schema: type: string format: date - name: min_amount in: query required: false description: Filter by minimum amount schema: type: number - name: max_amount in: query required: false description: Filter by maximum amount schema: type: number - name: type in: query required: false description: Filter by transaction type schema: type: string enum: - income - expense - name: search in: query required: false description: Search by name, notes, or merchant name schema: type: string - name: account_ids in: query required: false description: Filter by multiple account IDs schema: type: array items: type: string - name: category_ids in: query required: false description: Filter by multiple category IDs schema: type: array items: type: string - name: merchant_ids in: query required: false description: Filter by multiple merchant IDs schema: type: array items: type: string - name: tag_ids in: query required: false description: Filter by tag IDs schema: type: array items: type: string responses: '200': description: transactions filtered by date range content: application/json: schema: "$ref": "#/components/schemas/TransactionCollection" post: summary: Create transaction tags: - Transactions security: - apiKeyAuth: [] parameters: [] responses: '201': description: transaction created content: application/json: schema: "$ref": "#/components/schemas/Transaction" '422': description: validation error - missing required fields content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" requestBody: content: application/json: schema: type: object properties: transaction: type: object properties: account_id: type: string format: uuid description: Account ID (required) date: type: string format: date description: Transaction date amount: type: number description: Transaction amount name: type: string description: Transaction name/description description: type: string description: Alternative to name field notes: type: string description: Additional notes currency: type: string description: Currency code (defaults to family currency) category_id: type: string format: uuid description: Category ID merchant_id: type: string format: uuid description: Merchant ID nature: type: string enum: - income - expense - inflow - outflow description: Transaction nature (determines sign) tag_ids: type: array items: type: string format: uuid description: Array of tag IDs required: - account_id - date - amount - name required: - transaction required: true "/api/v1/transactions/{id}": parameters: - name: id in: path required: true description: Transaction ID schema: type: string get: summary: Retrieve a transaction tags: - Transactions security: - apiKeyAuth: [] responses: '200': description: transaction retrieved content: application/json: schema: "$ref": "#/components/schemas/Transaction" '404': description: transaction not found content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" patch: summary: Update a transaction tags: - Transactions security: - apiKeyAuth: [] parameters: [] responses: '200': description: transaction updated content: application/json: schema: "$ref": "#/components/schemas/Transaction" '404': description: transaction not found content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" requestBody: content: application/json: schema: type: object properties: transaction: type: object properties: date: type: string format: date amount: type: number name: type: string description: type: string description: Alternative to name field notes: type: string currency: type: string description: Currency code category_id: type: string format: uuid merchant_id: type: string format: uuid nature: type: string enum: - income - expense - inflow - outflow tag_ids: type: array items: type: string format: uuid description: Array of tag IDs to assign. Omit to preserve existing tags; use [] to clear all tags. required: true delete: summary: Delete a transaction tags: - Transactions security: - apiKeyAuth: [] responses: '200': description: transaction deleted content: application/json: schema: "$ref": "#/components/schemas/DeleteResponse" '404': description: transaction not found content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" "/api/v1/users/reset": delete: summary: Reset account tags: - Users description: Resets all financial data (accounts, categories, merchants, tags, etc.) for the current user's family while keeping the user account intact. The reset runs asynchronously in the background. The returned job_id is informational only; reset status is family-scoped, not job-scoped. Requires admin role. security: - apiKeyAuth: [] responses: '200': description: account reset initiated content: application/json: schema: "$ref": "#/components/schemas/ResetInitiatedResponse" '401': description: unauthorized content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" '403': description: forbidden - requires read_write scope and admin role content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" '500': description: reset enqueue failed content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" "/api/v1/users/reset/status": get: summary: Retrieve reset status tags: - Users description: Returns counts of family-owned data targeted by account reset. Use this after DELETE /api/v1/users/reset to decide whether reset materialization has completed. Completion is a counts-based family snapshot and may change if new data is created after reset. security: - apiKeyAuth: [] responses: '200': description: reset status returned content: application/json: schema: "$ref": "#/components/schemas/ResetStatusResponse" '401': description: unauthorized content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" '403': description: forbidden - requires admin role content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" "/api/v1/users/me": delete: summary: Delete account tags: - Users description: Permanently deactivates the current user account and all associated data. This action cannot be undone. security: - apiKeyAuth: [] responses: '200': description: account deleted content: application/json: schema: "$ref": "#/components/schemas/SuccessMessage" '401': description: unauthorized '403': description: insufficient scope '422': description: deactivation failed content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" "/api/v1/valuations": get: summary: List valuations tags: - Valuations security: - apiKeyAuth: [] parameters: - name: page in: query required: false description: 'Page number (default: 1)' schema: type: integer - name: per_page in: query required: false description: 'Items per page (default: 25, max: 100)' schema: type: integer - name: account_id in: query required: false description: Filter by account ID schema: type: string format: uuid - name: start_date in: query required: false description: Filter valuations from this date schema: type: string format: date - name: end_date in: query required: false description: Filter valuations until this date schema: type: string format: date responses: '200': description: valuations listed content: application/json: schema: "$ref": "#/components/schemas/ValuationCollection" '401': description: unauthorized content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" '422': description: invalid account filter content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" post: summary: Create valuation tags: - Valuations security: - apiKeyAuth: [] parameters: [] responses: '201': description: valuation created content: application/json: schema: "$ref": "#/components/schemas/Valuation" '200': description: existing valuation upserted content: application/json: schema: "$ref": "#/components/schemas/Valuation" '422': description: validation error - missing date content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" '404': description: account not found content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" requestBody: content: application/json: schema: type: object properties: valuation: type: object properties: account_id: type: string format: uuid description: Account ID (required) amount: type: number description: Valuation amount (required) date: type: string format: date description: Valuation date (required) notes: type: string description: Additional notes upsert: type: boolean description: Nested alternative to the top-level response-status flag. Top-level upsert takes precedence when both are provided. required: - account_id - amount - date upsert: type: boolean description: Response-status signal only. When true and a same-account same-date valuation exists before the request, the endpoint returns 200 OK instead of 201 Created. The underlying reconciliation write path is unchanged; this flag does not add duplicate-prevention or safe-retry guarantees beyond existing same-date reconciliation behavior. required: - valuation required: true "/api/v1/valuations/{id}": parameters: - name: id in: path required: true description: Valuation ID (entry ID) schema: type: string get: summary: Retrieve a valuation tags: - Valuations security: - apiKeyAuth: [] responses: '200': description: valuation retrieved content: application/json: schema: "$ref": "#/components/schemas/Valuation" '404': description: valuation not found content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" patch: summary: Update a valuation tags: - Valuations security: - apiKeyAuth: [] parameters: [] responses: '200': description: valuation updated with amount and date content: application/json: schema: "$ref": "#/components/schemas/Valuation" '422': description: validation error - only one of amount/date provided content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" '404': description: valuation not found content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" requestBody: content: application/json: schema: type: object properties: valuation: type: object properties: amount: type: number description: New valuation amount (must provide with date) date: type: string format: date description: New valuation date (must provide with amount) notes: type: string description: Additional notes required: true