{"info":{"description":"Cobalt financial platform API","title":"Cobalt API","version":"0.1.0"},"openapi":"3.1.0","components":{"schemas":{"BankAccountsResponse":{"type":"object","properties":{"accounts":{"type":"array","items":{"$ref":"#/components/schemas/BankAccountListItem"}}},"required":["accounts"]},"BankAccountListItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"mask":{"type":["string","null"]},"name":{"type":"string"},"source":{"type":"string","enum":["plaid","snaptrade","manual"]},"subtype":{"type":["string","null"]},"type":{"type":"string"},"plaidItemId":{"type":["string","null"]},"currency":{"type":["string","null"]},"current":{"type":["number","null"]},"updatedAt":{"type":["string","null"]},"userOverrideCreditLimit":{"type":["number","null"]},"institutionName":{"type":["string","null"]},"newAccountsAvailable":{"type":"boolean"},"logo":{"type":["string","null"]},"url":{"type":["string","null"]},"canAddInvestments":{"type":"boolean"},"creditLimit":{"type":["number","null"]},"hasInvestments":{"type":"boolean"},"hasLiabilities":{"type":"boolean"},"needsReauth":{"type":"boolean"},"pendingDisconnectAt":{"type":["string","null"]}},"required":["id","mask","name","source","subtype","type","plaidItemId","currency","current","updatedAt","userOverrideCreditLimit","institutionName","newAccountsAvailable","logo","url","canAddInvestments","creditLimit","hasInvestments","hasLiabilities","needsReauth","pendingDisconnectAt"]},"ErrorResponseWithCode":{"type":"object","properties":{"code":{"type":"string"},"error":{"type":"string"}},"required":["code","error"]},"ValidationError":{"type":"object","properties":{"error":{"type":"object","properties":{"issues":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"path":{"type":"array","items":{"anyOf":[{"type":"string"},{"type":"number"}]}}},"required":["code","message","path"]}},"name":{"type":"string"}},"required":["issues","name"]},"success":{"type":"boolean"}},"required":["error","success"]},"DisconnectBankAccountResponse":{"type":"object","properties":{"message":{"type":"string"},"success":{"type":"boolean"}},"required":["message","success"]},"PlaidItemsResponse":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/PlaidItem"}}},"required":["items"]},"PlaidItem":{"type":"object","properties":{"availableProducts":{"type":["array","null"],"items":{"type":"string"}},"billedProducts":{"type":["array","null"],"items":{"type":"string"}},"error":{},"id":{"type":"string","format":"uuid"},"institutionId":{"type":["string","null"]},"institutionLogo":{"type":["string","null"]},"institutionName":{"type":["string","null"]},"newAccountsAvailable":{"type":"boolean"},"plaidItemId":{"type":"string"},"userId":{"type":"string"},"createdAt":{"type":"string"},"pendingDisconnectAt":{"type":["string","null"]},"updatedAt":{"type":"string"}},"required":["availableProducts","billedProducts","id","institutionId","institutionLogo","institutionName","newAccountsAvailable","plaidItemId","userId","createdAt","pendingDisconnectAt","updatedAt"]},"PlaidItemAlertsResponse":{"type":"object","properties":{"alerts":{"type":"array","items":{"$ref":"#/components/schemas/PlaidItemAlert"}}},"required":["alerts"]},"PlaidItemAlert":{"type":"object","properties":{"institutionLogo":{"type":["string","null"]},"institutionName":{"type":"string"},"needsReauth":{"type":"boolean"},"newAccountsAvailable":{"type":"boolean"},"pendingDisconnectAt":{"type":["string","null"]},"plaidItemId":{"type":"string"}},"required":["institutionLogo","institutionName","needsReauth","newAccountsAvailable","pendingDisconnectAt","plaidItemId"]},"PlaidAccountsForItemResponse":{"type":"object","properties":{"accounts":{"type":"array","items":{"$ref":"#/components/schemas/PlaidAccount"}}},"required":["accounts"]},"PlaidAccount":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"mask":{"type":["string","null"]},"name":{"type":"string"},"officialName":{"type":["string","null"]},"subtype":{"type":["string","null"]},"type":{"type":"string"},"createdAt":{"type":"string"},"plaidAccountId":{"type":["string","null"]},"plaidItemId":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","mask","name","officialName","subtype","type","createdAt","plaidAccountId","plaidItemId","updatedAt"]},"BrokerageAccountListItem":{"type":"object","properties":{"accountDetails":{"type":["object","null"],"properties":{"balance":{"type":["string","null"]},"id":{"type":"string"},"lastSync":{"type":["string","null"]}},"required":["balance","id","lastSync"]},"accountStatus":{"type":["string","null"]},"accountType":{"type":["string","null"]},"balances":{"type":"array","items":{"type":"object","properties":{"buyingPower":{"type":["string","null"]},"cash":{"type":["string","null"]},"currencyCode":{"type":["string","null"]},"currencyName":{"type":["string","null"]},"id":{"type":"string"},"lastSync":{"type":["string","null"]}},"required":["buyingPower","cash","currencyCode","currencyName","id","lastSync"]}},"id":{"type":"string"},"institutionName":{"type":["string","null"]},"name":{"type":["string","null"]},"needsReauth":{"type":"boolean","description":"True when the SnapTrade authorization backing this account is disabled and requires user reconnect via `generateConnectionPortal` with `reconnectAuthorizationId`. Always false for non-SnapTrade accounts."},"snaptradeAuthorizationId":{"type":["string","null"],"description":"SnapTrade authorization id for reconnect flows. Null for Plaid / manual accounts. Pass as `reconnectAuthorizationId` to `generateConnectionPortal` when `needsReauth` is true."},"plaidAccountId":{"type":"string"},"source":{"$ref":"#/components/schemas/BrokerageAccountSource"}},"required":["accountDetails","accountStatus","accountType","balances","id","institutionName","name","needsReauth","snaptradeAuthorizationId","source"]},"BrokerageAccountSource":{"type":"string","enum":["plaid","snaptrade","manual"],"description":"Origin of the account: SnapTrade, Plaid, or manual."},"SuccessResponse":{"type":"object","properties":{"message":{"type":"string"},"success":{"type":"boolean"}},"required":["success"]},"CreateManualAccountResponse":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]},"PatchManualBalance":{"type":"object","properties":{"current":{"type":"number","description":"New signed balance to write to balance.current. Stored verbatim; caller decides sign. Up to 4 decimal places."}},"required":["current"]},"BankAccount":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"mask":{"type":["string","null"]},"name":{"type":"string"},"source":{"type":"string","enum":["plaid","snaptrade","manual"]},"subtype":{"type":["string","null"]},"type":{"type":"string"},"plaidItemId":{"type":["string","null"]},"currency":{"type":["string","null"]},"available":{"type":["number","null"]},"creditLimit":{"type":["number","null"]},"current":{"type":["number","null"]},"updatedAt":{"type":["string","null"]},"userOverrideCreditLimit":{"type":["number","null"]},"billedProducts":{"type":["array","null"],"items":{"type":"string"}},"error":{},"institutionId":{"type":["string","null"]},"institutionName":{"type":["string","null"]},"newAccountsAvailable":{"type":"boolean"},"logo":{"type":["string","null"]},"url":{"type":["string","null"]},"hasInvestmentAccounts":{"type":"boolean"},"pendingDisconnectAt":{"type":["string","null"]}},"required":["id","mask","name","source","subtype","type","plaidItemId","currency","available","creditLimit","current","updatedAt","userOverrideCreditLimit","billedProducts","institutionId","institutionName","newAccountsAvailable","logo","url","hasInvestmentAccounts","pendingDisconnectAt"]},"PatchAccount":{"type":"object","properties":{"creditLimit":{"type":["number","null"],"exclusiveMinimum":0,"description":"User-override credit limit (positive magnitude). Null clears the override; omit to leave unchanged."}}},"TransactionsResponse":{"type":"object","properties":{"hasMore":{"type":"boolean"},"nextCursor":{"type":["string","null"]},"transactions":{"type":"array","items":{"$ref":"#/components/schemas/TransactionResponse"}}},"required":["hasMore","nextCursor","transactions"]},"TransactionResponse":{"type":"object","properties":{"accountId":{"type":"string","format":"uuid"},"accountLogoDomain":{"type":["string","null"]},"accountName":{"type":"string"},"accountSubtype":{"type":["string","null"]},"accountType":{"type":"string"},"amount":{"type":"number"},"authorizedDate":{"type":["string","null"]},"category":{"type":["object","null"],"properties":{"groupName":{"type":"string"},"groupSystemKey":{"type":["string","null"]},"iconKey":{"type":"string"},"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"systemKey":{"type":["string","null"]}},"required":["groupName","groupSystemKey","iconKey","id","name","systemKey"]},"counterparties":{"type":["array","null"],"items":{"type":"object","properties":{"account_numbers":{"type":["object","null"],"properties":{"bacs":{"type":["object","null"],"properties":{"account":{"type":["string","null"]},"sort_code":{"type":["string","null"]}}},"international":{"type":["object","null"],"properties":{"bic":{"type":["string","null"]},"iban":{"type":["object","null"],"properties":{},"additionalProperties":false}}}}},"confidence_level":{"type":["string","null"]},"entity_id":{"type":["string","null"]},"logo_url":{"type":["string","null"]},"name":{"type":"string"},"type":{"type":"string","enum":["merchant","financial_institution","payment_app","marketplace","payment_terminal","income_source"]},"website":{"type":["string","null"]}},"required":["logo_url","name","type","website"]}},"date":{"type":"string"},"id":{"type":"string","format":"uuid"},"institutionLogo":{"type":["string","null"]},"institutionName":{"type":["string","null"]},"institutionUrl":{"type":["string","null"]},"location":{"$ref":"#/components/schemas/TransactionLocation"},"lockedFields":{"$ref":"#/components/schemas/TransactionLockedFields"},"logoUrl":{"type":["string","null"]},"merchantName":{"type":["string","null"]},"name":{"type":"string"},"notes":{"type":["string","null"],"maxLength":100000},"paymentChannel":{"type":["string","null"],"enum":["in store","online","other",null]},"pending":{"type":"boolean"},"plaidAccountId":{"type":["string","null"]},"source":{"type":"string","enum":["plaid","manual"]},"tagIds":{"type":"array","items":{"type":"string","format":"uuid"}},"website":{"type":["string","null"]}},"required":["accountId","accountLogoDomain","accountName","accountSubtype","accountType","amount","authorizedDate","category","counterparties","date","id","institutionLogo","institutionName","institutionUrl","location","lockedFields","logoUrl","merchantName","name","notes","paymentChannel","pending","plaidAccountId","source","tagIds","website"]},"TransactionLocation":{"type":["object","null"],"properties":{"address":{"type":["string","null"]},"city":{"type":["string","null"]},"country":{"type":["string","null"]},"lat":{"type":["number","null"]},"lon":{"type":["number","null"]},"postal_code":{"type":["string","null"]},"region":{"type":["string","null"]},"store_number":{"type":["string","null"]}},"required":["address","city","country","lat","lon","postal_code","region","store_number"]},"TransactionLockedFields":{"type":"array","items":{"type":"string","enum":["amount","category","date","location","merchantName","name","notes","paymentChannel","tags"]}},"RecurringStreamsResponse":{"type":"object","properties":{"streams":{"type":"array","items":{"$ref":"#/components/schemas/RecurringStreamResponse"}}},"required":["streams"]},"RecurringStreamResponse":{"type":"object","properties":{"accountId":{"type":"string"},"accountName":{"type":"string"},"accountSubtype":{"type":["string","null"]},"accountType":{"type":"string"},"averageAmount":{"type":"number"},"category":{"type":["object","null"],"properties":{"groupName":{"type":"string"},"groupSystemKey":{"type":["string","null"]},"iconKey":{"type":"string"},"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"systemKey":{"type":["string","null"]}},"required":["groupName","groupSystemKey","iconKey","id","name","systemKey"]},"description":{"type":"string"},"firstDate":{"type":"string"},"frequency":{"type":"string"},"id":{"type":"string","format":"uuid"},"institutionLogo":{"type":["string","null"]},"institutionName":{"type":["string","null"]},"institutionUrl":{"type":["string","null"]},"isActive":{"type":"boolean"},"lastAmount":{"type":"number"},"lastDate":{"type":"string"},"merchantName":{"type":["string","null"]},"predictedNextDate":{"type":["string","null"]},"status":{"type":"string"},"streamId":{"type":["string","null"]},"streamType":{"type":"string","enum":["inflow","outflow"]},"transactionIds":{"type":"array","items":{"type":"string"}},"updatedAt":{"type":["string","null"]}},"required":["accountId","accountName","accountSubtype","accountType","averageAmount","category","description","firstDate","frequency","id","institutionLogo","institutionName","institutionUrl","isActive","lastAmount","lastDate","merchantName","predictedNextDate","status","streamId","streamType","transactionIds","updatedAt"]},"CreditSpendingResponse":{"type":"object","properties":{"averageLabel":{"type":"string","enum":["daily","weekly","monthly","yearly"]},"averageSpending":{"type":"number"},"spending":{"type":"array","items":{"$ref":"#/components/schemas/SpendingBucket"}},"totalSpending":{"type":"number"}},"required":["averageLabel","averageSpending","spending","totalSpending"]},"SpendingBucket":{"type":"object","properties":{"amount":{"type":"number"},"date":{"type":"string"}},"required":["amount","date"]},"TransactionTagsResponse":{"type":"object","properties":{"tags":{"type":"array","items":{"$ref":"#/components/schemas/Tag"}}},"required":["tags"]},"Tag":{"type":"object","properties":{"archivedAt":{"type":["string","null"]},"color":{"$ref":"#/components/schemas/TagColor"},"createdAt":{"type":"string"},"id":{"type":"string","format":"uuid"},"name":{"type":"string","minLength":1,"maxLength":50},"updatedAt":{"type":"string"}},"required":["archivedAt","color","createdAt","id","name","updatedAt"]},"TagColor":{"type":"string","enum":["red","orange","amber","yellow","lime","green","teal","cyan","blue","indigo","violet","purple","pink","rose","slate","stone"]},"TransactionActivityResponse":{"type":"object","properties":{"events":{"type":"array","items":{"$ref":"#/components/schemas/TransactionActivityEvent"}}},"required":["events"]},"TransactionActivityEvent":{"type":"object","properties":{"actor":{"type":"string","enum":["user","system"]},"createdAt":{"type":"string"},"field":{"type":"string","enum":["amount","category","date","location","merchantName","name","notes","paymentChannel","tags"]},"id":{"type":"string"},"newValue":{},"oldValue":{}},"required":["actor","createdAt","field","id"]},"TagsResponse":{"type":"object","properties":{"tags":{"type":"array","items":{"$ref":"#/components/schemas/TagWithUsage"}}},"required":["tags"]},"TagWithUsage":{"allOf":[{"$ref":"#/components/schemas/Tag"},{"type":"object","properties":{"usageCount":{"type":"integer","minimum":0}},"required":["usageCount"]}]},"Category":{"type":"object","properties":{"createdAt":{"type":"string"},"excludeFromInsights":{"type":"boolean"},"groupId":{"type":"string","format":"uuid"},"hidden":{"type":"boolean"},"iconKey":{"type":"string"},"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"order":{"type":"integer","minimum":0},"systemKey":{"type":["string","null"]},"updatedAt":{"type":"string"}},"required":["createdAt","excludeFromInsights","groupId","hidden","iconKey","id","name","order","systemKey","updatedAt"]},"CategoryGroup":{"type":"object","properties":{"createdAt":{"type":"string"},"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"order":{"type":"integer","minimum":0},"systemKey":{"type":["string","null"]},"updatedAt":{"type":"string"}},"required":["createdAt","id","name","order","systemKey","updatedAt"]},"BrokerageOverview":{"type":"object","properties":{"balances":{"type":"array","items":{"$ref":"#/components/schemas/BrokerageBalance"}},"balancesByAccount":{"type":"object","additionalProperties":{"type":"array","items":{"$ref":"#/components/schemas/BrokerageBalance"}}},"activities":{"type":"array","items":{"$ref":"#/components/schemas/BrokerageActivity"}},"activitiesByAccount":{"type":"object","additionalProperties":{"type":"array","items":{"$ref":"#/components/schemas/BrokerageActivity"}}},"positions":{"type":"array","items":{"$ref":"#/components/schemas/BrokeragePosition"}},"positionsByAccount":{"type":"object","additionalProperties":{"type":"array","items":{"$ref":"#/components/schemas/BrokeragePosition"}}},"accounts":{"type":"array","items":{"$ref":"#/components/schemas/BrokerageAccount"}},"portfolioSnapshots":{"type":"array","items":{"$ref":"#/components/schemas/PortfolioSnapshot"}},"userBrokerages":{"type":"array","items":{"type":"string"}}},"required":["balances","balancesByAccount","activities","activitiesByAccount","positions","positionsByAccount","accounts","portfolioSnapshots","userBrokerages"]},"BrokerageBalance":{"type":"object","properties":{"accountId":{"type":"string"},"buyingPower":{"type":["string","null"]},"cash":{"type":["string","null"]},"createdAt":{"type":["string","null"]},"currencyCode":{"type":["string","null"]},"currencyName":{"type":["string","null"]},"id":{"type":"string"},"lastSync":{"type":["string","null"]},"snapTradeAccountId":{"type":["string","null"]},"updatedAt":{"type":["string","null"]},"userId":{"type":"string"}},"required":["accountId","buyingPower","cash","createdAt","currencyCode","currencyName","id","lastSync","snapTradeAccountId","updatedAt","userId"]},"BrokerageActivity":{"type":"object","properties":{"accountId":{"type":"string"},"activityId":{"type":["string","null"]},"amount":{"type":["string","null"]},"createdAt":{"type":["string","null"]},"currencyCode":{"type":["string","null"]},"currencyId":{"type":["string","null"]},"currencyName":{"type":["string","null"]},"description":{"type":["string","null"]},"exchangeCode":{"type":["string","null"]},"exchangeId":{"type":["string","null"]},"exchangeMicCode":{"type":["string","null"]},"exchangeName":{"type":["string","null"]},"externalReferenceId":{"type":["string","null"]},"fee":{"type":["string","null"]},"figiCode":{"type":["string","null"]},"fxRate":{"type":["string","null"]},"id":{"type":"string"},"institution":{"type":["string","null"]},"lastSync":{"type":["string","null"]},"optionSymbol":{},"optionType":{"type":["string","null"]},"pagination":{},"price":{"type":["string","null"]},"rawSymbol":{"type":["string","null"]},"securityTypeCode":{"type":["string","null"]},"securityTypeDescription":{"type":["string","null"]},"securityTypeId":{"type":["string","null"]},"settlementDate":{"type":["string","null"]},"snapTradeAccountId":{"type":["string","null"]},"symbol":{},"symbolDescription":{"type":["string","null"]},"symbolId":{"type":["string","null"]},"symbolTicker":{"type":["string","null"]},"tradeDate":{"type":["string","null"]},"type":{"type":["string","null"]},"units":{"type":["string","null"]},"updatedAt":{"type":["string","null"]},"userId":{"type":"string"}},"required":["accountId","activityId","amount","createdAt","currencyCode","currencyId","currencyName","description","exchangeCode","exchangeId","exchangeMicCode","exchangeName","externalReferenceId","fee","figiCode","fxRate","id","institution","lastSync","optionType","price","rawSymbol","securityTypeCode","securityTypeDescription","securityTypeId","settlementDate","snapTradeAccountId","symbolDescription","symbolId","symbolTicker","tradeDate","type","units","updatedAt","userId"]},"BrokeragePosition":{"type":"object","properties":{"accountId":{"type":"string"},"averagePurchasePrice":{"type":["string","null"]},"createdAt":{"type":["string","null"]},"currencyCode":{"type":["string","null"]},"currencyId":{"type":["string","null"]},"currencyName":{"type":["string","null"]},"exchangeCode":{"type":["string","null"]},"exchangeId":{"type":["string","null"]},"exchangeMicCode":{"type":["string","null"]},"exchangeName":{"type":["string","null"]},"figiCode":{"type":["string","null"]},"id":{"type":"string"},"isQuotable":{"type":["boolean","null"]},"isTradable":{"type":["boolean","null"]},"lastSync":{"type":["string","null"]},"localId":{"type":["string","null"]},"openPnl":{"type":["string","null"]},"price":{"type":["string","null"]},"rawSymbol":{"type":["string","null"]},"securityTypeCode":{"type":["string","null"]},"securityTypeDescription":{"type":["string","null"]},"securityTypeId":{"type":["string","null"]},"snapTradeAccountId":{"type":["string","null"]},"symbol":{"type":["string","null"]},"symbolDescription":{"type":["string","null"]},"symbolId":{"type":["string","null"]},"units":{"type":["string","null"]},"updatedAt":{"type":["string","null"]},"userId":{"type":"string"}},"required":["accountId","averagePurchasePrice","createdAt","currencyCode","currencyId","currencyName","exchangeCode","exchangeId","exchangeMicCode","exchangeName","figiCode","id","isQuotable","isTradable","lastSync","localId","openPnl","price","rawSymbol","securityTypeCode","securityTypeDescription","securityTypeId","snapTradeAccountId","symbol","symbolDescription","symbolId","units","updatedAt","userId"]},"BrokerageAccount":{"type":"object","properties":{"accountDetails":{"type":["object","null"],"properties":{"balance":{"type":["string","null"]},"id":{"type":"string"},"lastSync":{"type":["string","null"]}},"required":["balance","id","lastSync"]},"accountStatus":{"type":["string","null"]},"accountType":{"type":["string","null"]},"balanceData":{},"balances":{"type":"array","items":{"type":"object","properties":{"buyingPower":{"type":["string","null"]},"cash":{"type":["string","null"]},"currencyCode":{"type":["string","null"]},"currencyName":{"type":["string","null"]},"id":{"type":"string"},"lastSync":{"type":["string","null"]}},"required":["buyingPower","cash","currencyCode","currencyName","id","lastSync"]}},"cashRestrictions":{},"createdDate":{"type":"string"},"id":{"type":"string"},"institutionName":{"type":["string","null"]},"name":{"type":["string","null"]},"needsReauth":{"type":"boolean","description":"True when the SnapTrade authorization backing this account is disabled and requires user reconnect via `generateConnectionPortal` with `reconnectAuthorizationId`. Always false for non-SnapTrade accounts."},"snaptradeAuthorizationId":{"type":["string","null"],"description":"SnapTrade authorization id for reconnect flows. Null for Plaid / manual accounts. Pass as `reconnectAuthorizationId` to `generateConnectionPortal` when `needsReauth` is true."},"source":{"$ref":"#/components/schemas/BrokerageAccountSource"},"userId":{"type":"string"}},"required":["accountDetails","accountStatus","accountType","balances","createdDate","id","institutionName","name","needsReauth","snaptradeAuthorizationId","source","userId"]},"PortfolioSnapshot":{"type":"object","properties":{"accountId":{"type":"string"},"id":{"type":"string"},"snapshotDate":{"type":"string"},"value":{"type":"number"}},"required":["accountId","id","snapshotDate","value"]},"ManualHolding":{"type":"object","properties":{"accountId":{"type":"string","format":"uuid"},"averagePrice":{"type":["string","null"]},"costBasis":{"type":["string","null"]},"currency":{"type":["string","null"]},"id":{"type":"string","format":"uuid"},"institutionPrice":{"type":["string","null"]},"institutionPriceAsOf":{"type":["string","null"]},"institutionValue":{"type":["string","null"]},"quantity":{"type":"string"},"securityId":{"type":"string","format":"uuid"},"ticker":{"type":["string","null"]},"updatedAt":{"type":"string"}},"required":["accountId","averagePrice","costBasis","currency","id","institutionPrice","institutionPriceAsOf","institutionValue","quantity","securityId","ticker","updatedAt"]},"CreateManualHolding":{"type":"object","properties":{"accountId":{"type":"string"},"costBasis":{"type":"number","minimum":0},"currency":{"type":"string","minLength":3,"maxLength":3,"example":"USD"},"institutionPrice":{"type":"number","minimum":0},"institutionPriceAsOf":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"name":{"type":"string","minLength":1,"maxLength":255},"quantity":{"type":"number","exclusiveMinimum":0},"ticker":{"type":"string","minLength":1,"maxLength":32}},"required":["accountId","institutionPrice","quantity","ticker"]},"PatchManualHolding":{"type":"object","properties":{"costBasis":{"type":["number","null"],"minimum":0},"institutionPrice":{"type":"number","minimum":0},"institutionPriceAsOf":{"type":["string","null"],"pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"quantity":{"type":"number","exclusiveMinimum":0}}},"SellManualHolding":{"type":"object","properties":{"holdingId":{"type":"string","format":"uuid"},"sellPrice":{"type":"number","minimum":0},"sellQuantity":{"type":"number","exclusiveMinimum":0},"soldAt":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"}},"required":["holdingId","sellPrice","sellQuantity"]},"SetManualCashSleeve":{"type":"object","properties":{"accountId":{"type":"string","format":"uuid"},"amount":{"type":"number","minimum":0}},"required":["accountId","amount"]},"Chat":{"type":"object","properties":{"createdAt":{"type":"string"},"title":{"type":"string"},"updatedAt":{"type":"string"},"id":{"type":"string"}},"required":["createdAt","title","updatedAt","id"]},"ChatDetail":{"type":"object","properties":{"id":{"type":"string","minLength":1},"messages":{"type":"array","items":{"$ref":"#/components/schemas/ChatMessage"}},"votes":{"type":"object","additionalProperties":{"type":"string"}}},"required":["id","messages","votes"]},"ChatMessage":{"type":"object","properties":{"role":{"type":"string"},"id":{"type":"string"},"parts":{"type":"array","items":{"type":"object","additionalProperties":{}}}},"required":["role","id","parts"]},"CreateChatResponse":{"type":"object","properties":{"chatId":{"type":"string","minLength":1}},"required":["chatId"]},"Quote":{"type":"object","properties":{"change":{"type":"number"},"changePercent":{"type":"number"},"companyName":{"type":"string"},"currentPrice":{"type":"number"}},"required":["change","changePercent","companyName","currentPrice"]},"Overview":{"type":"object","properties":{"beta":{"type":["number","null"]},"ceo":{"type":["string","null"]},"companyName":{"type":"string"},"country":{"type":["string","null"]},"currency":{"type":["string","null"]},"description":{"type":["string","null"]},"dividendYield":{"type":["number","null"]},"exchange":{"type":["string","null"]},"fullTimeEmployees":{"type":["number","null"]},"industry":{"type":["string","null"]},"ipoDate":{"type":["string","null"]},"marketCap":{"type":["number","null"]},"pe":{"type":["number","null"]},"price":{"type":["number","null"]},"revenue":{"type":["number","null"]},"sector":{"type":["string","null"]},"symbol":{"type":"string"},"website":{"type":["string","null"]}},"required":["beta","ceo","companyName","country","currency","description","dividendYield","exchange","fullTimeEmployees","industry","ipoDate","marketCap","pe","price","revenue","sector","symbol","website"],"description":"Normalized FMP company profile (stable `/profile` + optional P/E and revenue enrichment)."},"ChartResponse":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ChartPoint"}}},"required":["data"]},"ChartPoint":{"type":"object","properties":{"close":{"type":"number"},"high":{"type":"number"},"id":{"type":"string"},"low":{"type":"number"},"open":{"type":"number"},"price":{"type":"number"},"time":{"type":"string","format":"date-time","example":"2025-05-13T14:30:00.000Z"},"volume":{"type":"number"}},"required":["price","time","volume"]},"ResearchNewsResponse":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ResearchArticle"}},"total_items":{"type":"number"},"total_pages":{"type":"number"}},"required":["data","total_items","total_pages"]},"ResearchArticle":{"type":"object","properties":{"date":{"type":"string"},"image_url":{"type":"string"},"news_url":{"type":"string"},"sentiment":{"type":"string"},"source_name":{"type":"string"},"text":{"type":"string"},"tickers":{"type":"array","items":{"type":"string"}},"title":{"type":"string"},"topics":{"type":"array","items":{"type":"string"}},"type":{"type":"string"}},"required":["date","news_url","sentiment","source_name","text","tickers","title","topics","type"]},"TickersSearchResponse":{"type":"object","properties":{"count":{"type":"number"},"tickers":{"type":"array","items":{"$ref":"#/components/schemas/TickerSearchResult"}}},"required":["count","tickers"]},"TickerSearchResult":{"type":"object","properties":{"active":{"type":"boolean"},"currency":{"type":"string"},"name":{"type":"string"},"region":{"type":"string"},"symbol":{"type":"string"},"type":{"type":"string"}},"required":["active","currency","name","region","symbol","type"]},"TickerHistoryResponse":{"type":"object","properties":{"points":{"type":"array","items":{"$ref":"#/components/schemas/TickerHistoryPoint"}},"requested":{"type":"string"},"symbol":{"type":"string"}},"required":["points","requested","symbol"]},"TickerHistoryPoint":{"type":"object","properties":{"close":{"type":"number"},"date":{"type":"string"},"high":{"type":"number"},"low":{"type":"number"}},"required":["close","date","high","low"]},"ScreenerRow":{"type":"object","properties":{"beta":{"type":["number","null"]},"companyName":{"type":["string","null"]},"country":{"type":["string","null"]},"exchange":{"type":["string","null"]},"exchangeShortName":{"type":["string","null"]},"industry":{"type":["string","null"]},"isActivelyTrading":{"type":["boolean","null"]},"isEtf":{"type":["boolean","null"]},"marketCap":{"type":["number","null"]},"peRatio":{"type":["number","null"]},"price":{"type":["number","null"]},"revenue":{"type":["number","null"]},"sector":{"type":["string","null"]},"symbol":{"type":"string"},"volume":{"type":["number","null"]}},"required":["symbol"],"additionalProperties":{}},"ConnectionPortal":{"type":"object","properties":{"redirectURI":{"type":"string"},"sessionId":{"type":"string"}},"required":["redirectURI"]},"SubscriptionStatus":{"type":"object","properties":{"cancelAtPeriodEnd":{"type":"boolean"},"connectionStates":{"type":"array","items":{"$ref":"#/components/schemas/ConnectionState"}},"hasActiveSubscription":{"type":"boolean"},"periodEnd":{"type":["string","null"]},"status":{"type":["string","null"]},"subscriptionSource":{"type":["string","null"],"enum":["stripe","appstore",null]},"tier":{"type":"string","enum":["free","pro"]}},"required":["cancelAtPeriodEnd","connectionStates","hasActiveSubscription","periodEnd","status","subscriptionSource","tier"]},"ConnectionState":{"type":"object","properties":{"externalId":{"type":"string"},"frozen":{"type":"boolean"},"id":{"type":"string"},"kind":{"type":"string","enum":["plaid","snaptrade"]}},"required":["externalId","frozen","id","kind"]},"BillingPortal":{"type":"object","properties":{"url":{"type":"string"}},"required":["url"]},"ShareSettings":{"type":"object","properties":{"shareAmount":{"type":"boolean"},"shareCard":{"type":"boolean"},"shareDate":{"type":"boolean"},"shareMaxAmountCents":{"type":["integer","null"]},"shareMerchant":{"type":"boolean"},"shareMinAmountCents":{"type":["integer","null"]},"shareNote":{"type":"boolean"}},"required":["shareAmount","shareCard","shareDate","shareMaxAmountCents","shareMerchant","shareMinAmountCents","shareNote"]},"SearchInstitutionsResponse":{"type":"object","properties":{"institutions":{"type":"array","items":{"$ref":"#/components/schemas/Institution"}}},"required":["institutions"]},"Institution":{"type":"object","properties":{"id":{"type":"string"},"logo":{"type":["string","null"]},"name":{"type":"string"},"primary_color":{"type":["string","null"]},"url":{"type":["string","null"]}},"required":["id","logo","name","primary_color","url"]},"InstitutionDb":{"type":"object","properties":{"createdAt":{"type":"string","format":"date-time"},"id":{"type":"string","format":"uuid"},"logo":{"type":["string","null"]},"name":{"type":"string"},"oauth":{"type":"boolean"},"plaidInstitutionId":{"type":"string"},"primaryColor":{"type":["string","null"]},"routingNumbers":{"type":["array","null"],"items":{"type":"string"}},"status":{"type":["string","null"]},"updatedAt":{"type":"string","format":"date-time"},"url":{"type":["string","null"]}},"required":["createdAt","id","logo","name","oauth","plaidInstitutionId","primaryColor","routingNumbers","status","updatedAt","url"]},"InstitutionDetail":{"type":"object","properties":{"id":{"type":"string"},"logo":{"type":["string","null"]},"name":{"type":"string"},"oauth":{"type":"boolean"},"primary_color":{"type":["string","null"]},"routing_numbers":{"type":"array","items":{"type":"string"}},"status":{},"url":{"type":["string","null"]}},"required":["id","logo","name","oauth","primary_color","routing_numbers","url"]},"ImportsListResponse":{"type":"object","properties":{"jobs":{"type":"array","items":{"type":"object","properties":{"createdAt":{"type":"string"},"id":{"type":"string","format":"uuid"},"originalFilename":{"type":["string","null"]},"status":{"type":"string","enum":["uploaded","column_mapped","account_mapped","category_mapped","committing"]}},"required":["createdAt","id","originalFilename","status"]}}},"required":["jobs"]},"ImportStagedRowResponse":{"type":"object","properties":{"amount":{"type":"string"},"date":{"type":"string"},"id":{"type":"string","format":"uuid"},"merchant":{"type":"string"},"notes":{"type":["string","null"]},"originalDescription":{"type":["string","null"]},"parseError":{"type":["string","null"]},"sourceAccountName":{"type":"string"},"sourceCategoryName":{"type":["string","null"]},"tags":{"type":"array","items":{"type":"string"}}},"required":["amount","date","id","merchant","notes","originalDescription","parseError","sourceAccountName","sourceCategoryName","tags"]},"BalanceSnapshotsResponse":{"type":"object","properties":{"snapshots":{"type":"array","items":{"$ref":"#/components/schemas/BalanceSnapshot"}}},"required":["snapshots"]},"BalanceSnapshot":{"type":"object","properties":{"accountId":{"type":"string"},"accountName":{"type":"string"},"accountSubtype":{"type":["string","null"]},"accountType":{"type":"string"},"availableBalance":{"type":["number","null"]},"createdAt":{"type":"string"},"creditLimit":{"type":["number","null"]},"currentBalance":{"type":"number"},"id":{"type":"string","format":"uuid"},"institutionName":{"type":["string","null"]},"plaidAccountId":{"type":["string","null"]},"snapshotDate":{"type":"string"}},"required":["accountId","accountName","accountSubtype","accountType","availableBalance","createdAt","creditLimit","currentBalance","id","institutionName","plaidAccountId","snapshotDate"]}},"parameters":{}},"paths":{"/api/accounts":{"get":{"description":"List the user's bank-shape accounts (depository, credit, loan) across Plaid + manual sources. Use `?type=` to narrow.","summary":"List bank accounts","tags":["Accounts"],"parameters":[{"schema":{"type":"string","enum":["depository","credit","loan"]},"required":false,"name":"type","in":"query"}],"responses":{"200":{"description":"List of bank accounts","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BankAccountsResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"403":{"description":"Subscription required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}}}},"/api/accounts/{id}":{"delete":{"description":"Disconnect an account by `financial_account.id`. Source-agnostic — server dispatches on row.source (plaid/snaptrade/manual). For Plaid, if no accounts remain under the item, also calls Plaid's `/item/remove`.","summary":"Disconnect account","tags":["Accounts"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Account disconnected","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DisconnectBankAccountResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"403":{"description":"Subscription required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"404":{"description":"Account not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"409":{"description":"Provider connection in invalid state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}},"502":{"description":"Upstream provider failure","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}},"get":{"description":"Single bank-shape account by internal id. Works for any source (Plaid, manual, snaptrade); Plaid-only fields are null for non-Plaid rows.","summary":"Get account details","tags":["Accounts"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Account details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BankAccount"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"403":{"description":"Subscription required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"404":{"description":"Account not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}}},"patch":{"description":"Partial update to an account by internal id. Currently supports user-override `creditLimit` (positive magnitude; null clears the override). Source-agnostic.","summary":"Update account","tags":["Accounts"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchAccount"}}}},"responses":{"200":{"description":"Updated account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BankAccount"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"403":{"description":"Subscription required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"404":{"description":"Account not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}}}},"/api/accounts/plaid-items":{"get":{"description":"List the user's Plaid items (bank connections).","summary":"List Plaid items","tags":["Accounts"],"responses":{"200":{"description":"List of Plaid items","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlaidItemsResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"403":{"description":"Subscription required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}}},"/api/accounts/plaid-items/alerts":{"get":{"description":"Plaid items needing reauth or pending disconnect.","summary":"List Plaid item alerts","tags":["Accounts"],"responses":{"200":{"description":"Plaid items needing attention","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlaidItemAlertsResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"403":{"description":"Subscription required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}}},"/api/accounts/items/{itemId}":{"get":{"description":"Accounts under a single Plaid item (bank connection).","summary":"Get accounts for a Plaid item","tags":["Accounts"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"itemId","in":"path"}],"responses":{"200":{"description":"Accounts for Plaid item","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlaidAccountsForItemResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"403":{"description":"Subscription required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"404":{"description":"Plaid item not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}}}},"/api/accounts/brokerage":{"get":{"description":"All brokerage-shaped accounts: SnapTrade, Plaid investment, and manual investment. Inspect `source` on each item to distinguish. SnapTrade-only fields (`snaptradeAuthorizationId`, `needsReauth`) are null/false for non-SnapTrade rows. Use `?source=` to filter (default: `all`).","summary":"List brokerage accounts","tags":["Accounts"],"parameters":[{"schema":{"type":"string","enum":["snaptrade","plaid","manual","all"]},"required":false,"name":"source","in":"query"}],"responses":{"200":{"description":"Brokerage accounts","content":{"application/json":{"schema":{"type":"object","properties":{"accounts":{"type":"array","items":{"$ref":"#/components/schemas/BrokerageAccountListItem"}}},"required":["accounts"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}}},"/api/accounts/manual/seed-snapshot":{"post":{"description":"Force today's balance snapshot upsert for the caller's manual accounts.","summary":"Seed today's snapshot for manual accounts","tags":["Accounts"],"responses":{"200":{"description":"Snapshot seeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"403":{"description":"Subscription required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}}},"/api/accounts/manual":{"post":{"description":"Create a manual (non-bank-linked) account and seed today's balance snapshot. Mirrors the Zero `m.accounts.createAccount` mutator. `subtype` must belong to the chosen `type` vocabulary. `creditLimit` only valid when `type === \"credit\"`. `currentBalance` is signed: positive for assets, negative for liabilities.","summary":"Create manual account","tags":["Accounts"],"requestBody":{"description":"Account to create","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"creditLimit":{"type":"number","exclusiveMinimum":0},"currency":{"type":"string","minLength":3,"maxLength":3,"default":"USD"},"currentBalance":{"type":"number"},"institutionName":{"type":"string","maxLength":255},"logoDomain":{"type":"string","maxLength":253},"name":{"type":"string","minLength":1,"maxLength":255},"subtype":{"type":"string","enum":["credit card","line of credit","checking","savings","cash","brokerage","ira","roth ira","401k","hsa","crypto","mortgage","student","auto","personal"]},"type":{"type":"string","enum":["depository","credit","investment","loan"]}},"required":["currentBalance","name","subtype","type"]}}}},"responses":{"201":{"description":"Created account id","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateManualAccountResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"403":{"description":"Subscription required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}}}},"/api/accounts/{id}/balance":{"patch":{"description":"Set `balance.current` for a manual account directly. Caller-controlled sign — value is written verbatim. Re-snapshots so net-worth views update immediately.","summary":"Patch manual account balance","tags":["Accounts"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"description":"New balance","required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchManualBalance"}}}},"responses":{"200":{"description":"Balance updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"400":{"description":"Account is not manual","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"403":{"description":"Subscription required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"404":{"description":"Account not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}}}},"/api/alerts":{"get":{"summary":"List active user alerts","tags":["Alerts"],"responses":{"200":{"description":"Active alerts for the signed-in user","content":{"application/json":{"schema":{"type":"object","properties":{"alerts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"metadata":{"type":["object","null"],"additionalProperties":{}},"source":{"type":"string"},"sourceId":{"type":["string","null"]},"type":{"type":"string"},"userId":{"type":"string"},"createdAt":{"type":"string"},"message":{"type":"string"},"resolvedAt":{"type":["string","null"]},"title":{"type":"string"}},"required":["id","metadata","source","sourceId","type","userId","createdAt","message","resolvedAt","title"]}}},"required":["alerts"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}}},"/api/transactions":{"get":{"description":"Paginated, filterable list of user transactions","summary":"List transactions","tags":["Transactions"],"parameters":[{"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"array","items":{"type":"string","format":"uuid"}}]},"required":false,"name":"accountId","in":"query"},{"schema":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}]},"required":false,"name":"accountSubtype","in":"query"},{"schema":{"type":"string"},"required":false,"name":"accountType","in":"query"},{"schema":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}]},"required":false,"name":"categoryGroup","in":"query"},{"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"array","items":{"type":"string","format":"uuid"}}]},"required":false,"name":"categoryId","in":"query"},{"schema":{"type":"string"},"required":false,"name":"cursor","in":"query"},{"schema":{"type":"string"},"required":false,"name":"endDate","in":"query"},{"schema":{"type":"number","minimum":1,"maximum":200,"default":50},"required":false,"name":"limit","in":"query"},{"schema":{"type":["number","null"]},"required":false,"name":"maxAmount","in":"query"},{"schema":{"type":["number","null"]},"required":false,"name":"minAmount","in":"query"},{"schema":{"type":"string","enum":["true","false"]},"required":false,"name":"pendingFilter","in":"query"},{"schema":{"type":"string"},"required":false,"name":"searchQuery","in":"query"},{"schema":{"type":"string"},"required":false,"name":"startDate","in":"query"},{"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"array","items":{"type":"string","format":"uuid"}}]},"required":false,"name":"tagId","in":"query"}],"responses":{"200":{"description":"List of transactions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TransactionsResponse"}}}},"400":{"description":"Invalid date range","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"403":{"description":"Subscription required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}}},"post":{"description":"Create a single manual transaction on a user-owned manual account. Server stamps `source: \"manual\"`, `pending: false`, and `userId`. Use `id` to pre-bind the row for optimistic UI flows.","summary":"Create a transaction","tags":["Transactions"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"accountId":{"type":"string","format":"uuid"},"amount":{"type":"number"},"categoryId":{"type":["string","null"],"format":"uuid"},"currency":{"type":["string","null"],"minLength":3,"maxLength":3},"date":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"id":{"type":"string","format":"uuid"},"merchantName":{"type":["string","null"],"minLength":1,"maxLength":255},"name":{"type":"string","minLength":1,"maxLength":255},"notes":{"type":["string","null"],"maxLength":100000},"website":{"type":["string","null"],"minLength":1,"maxLength":2048},"location":{"$ref":"#/components/schemas/TransactionLocation"}},"required":["accountId","amount","date","name"]}}}},"responses":{"200":{"description":"Transaction created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TransactionResponse"}}}},"400":{"description":"Account is not manual","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"403":{"description":"Subscription required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"404":{"description":"Account not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}}}},"/api/transactions/recurring":{"get":{"description":"Active recurring transaction streams (subscriptions, bills, income)","summary":"List recurring transactions","tags":["Transactions"],"responses":{"200":{"description":"Recurring streams","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecurringStreamsResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"403":{"description":"Subscription required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}}},"/api/transactions/spending":{"get":{"description":"Aggregated spending bucketed by period (daily/weekly/monthly). Covers credit and depository outflows; honors per-tx and per-category insight exclusions.","summary":"Spending","tags":["Transactions"],"parameters":[{"schema":{"type":"string"},"required":false,"name":"accountId","in":"query"},{"schema":{"type":"string","enum":["credit","depository","all"],"default":"all"},"required":false,"name":"accountType","in":"query"},{"schema":{"type":"string","enum":["1w","1m","3m","6m","1y","all"]},"required":true,"name":"period","in":"query"}],"responses":{"200":{"description":"Aggregated spending","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreditSpendingResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"403":{"description":"Subscription required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}}}},"/api/transactions/{transactionId}":{"patch":{"description":"Sparse partial update — only fields present in the body are written. Pass `null` to restore the original value (RFC 7396 semantics). Writes to transaction_edit for auditing.","summary":"Update a transaction","tags":["Transactions"],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"transactionId","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"categoryId":{"type":["string","null"],"format":"uuid"},"date":{"type":["string","null"],"pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"location":{"$ref":"#/components/schemas/TransactionLocation"},"merchantName":{"type":["string","null"],"minLength":1,"maxLength":255},"name":{"type":["string","null"],"minLength":1},"notes":{"type":["string","null"],"maxLength":100000},"paymentChannel":{"type":["string","null"],"enum":["in store","online","other",null]},"tags":{"type":"array","items":{"type":"string","format":"uuid"}},"website":{"type":["string","null"],"minLength":3,"maxLength":2048,"pattern":"^[^\\s]+\\.[^\\s]+$"}}}}}},"responses":{"200":{"description":"Transaction updated","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"403":{"description":"Subscription required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"404":{"description":"Transaction, category, or tag not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}}},"delete":{"description":"Delete a manual transaction. Idempotent — no error if already gone or not manual.","summary":"Delete a transaction","tags":["Transactions"],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"transactionId","in":"path"}],"responses":{"200":{"description":"Transaction deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"403":{"description":"Subscription required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}},"get":{"description":"Single transaction with joined account, institution, category, and tag ids.","summary":"Get transaction by id","tags":["Transactions"],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"transactionId","in":"path"}],"responses":{"200":{"description":"Transaction","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TransactionResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"403":{"description":"Subscription required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"404":{"description":"Transaction not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}}}},"/api/transactions/{transactionId}/tags":{"get":{"description":"Returns every tag attached to a transaction.","summary":"List tags on a transaction","tags":["Transactions"],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"transactionId","in":"path"}],"responses":{"200":{"description":"Tags on this transaction","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TransactionTagsResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"403":{"description":"Subscription required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"404":{"description":"Transaction not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}}}},"/api/transactions/{transactionId}/activity":{"get":{"description":"Ordered list of edit events for a transaction (oldest first).","summary":"Get transaction activity","tags":["Transactions"],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"transactionId","in":"path"}],"responses":{"200":{"description":"Transaction activity","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TransactionActivityResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"403":{"description":"Subscription required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"404":{"description":"Transaction not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}}}},"/api/transactions/geocode":{"get":{"description":"Free-text geocoding via OpenStreetMap Nominatim. Returns up to 5 candidates.","summary":"Search for a location by free-text query","tags":["Transactions"],"parameters":[{"schema":{"type":"string","minLength":2,"maxLength":200},"required":true,"name":"q","in":"query"}],"responses":{"200":{"description":"Geocoding candidates","content":{"application/json":{"schema":{"type":"object","properties":{"results":{"type":"array","items":{"type":"object","properties":{"displayName":{"type":"string"},"location":{"$ref":"#/components/schemas/TransactionLocation"}},"required":["displayName","location"]}}},"required":["results"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"403":{"description":"Subscription required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}},"502":{"description":"Geocoder upstream failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}}},"/api/tags":{"get":{"description":"Lists every tag the user owns (active + archived) with usage counts.","summary":"List tags","tags":["Tags"],"responses":{"200":{"description":"User's tags","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TagsResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"403":{"description":"Subscription required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}},"post":{"description":"Creates a new tag scoped to the calling user.","summary":"Create tag","tags":["Tags"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"color":{"$ref":"#/components/schemas/TagColor"},"name":{"type":"string","minLength":1,"maxLength":50}},"required":["color","name"]}}}},"responses":{"201":{"description":"Tag created","content":{"application/json":{"schema":{"type":"object","properties":{"tag":{"$ref":"#/components/schemas/TagWithUsage"}},"required":["tag"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"403":{"description":"Subscription required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}}}},"/api/tags/{tagId}":{"patch":{"description":"Renames, recolors, or archives/unarchives a tag.","summary":"Update tag","tags":["Tags"],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"tagId","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"archived":{"type":"boolean"},"color":{"$ref":"#/components/schemas/TagColor"},"name":{"type":"string","minLength":1,"maxLength":50}}}}}},"responses":{"200":{"description":"Tag updated","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"403":{"description":"Subscription required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"404":{"description":"Tag not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}}},"delete":{"description":"Permanently deletes a tag and all its transaction memberships (cascade).","summary":"Delete tag","tags":["Tags"],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"tagId","in":"path"}],"responses":{"200":{"description":"Tag deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"403":{"description":"Subscription required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"404":{"description":"Tag not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}}}},"/api/tags/bulk-apply":{"post":{"description":"Adds and/or removes tags across many transactions in a single operation.","summary":"Bulk apply tags","tags":["Tags"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"addTagIds":{"type":"array","items":{"type":"string","format":"uuid"},"default":[]},"removeTagIds":{"type":"array","items":{"type":"string","format":"uuid"},"default":[]},"transactionIds":{"type":"array","items":{"type":"string","format":"uuid"},"minItems":1}},"required":["transactionIds"]}}}},"responses":{"200":{"description":"Bulk apply complete","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"updatedCount":{"type":"integer"}},"required":["success","updatedCount"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"403":{"description":"Subscription required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"404":{"description":"One or more tags not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}}}},"/api/categories":{"get":{"description":"Lists every active category and group owned by the user.","summary":"List categories","tags":["Categories"],"responses":{"200":{"description":"User's categories + groups","content":{"application/json":{"schema":{"type":"object","properties":{"categories":{"type":"array","items":{"$ref":"#/components/schemas/Category"}},"groups":{"type":"array","items":{"$ref":"#/components/schemas/CategoryGroup"}}},"required":["categories","groups"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"403":{"description":"Subscription required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}},"post":{"description":"Creates a custom category under an existing group.","summary":"Create category","tags":["Categories"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"excludeFromInsights":{"type":"boolean"},"groupId":{"type":"string","format":"uuid"},"iconKey":{"type":"string","minLength":1,"maxLength":50},"name":{"type":"string","minLength":1,"maxLength":50}},"required":["groupId","iconKey","name"]}}}},"responses":{"201":{"description":"Category created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Category"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"403":{"description":"Subscription required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"404":{"description":"Group not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}}}},"/api/categories/reorder":{"post":{"description":"Replaces the order of categories within a single group.","summary":"Reorder categories","tags":["Categories"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"categoryIds":{"type":"array","items":{"type":"string","format":"uuid"},"minItems":1},"groupId":{"type":"string","format":"uuid"}},"required":["categoryIds","groupId"]}}}},"responses":{"200":{"description":"Reordered","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"403":{"description":"Subscription required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"404":{"description":"Group not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}}}},"/api/categories/groups/reorder":{"post":{"description":"Replaces the order of all groups for the user.","summary":"Reorder category groups","tags":["Categories"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"groupIds":{"type":"array","items":{"type":"string","format":"uuid"},"minItems":1}},"required":["groupIds"]}}}},"responses":{"200":{"description":"Reordered","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"403":{"description":"Subscription required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}}}},"/api/categories/groups":{"post":{"description":"Creates a custom category group.","summary":"Create category group","tags":["Categories"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":50}},"required":["name"]}}}},"responses":{"201":{"description":"Group created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CategoryGroup"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"403":{"description":"Subscription required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}}}},"/api/categories/groups/{groupId}":{"patch":{"description":"Renames or reorders a category group.","summary":"Update category group","tags":["Categories"],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"groupId","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":50},"order":{"type":"integer","minimum":0}}}}}},"responses":{"200":{"description":"Group updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CategoryGroup"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"403":{"description":"Subscription required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"404":{"description":"Group not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}}},"delete":{"description":"Soft-deletes a custom group. The group must be empty (no active categories). System groups cannot be deleted.","summary":"Delete category group","tags":["Categories"],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"groupId","in":"path"}],"responses":{"200":{"description":"Group deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"403":{"description":"Subscription required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"404":{"description":"Group not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"409":{"description":"Group has categories or is system-locked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}}}},"/api/categories/{categoryId}/hide":{"post":{"description":"Hides a category. Optionally reassigns dependent transactions and recurring rows to a target cat first; otherwise leaves them assigned to the now-hidden cat.","summary":"Hide category","tags":["Categories"],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"categoryId","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"reassignTo":{"type":["string","null"],"format":"uuid"}}}}}},"responses":{"200":{"description":"Category hidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Category"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"403":{"description":"Subscription required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"404":{"description":"Category or reassign target not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"409":{"description":"Reassign target invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}}}},"/api/categories/{categoryId}":{"patch":{"description":"Renames, recolors/icons, hides, reorders, or moves a category between groups.","summary":"Update category","tags":["Categories"],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"categoryId","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"excludeFromInsights":{"type":"boolean"},"groupId":{"type":"string","format":"uuid"},"hidden":{"type":"boolean"},"iconKey":{"type":"string","minLength":1,"maxLength":50},"name":{"type":"string","minLength":1,"maxLength":50},"order":{"type":"integer","minimum":0}}}}}},"responses":{"200":{"description":"Category updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Category"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"403":{"description":"Subscription required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"404":{"description":"Category or group not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}}},"delete":{"description":"Soft-deletes a custom category. Reassigns all dependent transactions and recurring rows to the user's Uncategorized seed cat. System cats cannot be deleted (only hidden).","summary":"Delete category","tags":["Categories"],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"categoryId","in":"path"}],"responses":{"200":{"description":"Category deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"403":{"description":"Subscription required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"404":{"description":"Category not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"409":{"description":"System category cannot be deleted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}}}},"/api/brokerage":{"get":{"description":"Brokerage overview (SnapTrade + Plaid investment, unified): accounts, balances, positions, activities, and snapshots.","summary":"Get brokerage overview","tags":["Brokerage"],"parameters":[{"schema":{"type":"string"},"required":false,"name":"endDate","in":"query"},{"schema":{"type":"string"},"required":false,"name":"startDate","in":"query"},{"schema":{"type":"number","minimum":1,"maximum":500},"required":false,"name":"activitiesLimit","in":"query"},{"schema":{"type":"number","minimum":1,"maximum":500},"required":false,"name":"positionsLimit","in":"query"}],"responses":{"200":{"description":"Brokerage overview payload","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BrokerageOverview"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"403":{"description":"Subscription required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}}}},"/api/brokerage/balances":{"get":{"summary":"Get brokerage balances","tags":["Brokerage"],"responses":{"200":{"description":"Account balances","content":{"application/json":{"schema":{"type":"object","properties":{"balances":{"type":"array","items":{"$ref":"#/components/schemas/BrokerageBalance"}},"balancesByAccount":{"type":"object","additionalProperties":{"type":"array","items":{"$ref":"#/components/schemas/BrokerageBalance"}}}},"required":["balances","balancesByAccount"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"403":{"description":"Subscription required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}}},"/api/brokerage/positions":{"get":{"summary":"Get brokerage positions","tags":["Brokerage"],"parameters":[{"schema":{"type":"string"},"required":false,"name":"accountId","in":"query"},{"schema":{"type":"number","minimum":1},"required":false,"name":"limit","in":"query"},{"schema":{"type":["number","null"],"minimum":0},"required":false,"name":"offset","in":"query"}],"responses":{"200":{"description":"Brokerage positions","content":{"application/json":{"schema":{"type":"object","properties":{"positions":{"type":"array","items":{"$ref":"#/components/schemas/BrokeragePosition"}},"positionsByAccount":{"type":"object","additionalProperties":{"type":"array","items":{"$ref":"#/components/schemas/BrokeragePosition"}}}},"required":["positions","positionsByAccount"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"403":{"description":"Subscription required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}}}},"/api/brokerage/activities":{"get":{"summary":"Get brokerage activities","tags":["Brokerage"],"parameters":[{"schema":{"type":"string"},"required":false,"name":"accountId","in":"query"},{"schema":{"type":"number","minimum":1},"required":false,"name":"limit","in":"query"},{"schema":{"type":["number","null"],"minimum":0},"required":false,"name":"offset","in":"query"}],"responses":{"200":{"description":"Brokerage activities","content":{"application/json":{"schema":{"type":"object","properties":{"activities":{"type":"array","items":{"$ref":"#/components/schemas/BrokerageActivity"}},"activitiesByAccount":{"type":"object","additionalProperties":{"type":"array","items":{"$ref":"#/components/schemas/BrokerageActivity"}}}},"required":["activities","activitiesByAccount"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"403":{"description":"Subscription required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}}}},"/api/brokerage/portfolio-snapshots":{"get":{"summary":"Get portfolio snapshots","tags":["Brokerage"],"parameters":[{"schema":{"type":"string"},"required":false,"name":"accountId","in":"query"},{"schema":{"type":"string"},"required":false,"name":"endDate","in":"query"},{"schema":{"type":"string"},"required":false,"name":"startDate","in":"query"}],"responses":{"200":{"description":"Portfolio snapshots","content":{"application/json":{"schema":{"type":"object","properties":{"snapshots":{"type":"array","items":{"$ref":"#/components/schemas/PortfolioSnapshot"}}},"required":["snapshots"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"403":{"description":"Subscription required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}}}},"/api/brokerage/user-brokerages":{"get":{"summary":"Get user brokerages","tags":["Brokerage"],"responses":{"200":{"description":"List of connected brokerage names","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"string"}}},"required":["data"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"403":{"description":"Subscription required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}}},"/api/brokerage/user-tickers":{"get":{"summary":"Get user tickers","tags":["Brokerage"],"responses":{"200":{"description":"List of held stock tickers","content":{"application/json":{"schema":{"type":"object","properties":{"tickers":{"type":"array","items":{"type":"string"}}},"required":["tickers"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"403":{"description":"Subscription required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}}},"/api/brokerage/manual-holdings":{"post":{"summary":"Create a manual holding on a manual investment account","tags":["Brokerage"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateManualHolding"}}}},"responses":{"200":{"description":"Holding created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ManualHolding"}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"403":{"description":"Subscription required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"404":{"description":"Account not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}}}},"/api/brokerage/manual-holdings/{holdingId}":{"patch":{"summary":"Update a manual holding (sparse patch)","tags":["Brokerage"],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"holdingId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchManualHolding"}}}},"responses":{"200":{"description":"Holding updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ManualHolding"}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"403":{"description":"Subscription required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"404":{"description":"Holding not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}}},"delete":{"summary":"Delete a manual holding","tags":["Brokerage"],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"holdingId","in":"path"}],"responses":{"200":{"description":"Holding deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"403":{"description":"Subscription required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"404":{"description":"Holding not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}}}},"/api/brokerage/manual-holdings/sell":{"post":{"summary":"Record a SELL against a manual holding","tags":["Brokerage"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SellManualHolding"}}}},"responses":{"200":{"description":"Sell recorded","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"Invalid input or oversell","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"403":{"description":"Subscription required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"404":{"description":"Holding not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}}}},"/api/brokerage/manual-cash-sleeve":{"put":{"summary":"Set the uninvested cash sleeve on a manual investment account","tags":["Brokerage"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetManualCashSleeve"}}}},"responses":{"200":{"description":"Cash sleeve set","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"403":{"description":"Subscription required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"404":{"description":"Account not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}}}},"/api/chat/list":{"get":{"description":"List the authenticated user's chats (most recently updated first).","summary":"List chats","tags":["Chat"],"responses":{"200":{"description":"Chat list","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Chat"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"403":{"description":"Subscription required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}}},"/api/chat/{chatId}":{"get":{"description":"Load messages and vote map for one chat. Returns 404 with empty messages when the chat is missing or inaccessible (matches legacy client behavior).","summary":"Get chat messages","tags":["Chat"],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"chatId","in":"path"}],"responses":{"200":{"description":"Chat detail","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatDetail"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"403":{"description":"Subscription required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"404":{"description":"Not found — empty messages","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatDetail"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}}},"delete":{"description":"Permanently delete a chat owned by the authenticated user. Cascades to messages and parts. REST endpoint for mobile clients; web uses the Zero `chats.delete` mutator.","summary":"Delete chat","tags":["Chat"],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"chatId","in":"path"}],"responses":{"200":{"description":"Chat deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"403":{"description":"Subscription required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"404":{"description":"Chat not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}}}},"/api/chat":{"post":{"description":"Create a new empty chat owned by the authenticated user.","summary":"Create chat","tags":["Chat"],"responses":{"201":{"description":"Chat created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateChatResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"403":{"description":"Subscription required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}}},"/api/research/quote":{"get":{"summary":"Get stock quote (price + change)","tags":["Research"],"parameters":[{"schema":{"type":"string","minLength":1,"example":"AAPL"},"required":true,"name":"symbol","in":"query"}],"responses":{"200":{"description":"Quote data","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Quote"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"404":{"description":"Ticker not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}},"502":{"description":"FMP upstream failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}}},"/api/research/overview":{"get":{"summary":"Get company overview","tags":["Research"],"parameters":[{"schema":{"type":"string","minLength":1,"example":"AAPL"},"required":true,"name":"symbol","in":"query"}],"responses":{"200":{"description":"Company overview","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Overview"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"404":{"description":"Ticker not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}},"502":{"description":"FMP upstream failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}}},"/api/research/chart":{"get":{"summary":"Get price chart data","tags":["Research"],"parameters":[{"schema":{"type":"string","deprecated":true,"description":"Deprecated — use timePeriod"},"required":false,"description":"Deprecated — use timePeriod","name":"interval","in":"query"},{"schema":{"type":"string","minLength":1,"example":"AAPL"},"required":true,"name":"symbol","in":"query"},{"schema":{"type":"string","enum":["1D","1W","1M","3M","6M","YTD","1Y","All"],"example":"1D"},"required":false,"name":"timePeriod","in":"query"}],"responses":{"200":{"description":"Chart data","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChartResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"404":{"description":"Ticker not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}},"502":{"description":"FMP upstream failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}}},"/api/research/news":{"get":{"summary":"Get ticker-specific news","tags":["Research"],"parameters":[{"schema":{"type":"string","minLength":1,"example":"AAPL"},"required":true,"name":"symbol","in":"query"}],"responses":{"200":{"description":"News articles","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResearchNewsResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}},"502":{"description":"Stock News upstream failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}}},"/api/research/search":{"get":{"summary":"Search tickers (cached NASDAQ + NYSE list)","tags":["Research"],"responses":{"200":{"description":"All cached tickers for client-side search","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TickersSearchResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}}},"/api/research/ticker-history":{"get":{"summary":"Get daily closes around a date (cost-basis picker)","tags":["Research"],"parameters":[{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Reference date YYYY-MM-DD","example":"2023-06-15"},"required":true,"description":"Reference date YYYY-MM-DD","name":"date","in":"query"},{"schema":{"type":"string","minLength":1,"example":"AAPL"},"required":true,"name":"symbol","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":30,"description":"Calendar days on each side of date (default 7)"},"required":false,"description":"Calendar days on each side of date (default 7)","name":"window","in":"query"}],"responses":{"200":{"description":"Daily closes around the requested date","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TickerHistoryResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}},"502":{"description":"FMP upstream failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}}},"/api/research/screener":{"get":{"summary":"Screen stocks (FMP screener + revenue + P/E from `fundamentals` table)","tags":["Research"],"parameters":[{"schema":{"type":["number","null"],"example":2},"required":false,"name":"betaLowerThan","in":"query"},{"schema":{"type":["number","null"],"example":0.5},"required":false,"name":"betaMoreThan","in":"query"},{"schema":{"type":"string","minLength":1,"example":"US"},"required":false,"name":"country","in":"query"},{"schema":{"type":["number","null"]},"required":false,"name":"dividendLowerThan","in":"query"},{"schema":{"type":["number","null"]},"required":false,"name":"dividendMoreThan","in":"query"},{"schema":{"type":"string","minLength":1,"description":"Ignored — this endpoint always returns NASDAQ and NYSE listings only.","example":"NASDAQ"},"required":false,"description":"Ignored — this endpoint always returns NASDAQ and NYSE listings only.","name":"exchange","in":"query"},{"schema":{"type":"string","minLength":1},"required":false,"name":"industry","in":"query"},{"schema":{"type":"string","enum":["true","false"],"description":"Filter actively trading names"},"required":false,"description":"Filter actively trading names","name":"isActivelyTrading","in":"query"},{"schema":{"type":"string","enum":["true","false"],"description":"ETF-only filter"},"required":false,"description":"ETF-only filter","name":"isEtf","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":10000,"example":50},"required":false,"name":"limit","in":"query"},{"schema":{"type":["number","null"]},"required":false,"name":"marketCapLowerThan","in":"query"},{"schema":{"type":["number","null"]},"required":false,"name":"marketCapMoreThan","in":"query"},{"schema":{"type":["number","null"]},"required":false,"name":"priceLowerThan","in":"query"},{"schema":{"type":["number","null"]},"required":false,"name":"priceMoreThan","in":"query"},{"schema":{"type":"string","minLength":1,"example":"Technology"},"required":false,"name":"sector","in":"query"},{"schema":{"type":["number","null"]},"required":false,"name":"volumeLowerThan","in":"query"},{"schema":{"type":["number","null"]},"required":false,"name":"volumeMoreThan","in":"query"}],"responses":{"200":{"description":"FMP company screener results","content":{"application/json":{"schema":{"type":"object","properties":{"count":{"type":"number"},"results":{"type":"array","items":{"$ref":"#/components/schemas/ScreenerRow"}}},"required":["count","results"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}},"502":{"description":"FMP upstream failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}}},"/api/snaptrade/generateConnectionPortal":{"post":{"summary":"Generate SnapTrade connection portal URL","tags":["SnapTrade"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"broker":{"type":"string","default":""},"reconnectAuthorizationId":{"type":"string"}}}}}},"responses":{"200":{"description":"Connection portal URL","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectionPortal"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"402":{"description":"Free-tier connection limit reached — upgrade required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"403":{"description":"Not available in demo mode","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}},"502":{"description":"SnapTrade upstream failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}}},"/api/subscriptions":{"get":{"description":"Check whether the authenticated user has an active subscription","summary":"Get subscription status","tags":["Subscriptions"],"responses":{"200":{"description":"Subscription status","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionStatus"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}}},"/api/subscriptions/billingPortal":{"post":{"description":"Create a Stripe billing portal session for the authenticated user","summary":"Create billing portal session","tags":["Subscriptions"],"responses":{"200":{"description":"Billing portal URL","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingPortal"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"403":{"description":"Not available in demo mode","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"409":{"description":"User has no Stripe customer","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}},"502":{"description":"Stripe upstream failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}}},"/api/user/deleteAccount":{"delete":{"description":"Permanently delete the authenticated user's account and all associated data","summary":"Delete user account","tags":["User"],"responses":{"200":{"description":"Account deleted","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"},"success":{"type":"boolean"}},"required":["message","success"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"403":{"description":"Not available in demo mode","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"500":{"description":"Account deletion failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}}},"/api/user/lastSeen":{"get":{"description":"Get the authenticated user's last-seen timestamp and whether financial updates should be shown","summary":"Get last seen","tags":["User"],"responses":{"200":{"description":"Last seen status","content":{"application/json":{"schema":{"type":"object","properties":{"lastSeenAt":{"type":["string","null"]},"shouldShowUpdates":{"type":"boolean"}},"required":["lastSeenAt","shouldShowUpdates"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}},"post":{"description":"Update the authenticated user's last-seen timestamp to now (called when user dismisses financial updates)","summary":"Update last seen","tags":["User"],"responses":{"200":{"description":"Last seen updated","content":{"application/json":{"schema":{"type":"object","properties":{"lastSeenAt":{"type":["string","null"]},"shouldShowUpdates":{"type":"boolean"}},"required":["lastSeenAt","shouldShowUpdates"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}}},"/api/user/sharingSettings":{"get":{"description":"Get the authenticated user's auto-share field preferences.","summary":"Get sharing settings","tags":["User"],"responses":{"200":{"description":"Share settings","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShareSettings"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}},"post":{"description":"Update the authenticated user's auto-share field preferences. Partial — only sent fields are written.","summary":"Update sharing settings","tags":["User"],"requestBody":{"description":"Settings patch","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"shareAmount":{"type":"boolean"},"shareCard":{"type":"boolean"},"shareDate":{"type":"boolean"},"shareMaxAmountCents":{"type":["integer","null"],"minimum":0},"shareMerchant":{"type":"boolean"},"shareMinAmountCents":{"type":["integer","null"],"minimum":0},"shareNote":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Updated settings","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShareSettings"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}}},"/api/user/sharingSettings/merchantBlocklist":{"post":{"description":"Add a merchant to the share blocklist. Retroactively deletes matching shared posts.","summary":"Block a merchant from sharing","tags":["User"],"requestBody":{"description":"Merchant name","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200}},"required":["name"]}}}},"responses":{"200":{"description":"Added","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}},"delete":{"description":"Remove a merchant from the share blocklist. Future syncs will share again.","summary":"Unblock a merchant","tags":["User"],"requestBody":{"description":"Merchant name","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200}},"required":["name"]}}}},"responses":{"200":{"description":"Removed","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}}},"/api/user/sharingSettings/categoryBlocklist":{"post":{"description":"Add a category to the share blocklist. Retroactively deletes matching shared posts.","summary":"Block a category from sharing","tags":["User"],"requestBody":{"description":"Category name","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200}},"required":["name"]}}}},"responses":{"200":{"description":"Added","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}},"delete":{"description":"Remove a category from the share blocklist. Future syncs will share again.","summary":"Unblock a category","tags":["User"],"requestBody":{"description":"Category name","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200}},"required":["name"]}}}},"responses":{"200":{"description":"Removed","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}}},"/api/institutions":{"get":{"description":"Search Plaid institutions by name (or list default set when query is empty).","summary":"Search institutions","tags":["Institutions"],"parameters":[{"schema":{"type":"string"},"required":false,"name":"query","in":"query"}],"responses":{"200":{"description":"Institution search results","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchInstitutionsResponse"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}},"502":{"description":"Plaid upstream failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}}},"/api/institutions/db/{id}":{"get":{"description":"Fetch the local DB row for an institution by Plaid institution id.","summary":"Get institution from database","tags":["Institutions"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Institution from database","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InstitutionDb"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}}}},"/api/institutions/sync":{"post":{"description":"Sync an institution from Plaid into the local DB. Idempotent: returns the existing row if already synced.","summary":"Sync institution from Plaid to database","tags":["Institutions"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"institutionId":{"type":"string"}},"required":["institutionId"]}}}},"responses":{"200":{"description":"Institution synced","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InstitutionDb"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}},"502":{"description":"Plaid upstream failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}}},"/api/institutions/{id}":{"get":{"description":"Fetch a single institution from Plaid by id (proxied, mapped to our shape).","summary":"Get institution from Plaid","tags":["Institutions"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Institution details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InstitutionDetail"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}},"502":{"description":"Plaid upstream failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}}},"/api/imports/list":{"get":{"summary":"List resumable import jobs","tags":["Imports"],"responses":{"200":{"description":"In-progress import jobs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImportsListResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}}},"/api/imports/{id}/column-map":{"get":{"summary":"Suggest column mapping (Step 2)","tags":["Imports"],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"AI-inferred column mapping","content":{"application/json":{"schema":{"type":"object","properties":{"fromCache":{"type":"boolean"},"headers":{"type":"array","items":{"type":"string"}},"mapping":{"type":"object","properties":{"account":{"type":["object","null"],"properties":{"column":{"type":"string"}},"required":["column"]},"amount":{"oneOf":[{"type":"object","properties":{"column":{"type":"string"},"kind":{"type":"string","enum":["signed"]},"parensNegative":{"type":"boolean"},"signConvention":{"type":"string","enum":["outflow_negative","outflow_positive"]}},"required":["column","kind","parensNegative","signConvention"]},{"type":"object","properties":{"inflowColumn":{"type":"string"},"kind":{"type":"string","enum":["split"]},"outflowColumn":{"type":"string"}},"required":["inflowColumn","kind","outflowColumn"]},{"type":"object","properties":{"debitValues":{"type":"array","items":{"type":"string"}},"kind":{"type":"string","enum":["magnitude_type"]},"magnitudeColumn":{"type":"string"},"typeColumn":{"type":"string"}},"required":["debitValues","kind","magnitudeColumn","typeColumn"]}]},"category":{"type":["object","null"],"properties":{"column":{"type":"string"}},"required":["column"]},"confidence":{"type":"number","minimum":0,"maximum":1},"date":{"oneOf":[{"type":"object","properties":{"column":{"type":"string"},"format":{"type":"string"},"kind":{"type":"string","enum":["column"]}},"required":["column","format","kind"]},{"type":"object","properties":{"kind":{"type":"string","enum":["missing"]}},"required":["kind"]}]},"excludeRule":{"type":["object","null"],"properties":{"column":{"type":"string"},"trueValues":{"type":"array","items":{"type":"string"}}},"required":["column","trueValues"]},"merchant":{"type":"object","properties":{"column":{"type":"string"}},"required":["column"]},"notes":{"type":["object","null"],"properties":{"column":{"type":"string"}},"required":["column"]},"originalDescription":{"type":["object","null"],"properties":{"column":{"type":"string"}},"required":["column"]},"tags":{"type":["object","null"],"properties":{"column":{"type":"string"},"delimiter":{"type":"string"}},"required":["column","delimiter"]},"transferRule":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","enum":["category_match"]},"values":{"type":"array","items":{"type":"string"}}},"required":["kind","values"]},{"type":"object","properties":{"column":{"type":"string"},"kind":{"type":"string","enum":["type_match"]},"values":{"type":"array","items":{"type":"string"}}},"required":["column","kind","values"]},{"type":"object","properties":{"kind":{"type":"string","enum":["merchant_prefix"]},"prefixes":{"type":"array","items":{"type":"string"}}},"required":["kind","prefixes"]},{"type":"null"}]}},"required":["account","amount","category","confidence","date","excludeRule","merchant","notes","originalDescription","tags","transferRule"]},"sampleRows":{"type":"array","items":{"type":"object","additionalProperties":{"type":"string"}}}},"required":["fromCache","headers","mapping","sampleRows"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"404":{"description":"Import job not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}},"post":{"summary":"Confirm column mapping","tags":["Imports"],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"defaultAccountName":{"type":"string","minLength":1},"defaultDate":{"type":"string"},"mapping":{"type":"object","properties":{"account":{"type":["object","null"],"properties":{"column":{"type":"string"}},"required":["column"]},"amount":{"oneOf":[{"type":"object","properties":{"column":{"type":"string"},"kind":{"type":"string","enum":["signed"]},"parensNegative":{"type":"boolean"},"signConvention":{"type":"string","enum":["outflow_negative","outflow_positive"]}},"required":["column","kind","parensNegative","signConvention"]},{"type":"object","properties":{"inflowColumn":{"type":"string"},"kind":{"type":"string","enum":["split"]},"outflowColumn":{"type":"string"}},"required":["inflowColumn","kind","outflowColumn"]},{"type":"object","properties":{"debitValues":{"type":"array","items":{"type":"string"}},"kind":{"type":"string","enum":["magnitude_type"]},"magnitudeColumn":{"type":"string"},"typeColumn":{"type":"string"}},"required":["debitValues","kind","magnitudeColumn","typeColumn"]}]},"category":{"type":["object","null"],"properties":{"column":{"type":"string"}},"required":["column"]},"confidence":{"type":"number","minimum":0,"maximum":1},"date":{"oneOf":[{"type":"object","properties":{"column":{"type":"string"},"format":{"type":"string"},"kind":{"type":"string","enum":["column"]}},"required":["column","format","kind"]},{"type":"object","properties":{"kind":{"type":"string","enum":["missing"]}},"required":["kind"]}]},"excludeRule":{"type":["object","null"],"properties":{"column":{"type":"string"},"trueValues":{"type":"array","items":{"type":"string"}}},"required":["column","trueValues"]},"merchant":{"type":"object","properties":{"column":{"type":"string"}},"required":["column"]},"notes":{"type":["object","null"],"properties":{"column":{"type":"string"}},"required":["column"]},"originalDescription":{"type":["object","null"],"properties":{"column":{"type":"string"}},"required":["column"]},"tags":{"type":["object","null"],"properties":{"column":{"type":"string"},"delimiter":{"type":"string"}},"required":["column","delimiter"]},"transferRule":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","enum":["category_match"]},"values":{"type":"array","items":{"type":"string"}}},"required":["kind","values"]},{"type":"object","properties":{"column":{"type":"string"},"kind":{"type":"string","enum":["type_match"]},"values":{"type":"array","items":{"type":"string"}}},"required":["column","kind","values"]},{"type":"object","properties":{"kind":{"type":"string","enum":["merchant_prefix"]},"prefixes":{"type":"array","items":{"type":"string"}}},"required":["kind","prefixes"]},{"type":"null"}]}},"required":["account","amount","category","confidence","date","excludeRule","merchant","notes","originalDescription","tags","transferRule"]},"singleAccountChoice":{"oneOf":[{"type":"object","properties":{"accountId":{"type":"string","format":"uuid"},"kind":{"type":"string","enum":["existing"]}},"required":["accountId","kind"]},{"type":"object","properties":{"institutionLogoDomain":{"type":"string","maxLength":255},"institutionName":{"type":"string","maxLength":255},"kind":{"type":"string","enum":["create"]},"name":{"type":"string","minLength":1}},"required":["kind","name"]}]}},"required":["mapping"]}}}},"responses":{"200":{"description":"Mapping confirmed; rows staged","content":{"application/json":{"schema":{"type":"object","properties":{"rejected":{"type":"integer","minimum":0},"staged":{"type":"integer","minimum":0},"success":{"type":"boolean"}},"required":["rejected","staged","success"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}}},"/api/imports/{id}/account-map":{"get":{"summary":"Suggest account mapping (Step 3)","tags":["Imports"],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Account suggestions","content":{"application/json":{"schema":{"type":"object","properties":{"path":{"type":"string","enum":["A","B"]},"sourceLabels":{"type":"array","items":{"type":"string"}},"suggestions":{"type":"array","items":{"type":"object","properties":{"confidence":{"type":"number"},"fromCache":{"type":"boolean"},"newName":{"type":"string"},"sourceLabel":{"type":"string"},"suggestedInstitutionDomain":{"type":"string"},"suggestedInstitutionName":{"type":"string"},"suggestedSubtype":{"type":"string"},"suggestedType":{"type":"string","enum":["depository","credit","investment","loan"]},"target":{"type":"string"}},"required":["confidence","fromCache","sourceLabel","target"]}}},"required":["path","sourceLabels","suggestions"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"404":{"description":"Import job not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}},"post":{"summary":"Confirm account mapping","tags":["Imports"],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","enum":["perLabel"]},"map":{"type":"object","additionalProperties":{"oneOf":[{"type":"object","properties":{"accountId":{"type":"string","format":"uuid"},"kind":{"type":"string","enum":["existing"]}},"required":["accountId","kind"]},{"type":"object","properties":{"institutionLogoDomain":{"type":"string","maxLength":255},"institutionName":{"type":"string","maxLength":255},"kind":{"type":"string","enum":["create"]},"name":{"type":"string","minLength":1,"maxLength":255},"subtype":{"type":"string","minLength":1,"maxLength":64},"type":{"type":"string","enum":["depository","credit","investment","loan"]}},"required":["kind","name","subtype","type"]},{"type":"object","properties":{"kind":{"type":"string","enum":["skip"]}},"required":["kind"]}]}}},"required":["kind","map"]},{"type":"object","properties":{"choice":{"oneOf":[{"type":"object","properties":{"accountId":{"type":"string","format":"uuid"},"kind":{"type":"string","enum":["existing"]}},"required":["accountId","kind"]},{"type":"object","properties":{"institutionLogoDomain":{"type":"string","maxLength":255},"institutionName":{"type":"string","maxLength":255},"kind":{"type":"string","enum":["create"]},"name":{"type":"string","minLength":1,"maxLength":255},"subtype":{"type":"string","minLength":1,"maxLength":64},"type":{"type":"string","enum":["depository","credit","investment","loan"]}},"required":["kind","name","subtype","type"]},{"type":"object","properties":{"kind":{"type":"string","enum":["skip"]}},"required":["kind"]}]},"kind":{"type":"string","enum":["single"]}},"required":["choice","kind"]}]}}}},"responses":{"200":{"description":"Account mapping confirmed","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"},"success":{"type":"boolean"}},"required":["success"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}}},"/api/imports/{id}/category-map":{"get":{"summary":"Suggest category mapping (Step 4)","tags":["Imports"],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Category suggestions","content":{"application/json":{"schema":{"type":"object","properties":{"sourceLabels":{"type":"array","items":{"type":"string"}},"suggestions":{"type":"array","items":{"type":"object","properties":{"action":{"type":"string","enum":["link","skip"]},"confidence":{"type":"number"},"fromCache":{"type":"boolean"},"sourceLabel":{"type":"string"},"targetCategoryId":{"type":["string","null"]}},"required":["action","confidence","fromCache","sourceLabel","targetCategoryId"]}},"userCategories":{"type":"array","items":{"type":"object","properties":{"groupId":{"type":"string"},"iconKey":{"type":"string"},"id":{"type":"string"},"name":{"type":"string"},"systemKey":{"type":["string","null"]}},"required":["groupId","iconKey","id","name","systemKey"]}},"userGroups":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"systemKey":{"type":["string","null"]}},"required":["id","name","systemKey"]}}},"required":["sourceLabels","suggestions","userCategories","userGroups"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"404":{"description":"Import job not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}},"post":{"summary":"Confirm category mapping","tags":["Imports"],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"map":{"type":"object","additionalProperties":{"oneOf":[{"type":"object","properties":{"action":{"type":"string","enum":["link"]},"targetCategoryId":{"type":"string","format":"uuid"}},"required":["action","targetCategoryId"]},{"type":"object","properties":{"action":{"type":"string","enum":["skip"]}},"required":["action"]}]}}},"required":["map"]}}}},"responses":{"200":{"description":"Category mapping confirmed","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"},"success":{"type":"boolean"}},"required":["success"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}}},"/api/imports/{id}/staged-preview":{"get":{"summary":"Sample of staged rows","tags":["Imports"],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"A few staged rows for the commit-screen preview","content":{"application/json":{"schema":{"type":"object","properties":{"rows":{"type":"array","items":{"type":"object","properties":{"amount":{"type":"string"},"date":{"type":"string"},"merchant":{"type":"string"},"sourceAccountName":{"type":"string"},"sourceCategoryName":{"type":["string","null"]}},"required":["amount","date","merchant","sourceAccountName","sourceCategoryName"]}}},"required":["rows"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}}},"/api/imports/{id}/staged-rows":{"get":{"summary":"All staged rows","tags":["Imports"],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Full staged-row set for the expanded table view","content":{"application/json":{"schema":{"type":"object","properties":{"rows":{"type":"array","items":{"type":"object","properties":{"amount":{"type":"string"},"date":{"type":"string"},"id":{"type":"string","format":"uuid"},"merchant":{"type":"string"},"notes":{"type":["string","null"]},"originalDescription":{"type":["string","null"]},"parseError":{"type":["string","null"]},"sourceAccountName":{"type":"string"},"sourceCategoryName":{"type":["string","null"]},"tags":{"type":"array","items":{"type":"string"}}},"required":["amount","date","id","merchant","notes","originalDescription","parseError","sourceAccountName","sourceCategoryName","tags"]}},"tagsMapped":{"type":"boolean"}},"required":["rows","tagsMapped"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}}},"/api/imports/{id}/staged-rows/{rowId}":{"patch":{"summary":"Patch editable fields on a staged row","tags":["Imports"],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","format":"uuid"},"required":true,"name":"rowId","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"amount":{"type":"string","pattern":"^-?\\d+(\\.\\d+)?$"},"date":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"merchant":{"type":"string","minLength":1},"notes":{"type":["string","null"]},"originalDescription":{"type":["string","null"]}}}}}},"responses":{"200":{"description":"Updated staged row","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImportStagedRowResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"404":{"description":"Staged row not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}}},"/api/imports/{id}/resolutions":{"get":{"summary":"Account + category resolutions for this import","tags":["Imports"],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Confirmed account + category decisions for this import","content":{"application/json":{"schema":{"type":"object","properties":{"accountByLabel":{"type":"object","additionalProperties":{"type":"string"}},"categoryByLabel":{"type":"object","additionalProperties":{"type":"string"}},"pendingAccounts":{"type":"array","items":{"type":"object","properties":{"institutionLogoDomain":{"type":["string","null"]},"institutionName":{"type":["string","null"]},"key":{"type":"string"},"name":{"type":"string"},"subtype":{"type":"string"},"type":{"type":"string"}},"required":["institutionLogoDomain","institutionName","key","name","subtype","type"]}},"pendingCategories":{"type":"array","items":{"type":"object","properties":{"color":{"type":["string","null"]},"groupId":{"type":"string"},"iconKey":{"type":"string"},"name":{"type":"string"},"sourceLabel":{"type":"string"}},"required":["color","groupId","iconKey","name","sourceLabel"]}},"singleAccountId":{"type":["string","null"]}},"required":["accountByLabel","categoryByLabel","pendingAccounts","pendingCategories","singleAccountId"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}}},"/api/imports/{id}/commit":{"post":{"summary":"Commit staged rows into transactions","tags":["Imports"],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"override":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Commit workflow triggered","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"},"success":{"type":"boolean"}},"required":["success"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"404":{"description":"Import job not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"409":{"description":"Job not ready for commit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Pre-commit gate blocked the import, or raised warnings needing override","content":{"application/json":{"schema":{"type":"object","properties":{"blocked":{"type":"boolean"},"reasons":{"type":"array","items":{"type":"string"}},"warnings":{"type":"array","items":{"type":"string"}}},"required":["blocked","reasons","warnings"]}}}}}}},"/api/imports/{id}/cancel":{"post":{"summary":"Cancel an in-progress commit","tags":["Imports"],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Cancellation requested","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"},"success":{"type":"boolean"}},"required":["success"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}}},"/api/imports/{id}":{"delete":{"summary":"Discard an in-progress import job","tags":["Imports"],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Import job discarded","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"},"success":{"type":"boolean"}},"required":["success"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"404":{"description":"Import job not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"409":{"description":"Cannot delete a committed job","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}},"get":{"summary":"Get import job status","tags":["Imports"],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Import job status","content":{"application/json":{"schema":{"type":"object","properties":{"errorMessage":{"type":["string","null"]},"id":{"type":"string","format":"uuid"},"originalFilename":{"type":["string","null"]},"progress":{"type":["object","null"],"properties":{"done":{"type":"number"},"message":{"type":"string"},"step":{"type":"string"},"total":{"type":"number"}},"required":["done","step","total"]},"rejectedRows":{"type":"integer","minimum":0},"source":{"type":"string","enum":["csv"]},"status":{"type":"string","enum":["uploaded","column_mapped","account_mapped","category_mapped","committing","committed","failed","cancelled"]},"summary":{"type":["object","null"],"properties":{"duplicates":{"type":"number"},"excluded":{"type":"number"},"failed":{"type":"number"},"imported":{"type":"number"}},"required":["duplicates","excluded","failed","imported"]},"totalRows":{"type":"integer","minimum":0}},"required":["errorMessage","id","originalFilename","progress","rejectedRows","source","status","summary","totalRows"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"404":{"description":"Import job not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}}},"/api/plaid/createLinkToken":{"post":{"summary":"Mint a Plaid link token and start the parked add-account workflow","tags":["Plaid"],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"institutionId":{"type":"string"}}}}}},"responses":{"200":{"description":"Link token minted; workflow parked on hook. Client must echo the Plaid Link outcome via /resolveLink","content":{"application/json":{"schema":{"type":"object","properties":{"hookToken":{"type":"string"},"institutionLogo":{"type":["string","null"]},"institutionName":{"type":["string","null"]},"institutionUrl":{"type":["string","null"]},"link_token":{"type":"string"},"mode":{"type":"string","enum":["update","reauth"]},"plaidItemId":{"type":"string"},"runId":{"type":"string"}},"required":["hookToken","link_token","runId"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"402":{"description":"Free-tier connection limit reached — upgrade required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"403":{"description":"Not available in demo mode","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}},"502":{"description":"Plaid API failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}}},"/api/plaid/resolveLink":{"post":{"summary":"Resolve a parked add-account workflow (Plaid onSuccess or onExit)","tags":["Plaid"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"cancelled":{"type":"boolean"},"hookToken":{"type":"string"},"publicToken":{"type":"string"}},"required":["hookToken"]}}}},"responses":{"200":{"description":"Workflow resumed","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"Invalid payload or foreign hook token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"403":{"description":"Not available in demo mode","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}},"500":{"description":"Server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}}},"/api/plaid/linkToken/update":{"post":{"summary":"Mint an update-mode link token and start the parked workflow","tags":["Plaid"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"mode":{"type":"string","enum":["add-accounts","add-products","reauth"]},"plaidItemId":{"type":"string"}},"required":["mode","plaidItemId"]}}}},"responses":{"200":{"description":"Link token minted; workflow parked on hook. Client must echo the Plaid Link outcome via /resolveLink","content":{"application/json":{"schema":{"type":"object","properties":{"hookToken":{"type":"string"},"institutionLogo":{"type":["string","null"]},"institutionName":{"type":["string","null"]},"institutionUrl":{"type":["string","null"]},"link_token":{"type":"string"},"mode":{"type":"string","enum":["update","reauth"]},"plaidItemId":{"type":"string"},"runId":{"type":"string"}},"required":["hookToken","link_token","runId"]}}}},"400":{"description":"Unsupported update mode","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"404":{"description":"Plaid item not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}},"502":{"description":"Plaid API failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}}},"/api/plaid/balance-snapshots":{"get":{"summary":"Get balance snapshots","tags":["Plaid"],"parameters":[{"schema":{"type":"string"},"required":false,"name":"accountId","in":"query"},{"schema":{"type":"string"},"required":false,"name":"endDate","in":"query"},{"schema":{"type":"string"},"required":false,"name":"startDate","in":"query"}],"responses":{"200":{"description":"Balance snapshots","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BalanceSnapshotsResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}}}},"/api/appstore/sync":{"post":{"deprecated":true,"description":"DEPRECATED: iOS migrated to Stripe-only paywall (SRI-102). Legacy StoreKit clients may still call this; receives no traffic from current builds. Slated for removal after two releases of zero hits — see issue #364. Persists App Store subscription data after StoreKit reports a purchase.","summary":"Sync App Store subscription (StoreKit)","tags":["App Store"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"environment":{"type":"string","enum":["Production","Sandbox"],"default":"Production"},"expiresAt":{"type":"string","minLength":1},"latestTransactionId":{"type":["string","null"]},"originalTransactionId":{"type":"string"},"productId":{"type":"string"}},"required":["expiresAt","originalTransactionId","productId"]}}}},"responses":{"200":{"description":"Subscription created or updated","content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["created","transferred","updated"]},"success":{"type":"boolean","enum":[true]},"subscriptionId":{"type":"string"}},"required":["action","success","subscriptionId"]}}}},"400":{"description":"Invalid body or dates","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}},"502":{"description":"App Store upstream failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}}}},"webhooks":{}}