--- 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 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.) 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 AccountDetail: type: object required: - id - name - balance - currency - classification - account_type properties: id: type: string format: uuid name: type: string balance: type: string currency: type: string classification: type: string account_type: type: string AccountCollection: type: object required: - accounts - pagination properties: accounts: type: array items: "$ref": "#/components/schemas/AccountDetail" pagination: "$ref": "#/components/schemas/Pagination" Category: type: object required: - id - name - classification - color - icon properties: id: type: string format: uuid name: type: string classification: 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 - classification - color - icon - subcategories_count - created_at - updated_at properties: id: type: string format: uuid name: type: string classification: type: string enum: - income - expense 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" 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 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 DeleteResponse: type: object required: - message properties: message: type: string SuccessMessage: 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 properties: rows_count: type: integer minimum: 0 valid_rows_count: type: integer minimum: 0 nullable: true ImportSummary: type: object required: - id - type - status - created_at - updated_at properties: id: type: string format: uuid type: type: string enum: - TransactionImport - TradeImport - AccountImport - MintImport - CategoryImport - RuleImport 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 ImportDetail: type: object required: - id - type - status - created_at - updated_at properties: id: type: string format: uuid type: type: string enum: - TransactionImport - TradeImport - AccountImport - MintImport - CategoryImport - RuleImport 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 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" paths: "/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/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 responses: '200': description: accounts paginated content: application/json: schema: "$ref": "#/components/schemas/AccountCollection" "/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/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" "/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: classification in: query required: false description: Filter by classification (income or expense) schema: type: string enum: - income - expense - 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/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 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. 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: "$ref": "#/components/schemas/ErrorResponse" requestBody: content: application/json: schema: type: object properties: raw_file_content: type: string description: The raw CSV content as a string type: type: string enum: - TransactionImport - TradeImport - AccountImport - MintImport - CategoryImport - RuleImport 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: Header name for the date column amount_col_label: type: string description: Header name for the amount column name_col_label: type: string description: Header name for the transaction name column category_col_label: type: string description: Header name for the category column tags_col_label: type: string description: Header name for the tags column notes_col_label: type: string description: Header name for the notes column date_format: type: string description: 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: Number format for parsing amounts signage_convention: type: string enum: - inflows_positive - inflows_negative description: How to interpret positive/negative amounts col_sep: type: string enum: - "," - ";" description: Column separator required: true "/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/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/valuations": post: summary: Create valuation tags: - Valuations security: - apiKeyAuth: [] parameters: - name: Authorization in: header required: true schema: type: string description: Bearer token with write scope responses: '201': description: valuation created 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 required: - account_id - amount - date required: - valuation required: true "/api/v1/valuations/{id}": parameters: - name: Authorization in: header required: true schema: type: string description: Bearer token - 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 "/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. Requires admin role. security: - apiKeyAuth: [] responses: '200': description: account reset initiated content: application/json: schema: "$ref": "#/components/schemas/SuccessMessage" '401': description: unauthorized content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" '403': description: "forbidden \u2014 requires read_write scope and 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 content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" '403': description: insufficient scope content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse" '422': description: deactivation failed content: application/json: schema: "$ref": "#/components/schemas/ErrorResponse"