{"info":{"description":"Cobalt public REST API. Stable, versioned, SDK-generated.","title":"Cobalt API","version":"1.0.0"},"openapi":"3.1.0","security":[{"bearerAuth":[]}],"servers":[{"url":"https://api.cobaltpf.com/v1"}],"components":{"securitySchemes":{"bearerAuth":{"bearerFormat":"API key","description":"Cobalt API key (prefix `ck_live_`). Issue from dashboard → Settings → API keys.","scheme":"bearer","type":"http"}},"schemas":{"AccountList":{"type":"array","items":{"$ref":"#/components/schemas/Account"}},"Account":{"type":"object","properties":{"balance":{"type":["number","null"],"description":"Signed balance in the account's currency. Positive for assets (bank, investment), negative for liabilities (credit_card, loan). Net worth = sum of balances. Null when the provider has not reported one."},"creditLimit":{"type":["number","null"],"description":"Credit limit for credit-card accounts. Null for non-credit accounts."},"currency":{"type":["string","null"],"example":"USD"},"id":{"type":"string","description":"Stable account identifier."},"institution":{"type":["string","null"],"description":"Name of the institution holding the account."},"mask":{"type":["string","null"],"description":"Last 4 digits of the account number when reported by the provider. Best-effort — some institutions (e.g. Venmo, Fidelity) return null even when the underlying account has a number. Do not rely on `mask` for account identity; use `id`."},"name":{"type":"string","description":"Account display name."},"type":{"$ref":"#/components/schemas/AccountType"}},"required":["balance","creditLimit","currency","id","institution","mask","name","type"]},"AccountType":{"type":"string","enum":["bank","credit_card","investment","loan","other"]},"ErrorResponseWithCode":{"type":"object","properties":{"code":{"type":"string"},"error":{"type":"string"}},"required":["code","error"]},"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."},"AccountCreateResponse":{"type":"object","properties":{"balance":{"type":["number","null"],"description":"Signed balance in the account's currency. Positive for assets (bank, investment), negative for liabilities (credit_card, loan). Net worth = sum of balances. Null when the provider has not reported one."},"creditLimit":{"type":["number","null"],"description":"Credit limit for credit-card accounts. Null for non-credit accounts."},"currency":{"type":["string","null"],"example":"USD"},"id":{"type":"string","description":"Stable account identifier."},"institution":{"type":["string","null"],"description":"Name of the institution holding the account."},"mask":{"type":["string","null"],"description":"Last 4 digits of the account number when reported by the provider. Best-effort — some institutions (e.g. Venmo, Fidelity) return null even when the underlying account has a number. Do not rely on `mask` for account identity; use `id`."},"name":{"type":"string","description":"Account display name."},"type":{"$ref":"#/components/schemas/AccountType"}},"required":["balance","creditLimit","currency","id","institution","mask","name","type"]},"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"]},"AccountCreateRequest":{"type":"object","properties":{"creditLimit":{"type":"number","exclusiveMinimum":0,"description":"Credit limit. Only valid when `type === \"credit_card\"`."},"currency":{"type":"string","minLength":3,"maxLength":3,"default":"USD"},"currentBalance":{"type":"number","description":"Signed balance. Positive for assets, negative for liabilities (credit_card, loan). Magnitude stored internally."},"logoDomain":{"type":"string","maxLength":253},"name":{"type":"string","minLength":1,"maxLength":255},"subtype":{"type":"string","enum":["checking","savings","cash","credit card","line of credit","brokerage","ira","roth ira","401k","hsa","crypto","mortgage","student","auto","personal"]},"type":{"type":"string","enum":["bank","credit_card","investment","loan"]}},"required":["currentBalance","name","subtype","type"]},"AccountDetail":{"type":"object","properties":{"balance":{"type":["number","null"],"description":"Signed balance in the account's currency. Positive for assets (bank, investment), negative for liabilities (credit_card, loan). Net worth = sum of balances. Null when the provider has not reported one."},"creditLimit":{"type":["number","null"],"description":"Credit limit for credit-card accounts. Null for non-credit accounts."},"currency":{"type":["string","null"],"example":"USD"},"id":{"type":"string","description":"Stable account identifier."},"institution":{"type":["string","null"],"description":"Name of the institution holding the account."},"mask":{"type":["string","null"],"description":"Last 4 digits of the account number when reported by the provider. Best-effort — some institutions (e.g. Venmo, Fidelity) return null even when the underlying account has a number. Do not rely on `mask` for account identity; use `id`."},"name":{"type":"string","description":"Account display name."},"type":{"$ref":"#/components/schemas/AccountType"}},"required":["balance","creditLimit","currency","id","institution","mask","name","type"]},"TransactionList":{"type":"object","properties":{"hasMore":{"type":"boolean"},"items":{"type":"array","items":{"$ref":"#/components/schemas/Transaction"}},"nextCursor":{"type":["string","null"]}},"required":["hasMore","items","nextCursor"]},"Transaction":{"type":"object","properties":{"accountId":{"type":"string","description":"Identifier of the account this transaction belongs to."},"amount":{"type":"number","description":"Signed amount. Positive = money in (credit / refund / income), negative = money out (debit / spending)."},"category":{"type":["string","null"],"description":"Category name; null if uncategorized."},"date":{"type":"string","description":"Transaction date (YYYY-MM-DD)."},"id":{"type":"string"},"location":{"$ref":"#/components/schemas/TransactionLocation"},"merchant":{"type":["string","null"]},"name":{"type":"string","description":"Raw description from the institution."},"notes":{"type":["string","null"],"description":"Additional details regarding the transaction. Supports Markdown.","example":"**Reimbursable** — paid for team lunch, expense via Expensify"},"pending":{"type":"boolean"},"tagIds":{"type":"array","items":{"type":"string"}}},"required":["accountId","amount","category","date","id","location","merchant","name","notes","pending","tagIds"]},"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"],"description":"Merchant location when reported by the institution. Null when no location fields are available."},"TransactionDetail":{"type":"object","properties":{"accountId":{"type":"string","description":"Identifier of the account this transaction belongs to."},"amount":{"type":"number","description":"Signed amount. Positive = money in (credit / refund / income), negative = money out (debit / spending)."},"category":{"type":["string","null"],"description":"Category name; null if uncategorized."},"date":{"type":"string","description":"Transaction date (YYYY-MM-DD)."},"id":{"type":"string"},"location":{"$ref":"#/components/schemas/TransactionLocation"},"merchant":{"type":["string","null"]},"name":{"type":"string","description":"Raw description from the institution."},"notes":{"type":["string","null"],"description":"Additional details regarding the transaction. Supports Markdown.","example":"**Reimbursable** — paid for team lunch, expense via Expensify"},"pending":{"type":"boolean"},"tagIds":{"type":"array","items":{"type":"string"}}},"required":["accountId","amount","category","date","id","location","merchant","name","notes","pending","tagIds"]},"TransactionCreate":{"type":"object","properties":{"accountId":{"type":"string","description":"Account to attach this transaction to. Must be a manual account.","example":"01HX8N7K5Q3M2P9R4V6Y8Z1A2B"},"amount":{"type":"number","description":"Positive = money in (refund/income), negative = money out (spending).","example":-24.5},"categoryId":{"type":"string","format":"uuid","description":"Category id from `GET /v1/categories`. Omit to leave uncategorized.","example":"8f3b2a1e-4c5d-6e7f-8a9b-0c1d2e3f4a5b"},"date":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","example":"2026-05-22"},"location":{"allOf":[{"$ref":"#/components/schemas/TransactionLocation"},{"description":"Merchant location. When set, `location` is added to lockedFields so future syncs can't overwrite it."}]},"merchantName":{"type":"string","minLength":1,"maxLength":255,"example":"Blue Bottle Coffee"},"name":{"type":"string","minLength":1,"maxLength":255,"description":"Transaction description.","example":"Blue Bottle Coffee — Mission"},"notes":{"type":"string","maxLength":2000,"description":"Additional details regarding the transaction. Supports Markdown.","example":"**Reimbursable** — paid for team lunch, expense via Expensify"},"tagIds":{"type":"array","items":{"type":"string","format":"uuid"},"description":"Tag ids to attach (from `GET /v1/tags`).","example":["a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d"]},"website":{"type":"string","minLength":3,"maxLength":2048,"pattern":"^[^\\s]+\\.[^\\s]+$","description":"Merchant website. Bare domain or full URL.","example":"bluebottlecoffee.com"}},"required":["accountId","amount","date","name"]},"TransactionTagsUpdate":{"type":"object","properties":{"tagIds":{"type":"array","items":{"type":"string"},"description":"Full replacement set of tag ids. Pass `[]` to clear all tags."}},"required":["tagIds"]},"TagList":{"type":"array","items":{"$ref":"#/components/schemas/Tag"}},"Tag":{"type":"object","properties":{"archivedAt":{"type":["string","null"]},"color":{"type":["string","null"]},"createdAt":{"type":"string"},"id":{"type":"string"},"name":{"type":"string"}},"required":["archivedAt","color","createdAt","id","name"]},"TagDetail":{"type":"object","properties":{"archivedAt":{"type":["string","null"]},"color":{"type":["string","null"]},"createdAt":{"type":"string"},"id":{"type":"string"},"name":{"type":"string"}},"required":["archivedAt","color","createdAt","id","name"]},"TagColor":{"type":"string","enum":["red","orange","amber","yellow","lime","green","teal","cyan","blue","indigo","violet","purple","pink","rose","slate","stone"]},"TagsBulkApplyResponse":{"type":"object","properties":{"success":{"type":"boolean"},"updatedCount":{"type":"integer"}},"required":["success","updatedCount"]},"PositionList":{"type":"array","items":{"$ref":"#/components/schemas/Position"}},"Position":{"type":"object","properties":{"accountId":{"type":"string","description":"Account this position belongs to."},"averagePrice":{"type":["number","null"],"description":"Cost basis per share."},"currency":{"type":["string","null"]},"description":{"type":["string","null"],"description":"Security long name."},"id":{"type":"string"},"marketValue":{"type":["number","null"],"description":"units × price."},"openPnl":{"type":["number","null"],"description":"Unrealized profit/loss."},"price":{"type":["number","null"],"description":"Latest reported price per share."},"symbol":{"type":["string","null"],"description":"Ticker symbol (e.g. AAPL)."},"units":{"type":["number","null"],"description":"Quantity held."}},"required":["accountId","averagePrice","currency","description","id","marketValue","openPnl","price","symbol","units"]},"ActivityList":{"type":"array","items":{"$ref":"#/components/schemas/Activity"}},"Activity":{"type":"object","properties":{"accountId":{"type":"string"},"amount":{"type":["number","null"],"description":"Cash impact. Positive = cash in (sell, dividend), negative = cash out (buy, fee)."},"currency":{"type":["string","null"]},"description":{"type":["string","null"]},"fee":{"type":["number","null"]},"id":{"type":"string"},"price":{"type":["number","null"]},"settlementDate":{"type":["string","null"]},"symbol":{"type":["string","null"]},"tradeDate":{"type":["string","null"]},"type":{"type":["string","null"],"description":"Activity kind (e.g. BUY, SELL, DIVIDEND, FEE, INTEREST, CONTRIBUTION, WITHDRAWAL). Provider-defined; open-ended."},"units":{"type":["number","null"]}},"required":["accountId","amount","currency","description","fee","id","price","settlementDate","symbol","tradeDate","type","units"]},"PortfolioSnapshotList":{"type":"array","items":{"$ref":"#/components/schemas/PortfolioSnapshot"}},"PortfolioSnapshot":{"type":"object","properties":{"accountId":{"type":"string"},"date":{"type":"string","example":"2026-05-22"},"id":{"type":"string"},"value":{"type":"number","description":"Total portfolio value at end-of-day."}},"required":["accountId","date","id","value"]},"BalanceSnapshotList":{"type":"array","items":{"$ref":"#/components/schemas/BalanceSnapshot"}},"BalanceSnapshot":{"type":"object","properties":{"accountId":{"type":"string"},"availableBalance":{"type":["number","null"],"description":"Funds available to spend (current minus pending). Null when unreported."},"creditLimit":{"type":["number","null"],"description":"Credit limit for credit-card accounts. Null for non-credit accounts."},"currentBalance":{"type":"number","description":"Posted balance at end-of-day. Signed: positive for assets, negative for liabilities."},"date":{"type":"string","example":"2026-05-22"},"id":{"type":"string"}},"required":["accountId","availableBalance","creditLimit","currentBalance","date","id"]},"CategoryList":{"type":"object","properties":{"categories":{"type":"array","items":{"$ref":"#/components/schemas/Category"}},"groups":{"type":"array","items":{"$ref":"#/components/schemas/CategoryGroup"}}},"required":["categories","groups"]},"Category":{"type":"object","properties":{"excludeFromInsights":{"type":["boolean","null"],"description":"When true, transactions in this category are excluded from spending insights (e.g. transfers)."},"groupId":{"type":["string","null"],"description":"Parent category group id."},"hidden":{"type":["boolean","null"]},"iconKey":{"type":["string","null"]},"id":{"type":"string"},"name":{"type":"string"},"systemKey":{"type":["string","null"],"description":"Stable identifier for seeded system categories (e.g. `groceries`). Null for user-created categories."}},"required":["excludeFromInsights","groupId","hidden","iconKey","id","name","systemKey"]},"CategoryGroup":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"systemKey":{"type":["string","null"]}},"required":["id","name","systemKey"]},"CategoryDetailResponse":{"type":"object","properties":{"excludeFromInsights":{"type":["boolean","null"],"description":"When true, transactions in this category are excluded from spending insights (e.g. transfers)."},"groupId":{"type":["string","null"],"description":"Parent category group id."},"hidden":{"type":["boolean","null"]},"iconKey":{"type":["string","null"]},"id":{"type":"string"},"name":{"type":"string"},"systemKey":{"type":["string","null"],"description":"Stable identifier for seeded system categories (e.g. `groceries`). Null for user-created categories."}},"required":["excludeFromInsights","groupId","hidden","iconKey","id","name","systemKey"]},"RecurringStreamList":{"type":"array","items":{"$ref":"#/components/schemas/RecurringStream"}},"RecurringStream":{"type":"object","properties":{"accountId":{"type":"string"},"averageAmount":{"type":"number"},"category":{"allOf":[{"$ref":"#/components/schemas/Category"},{"type":["object","null"]}]},"description":{"type":["string","null"]},"firstDate":{"type":["string","null"]},"frequency":{"$ref":"#/components/schemas/RecurringStreamFrequency"},"id":{"type":"string"},"isActive":{"type":"boolean"},"lastAmount":{"type":"number"},"lastDate":{"type":["string","null"]},"merchantName":{"type":["string","null"]},"predictedNextDate":{"type":["string","null"],"description":"Best-effort guess at the next charge date."},"status":{"$ref":"#/components/schemas/RecurringStreamStatus"},"streamType":{"$ref":"#/components/schemas/RecurringStreamType"}},"required":["accountId","averageAmount","category","description","firstDate","frequency","id","isActive","lastAmount","lastDate","merchantName","predictedNextDate","status","streamType"]},"RecurringStreamFrequency":{"type":"string","enum":["UNKNOWN","WEEKLY","BIWEEKLY","SEMI_MONTHLY","MONTHLY","ANNUALLY"],"description":"Detected cadence."},"RecurringStreamStatus":{"type":["string","null"],"enum":["UNKNOWN","MATURE","EARLY_DETECTION","TOMBSTONED",null],"description":"Detection state of the stream."},"RecurringStreamType":{"type":["string","null"],"enum":["inflow","outflow",null],"description":"Direction of cash flow."},"SpendingResponse":{"type":"object","properties":{"averageLabel":{"type":"string","enum":["daily","weekly","monthly","yearly"]},"averageSpending":{"type":"number"},"buckets":{"type":"array","items":{"$ref":"#/components/schemas/SpendingBucket"}},"totalSpending":{"type":"number"}},"required":["averageLabel","averageSpending","buckets","totalSpending"]},"SpendingBucket":{"type":"object","properties":{"amount":{"type":"number","description":"Total spending in the bucket window."},"date":{"type":"string","description":"Bucket start date."}},"required":["amount","date"]}},"parameters":{}},"paths":{"/accounts":{"get":{"description":"Returns every account the API key's owner has connected — checking, savings, credit, brokerage, and manual.","operationId":"accounts_list","security":[{"bearerAuth":[]}],"summary":"List accounts","tags":["Accounts"],"responses":{"200":{"description":"Accounts owned by the authenticated user","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountList"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}},"post":{"description":"Create a manual (non-bank-linked) account — bank, credit_card, investment, or loan — and seed today's balance snapshot. `subtype` must belong to the chosen `type` vocabulary (see schema). `creditLimit` only valid when `type === \"credit_card\"`. `currentBalance` is signed: positive for assets, negative for liabilities.","operationId":"accounts_create","security":[{"bearerAuth":[]}],"summary":"Create manual account","tags":["Accounts"],"requestBody":{"description":"Account to create","required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountCreateRequest"}}}},"responses":{"201":{"description":"Created account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountCreateResponse"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}}}},"/accounts/brokerage":{"get":{"description":"All brokerage-shaped accounts (SnapTrade, Plaid investment, manual investment). Inspect `source` on each item to distinguish. SnapTrade-only fields (`snaptradeAuthorizationId`, `needsReauth`) are null/false for non-SnapTrade rows.","operationId":"accounts_brokerage_list","security":[{"bearerAuth":[]}],"summary":"List brokerage accounts","tags":["Accounts"],"parameters":[{"schema":{"type":"string","enum":["snaptrade","plaid","manual","all"],"description":"Filter by account origin. Default `all`."},"required":false,"description":"Filter by account origin. Default `all`.","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":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}}},"/accounts/{id}":{"get":{"description":"Fetch a single account by identifier.","operationId":"accounts_get","security":[{"bearerAuth":[]}],"summary":"Get account","tags":["Accounts"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Account detail","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountDetail"}}}},"401":{"description":"Missing or invalid API key","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"}}}}}},"delete":{"description":"Delete an account by id. Source-agnostic: manual accounts are removed outright, Plaid-linked accounts are unlinked (and Plaid's `/item/remove` is called once the item drains), and SnapTrade brokerage accounts are disconnected upstream.","operationId":"accounts_delete","security":[{"bearerAuth":[]}],"summary":"Delete account","tags":["Accounts"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"204":{"description":"Account deleted"},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"404":{"description":"Account not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"409":{"description":"Account cannot be deleted via this endpoint","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}}}},"/transactions":{"get":{"description":"Returns transactions across all of the user's accounts, newest first. Use `nextCursor` to page. **Note:** `amount` is signed — positive = money in (credit/refund/income), negative = money out (debit/spending).","operationId":"transactions_list","security":[{"bearerAuth":[]}],"summary":"List transactions","tags":["Transactions"],"parameters":[{"schema":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Restrict to one or more account ids. Repeat the param (`?accountId=a&accountId=b`) for multiple."},"required":false,"description":"Restrict to one or more account ids. Repeat the param (`?accountId=a&accountId=b`) for multiple.","name":"accountId","in":"query"},{"schema":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Restrict to one or more category group systemKeys. Repeat the param (`?categoryGroup=food_and_drink&categoryGroup=travel`) for multiple."},"required":false,"description":"Restrict to one or more category group systemKeys. Repeat the param (`?categoryGroup=food_and_drink&categoryGroup=travel`) for multiple.","name":"categoryGroup","in":"query"},{"schema":{"type":"string","description":"Opaque cursor returned by the previous page. Omit for first page."},"required":false,"description":"Opaque cursor returned by the previous page. Omit for first page.","name":"cursor","in":"query"},{"schema":{"type":"string","example":"2026-05-22"},"required":false,"name":"endDate","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":200,"default":50},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","example":"2026-01-01"},"required":false,"name":"startDate","in":"query"}],"responses":{"200":{"description":"Paginated transactions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TransactionList"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}}},"post":{"description":"Add a manual transaction. The target `accountId` must reference a manual (not bank-synced) account. **Note:** `amount` is signed — positive = money in (credit/refund/income), negative = money out (debit/spending).","operationId":"transactions_create","security":[{"bearerAuth":[]}],"summary":"Create transaction","tags":["Transactions"],"requestBody":{"description":"Transaction to create","required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TransactionCreate"}}}},"responses":{"201":{"description":"Created transaction","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TransactionDetail"}}}},"400":{"description":"Target account is not manual or does not exist","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}}}},"/transactions/{transactionId}":{"get":{"description":"Fetch a single transaction by identifier.","operationId":"transactions_get","security":[{"bearerAuth":[]}],"summary":"Get transaction","tags":["Transactions"],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"transactionId","in":"path"}],"responses":{"200":{"description":"Transaction detail","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TransactionDetail"}}}},"401":{"description":"Missing or invalid API key","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"}}}}}},"patch":{"description":"Sparse partial update — only fields present in the body are written. Pass `null` to restore the original (provider-derived) value. Returns the updated transaction.","operationId":"transactions_update","security":[{"bearerAuth":[]}],"summary":"Update transaction","tags":["Transactions"],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"transactionId","in":"path"}],"requestBody":{"description":"Fields to update","required":true,"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},"tags":{"type":"array","items":{"type":"string","format":"uuid"}},"website":{"type":["string","null"],"minLength":3,"maxLength":2048,"pattern":"^[^\\s]+\\.[^\\s]+$"}}}}}},"responses":{"200":{"description":"Updated transaction","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TransactionDetail"}}}},"401":{"description":"Missing or invalid API key","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 — returns success even if the transaction is already gone or is provider-managed (Plaid/SnapTrade).","operationId":"transactions_delete","security":[{"bearerAuth":[]}],"summary":"Delete 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":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}}},"/transactions/{transactionId}/tags":{"put":{"description":"Replace the set of tags on a transaction. Use `GET /v1/tags` to discover available tag ids.","operationId":"transactions_updateTags","security":[{"bearerAuth":[]}],"summary":"Set transaction tags","tags":["Transactions"],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"transactionId","in":"path"}],"requestBody":{"description":"Tag ids to attach","required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TransactionTagsUpdate"}}}},"responses":{"200":{"description":"Updated transaction","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TransactionDetail"}}}},"401":{"description":"Missing or invalid API key","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"}}}}}}},"/tags":{"get":{"description":"List every tag owned by the authenticated user.","operationId":"tags_list","security":[{"bearerAuth":[]}],"summary":"List tags","tags":["Tags"],"responses":{"200":{"description":"Tags owned by the authenticated user","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TagList"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}},"post":{"description":"Create a new tag. Names must be unique per user (case-insensitive).","operationId":"tags_create","security":[{"bearerAuth":[]}],"summary":"Create tag","tags":["Tags"],"requestBody":{"description":"Tag to create","required":true,"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":"Created tag","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TagDetail"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"409":{"description":"A tag with that name already exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}}}},"/tags/bulk-apply":{"post":{"description":"Add and/or remove tags across many transactions in one call.","operationId":"tags_bulkApply","security":[{"bearerAuth":[]}],"summary":"Bulk apply tags","tags":["Tags"],"requestBody":{"description":"Transactions + tag add/remove sets","required":true,"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":{"$ref":"#/components/schemas/TagsBulkApplyResponse"}}}},"401":{"description":"Missing or invalid API key","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"}}}}}}},"/tags/{tagId}":{"get":{"description":"Fetch a single tag by identifier.","operationId":"tags_get","security":[{"bearerAuth":[]}],"summary":"Get tag","tags":["Tags"],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"tagId","in":"path"}],"responses":{"200":{"description":"Tag detail","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TagDetail"}}}},"401":{"description":"Missing or invalid API key","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"}}}}}},"patch":{"description":"Update name, color, or archived state of a tag.","operationId":"tags_update","security":[{"bearerAuth":[]}],"summary":"Update tag","tags":["Tags"],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"tagId","in":"path"}],"requestBody":{"description":"Fields to update","required":true,"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":"Updated tag","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TagDetail"}}}},"401":{"description":"Missing or invalid API key","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 delete a tag and detach it from all transactions.","operationId":"tags_delete","security":[{"bearerAuth":[]}],"summary":"Delete tag","tags":["Tags"],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"tagId","in":"path"}],"responses":{"204":{"description":"Tag deleted"},"401":{"description":"Missing or invalid API key","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"}}}}}}},"/positions":{"get":{"description":"List brokerage positions (stock, ETF, crypto holdings) across the user's investment accounts.","operationId":"positions_list","security":[{"bearerAuth":[]}],"summary":"List positions","tags":["Positions"],"parameters":[{"schema":{"type":"string","description":"Filter to a single brokerage account."},"required":false,"description":"Filter to a single brokerage account.","name":"accountId","in":"query"}],"responses":{"200":{"description":"Positions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PositionList"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}}},"/activities":{"get":{"description":"Brokerage activity history — buys, sells, dividends, fees, transfers across investment accounts.","operationId":"activities_list","security":[{"bearerAuth":[]}],"summary":"List activities","tags":["Activities"],"parameters":[{"schema":{"type":"string","description":"Filter to a single brokerage account."},"required":false,"description":"Filter to a single brokerage account.","name":"accountId","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":500},"required":false,"name":"limit","in":"query"},{"schema":{"type":["integer","null"],"minimum":0},"required":false,"name":"offset","in":"query"}],"responses":{"200":{"description":"Activities","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActivityList"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}}},"/portfolio/snapshots":{"get":{"description":"End-of-day portfolio value snapshots. Defaults to the trailing 6 months when no dates are supplied.","operationId":"portfolio_snapshots","security":[{"bearerAuth":[]}],"summary":"List portfolio snapshots","tags":["Portfolio"],"parameters":[{"schema":{"type":"string","description":"Filter to a single brokerage account."},"required":false,"description":"Filter to a single brokerage account.","name":"accountId","in":"query"},{"schema":{"type":"string","example":"2026-05-22"},"required":false,"name":"endDate","in":"query"},{"schema":{"type":"string","example":"2026-01-01"},"required":false,"name":"startDate","in":"query"}],"responses":{"200":{"description":"Snapshots","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortfolioSnapshotList"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}}},"/balances/snapshots":{"get":{"description":"End-of-day balance history for non-brokerage accounts (checking, savings, credit). Use `/v1/portfolio/snapshots` for brokerage value series.","operationId":"balances_snapshots","security":[{"bearerAuth":[]}],"summary":"List balance snapshots","tags":["Balances"],"parameters":[{"schema":{"type":"string","description":"Filter to a single account."},"required":false,"description":"Filter to a single account.","name":"accountId","in":"query"},{"schema":{"type":"string","example":"2026-05-22"},"required":false,"name":"endDate","in":"query"},{"schema":{"type":"string","example":"2026-01-01"},"required":false,"name":"startDate","in":"query"}],"responses":{"200":{"description":"Balance snapshots","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BalanceSnapshotList"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}}},"/categories":{"get":{"description":"Returns the spending category taxonomy for the user — both system-seeded categories and user-created ones.","operationId":"categories_list","security":[{"bearerAuth":[]}],"summary":"List categories","tags":["Categories"],"responses":{"200":{"description":"Categories + groups","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CategoryList"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}},"post":{"description":"Create a custom category under an existing group.","operationId":"categories_create","security":[{"bearerAuth":[]}],"summary":"Create category","tags":["Categories"],"requestBody":{"description":"Category to create","required":true,"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":"Created category","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CategoryDetailResponse"}}}},"401":{"description":"Missing or invalid API key","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"}}}}}}},"/categories/{categoryId}":{"patch":{"description":"Rename, recolor, hide, reorder, or move a category between groups.","operationId":"categories_update","security":[{"bearerAuth":[]}],"summary":"Update category","tags":["Categories"],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"categoryId","in":"path"}],"requestBody":{"description":"Fields to update","required":true,"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":"Updated category","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CategoryDetailResponse"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"404":{"description":"Category or group not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"409":{"description":"Operation not permitted on this category","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}}}},"delete":{"description":"Soft-delete a custom category. Dependent transactions and recurring rows are reassigned to the user's Uncategorized seed category. System categories cannot be deleted — hide them instead.","operationId":"categories_delete","security":[{"bearerAuth":[]}],"summary":"Delete category","tags":["Categories"],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"categoryId","in":"path"}],"responses":{"204":{"description":"Category deleted"},"401":{"description":"Missing or invalid API key","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"}}}}}}},"/recurring":{"get":{"description":"Detected recurring streams — subscriptions, bills, and recurring deposits. Only active streams are returned.","operationId":"recurring_list","security":[{"bearerAuth":[]}],"summary":"List recurring streams","tags":["Recurring"],"responses":{"200":{"description":"Recurring streams","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecurringStreamList"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}}},"/spending":{"get":{"description":"Aggregated spending over a time window. Returns a bucket series for charting plus running total + average.","operationId":"spending_get","security":[{"bearerAuth":[]}],"summary":"Get spending","tags":["Spending"],"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"],"default":"1m"},"required":false,"name":"period","in":"query"}],"responses":{"200":{"description":"Spending aggregate","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpendingResponse"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseWithCode"}}}}}}}},"webhooks":{}}