fin-infra 0.5.0__tar.gz → 0.6.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (180) hide show
  1. {fin_infra-0.5.0 → fin_infra-0.6.0}/PKG-INFO +1 -1
  2. {fin_infra-0.5.0 → fin_infra-0.6.0}/pyproject.toml +1 -1
  3. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/banking/__init__.py +49 -2
  4. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/providers/banking/plaid_client.py +29 -9
  5. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/providers/banking/teller_client.py +13 -5
  6. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/providers/base.py +38 -2
  7. {fin_infra-0.5.0 → fin_infra-0.6.0}/LICENSE +0 -0
  8. {fin_infra-0.5.0 → fin_infra-0.6.0}/README.md +0 -0
  9. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/__init__.py +0 -0
  10. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/__main__.py +0 -0
  11. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/analytics/__init__.py +0 -0
  12. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/analytics/add.py +0 -0
  13. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/analytics/cash_flow.py +0 -0
  14. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/analytics/ease.py +0 -0
  15. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/analytics/models.py +0 -0
  16. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/analytics/portfolio.py +0 -0
  17. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/analytics/projections.py +0 -0
  18. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/analytics/rebalancing.py +0 -0
  19. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/analytics/savings.py +0 -0
  20. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/analytics/scenarios.py +0 -0
  21. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/analytics/spending.py +0 -0
  22. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/banking/history.py +0 -0
  23. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/banking/utils.py +0 -0
  24. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/brokerage/__init__.py +0 -0
  25. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/budgets/__init__.py +0 -0
  26. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/budgets/add.py +0 -0
  27. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/budgets/alerts.py +0 -0
  28. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/budgets/ease.py +0 -0
  29. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/budgets/models.py +0 -0
  30. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/budgets/scaffold_templates/README.md +0 -0
  31. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/budgets/scaffold_templates/__init__.py +0 -0
  32. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/budgets/scaffold_templates/models.py.tmpl +0 -0
  33. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/budgets/scaffold_templates/repository.py.tmpl +0 -0
  34. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/budgets/scaffold_templates/schemas.py.tmpl +0 -0
  35. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/budgets/templates.py +0 -0
  36. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/budgets/tracker.py +0 -0
  37. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/cashflows/__init__.py +0 -0
  38. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/cashflows/core.py +0 -0
  39. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/categorization/__init__.py +0 -0
  40. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/categorization/add.py +0 -0
  41. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/categorization/ease.py +0 -0
  42. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/categorization/engine.py +0 -0
  43. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/categorization/llm_layer.py +0 -0
  44. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/categorization/models.py +0 -0
  45. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/categorization/rules.py +0 -0
  46. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/categorization/taxonomy.py +0 -0
  47. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/chat/__init__.py +0 -0
  48. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/chat/ease.py +0 -0
  49. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/chat/planning.py +0 -0
  50. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/cli/__init__.py +0 -0
  51. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/cli/cmds/__init__.py +0 -0
  52. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/cli/cmds/scaffold_cmds.py +0 -0
  53. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/clients/__init__.py +0 -0
  54. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/clients/base.py +0 -0
  55. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/clients/plaid.py +0 -0
  56. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/compliance/__init__.py +0 -0
  57. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/credit/__init__.py +0 -0
  58. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/credit/add.py +0 -0
  59. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/credit/experian/__init__.py +0 -0
  60. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/credit/experian/auth.py +0 -0
  61. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/credit/experian/client.py +0 -0
  62. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/credit/experian/parser.py +0 -0
  63. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/credit/experian/provider.py +0 -0
  64. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/credit/mock.py +0 -0
  65. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/crypto/__init__.py +0 -0
  66. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/crypto/insights.py +0 -0
  67. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/documents/__init__.py +0 -0
  68. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/documents/add.py +0 -0
  69. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/documents/analysis.py +0 -0
  70. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/documents/ease.py +0 -0
  71. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/documents/models.py +0 -0
  72. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/documents/ocr.py +0 -0
  73. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/documents/storage.py +0 -0
  74. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/exceptions.py +0 -0
  75. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/goals/__init__.py +0 -0
  76. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/goals/add.py +0 -0
  77. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/goals/funding.py +0 -0
  78. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/goals/management.py +0 -0
  79. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/goals/milestones.py +0 -0
  80. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/goals/models.py +0 -0
  81. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/goals/scaffold_templates/README.md +0 -0
  82. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/goals/scaffold_templates/__init__.py +0 -0
  83. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/goals/scaffold_templates/models.py.tmpl +0 -0
  84. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/goals/scaffold_templates/repository.py.tmpl +0 -0
  85. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/goals/scaffold_templates/schemas.py.tmpl +0 -0
  86. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/insights/__init__.py +0 -0
  87. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/insights/aggregator.py +0 -0
  88. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/insights/models.py +0 -0
  89. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/investments/__init__.py +0 -0
  90. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/investments/add.py +0 -0
  91. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/investments/ease.py +0 -0
  92. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/investments/models.py +0 -0
  93. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/investments/providers/__init__.py +0 -0
  94. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/investments/providers/base.py +0 -0
  95. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/investments/providers/plaid.py +0 -0
  96. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/investments/providers/snaptrade.py +0 -0
  97. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/investments/scaffold_templates/README.md +0 -0
  98. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/investments/scaffold_templates/__init__.py +0 -0
  99. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/investments/scaffold_templates/models.py.tmpl +0 -0
  100. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/investments/scaffold_templates/repository.py.tmpl +0 -0
  101. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/investments/scaffold_templates/schemas.py.tmpl +0 -0
  102. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/markets/__init__.py +0 -0
  103. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/models/__init__.py +0 -0
  104. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/models/accounts.py +0 -0
  105. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/models/brokerage.py +0 -0
  106. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/models/candle.py +0 -0
  107. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/models/credit.py +0 -0
  108. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/models/money.py +0 -0
  109. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/models/quotes.py +0 -0
  110. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/models/tax.py +0 -0
  111. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/models/transactions.py +0 -0
  112. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/net_worth/__init__.py +0 -0
  113. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/net_worth/add.py +0 -0
  114. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/net_worth/aggregator.py +0 -0
  115. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/net_worth/calculator.py +0 -0
  116. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/net_worth/ease.py +0 -0
  117. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/net_worth/goals.py +0 -0
  118. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/net_worth/insights.py +0 -0
  119. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/net_worth/models.py +0 -0
  120. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/net_worth/scaffold_templates/README.md +0 -0
  121. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/net_worth/scaffold_templates/__init__.py +0 -0
  122. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/net_worth/scaffold_templates/models.py.tmpl +0 -0
  123. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/net_worth/scaffold_templates/repository.py.tmpl +0 -0
  124. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/net_worth/scaffold_templates/schemas.py.tmpl +0 -0
  125. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/normalization/__init__.py +0 -0
  126. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/normalization/currency_converter.py +0 -0
  127. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/normalization/models.py +0 -0
  128. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/normalization/providers/__init__.py +0 -0
  129. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/normalization/providers/exchangerate.py +0 -0
  130. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/normalization/providers/static_mappings.py +0 -0
  131. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/normalization/symbol_resolver.py +0 -0
  132. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/obs/__init__.py +0 -0
  133. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/obs/classifier.py +0 -0
  134. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/providers/__init__.py +0 -0
  135. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/providers/banking/base.py +0 -0
  136. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/providers/brokerage/alpaca.py +0 -0
  137. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/providers/brokerage/base.py +0 -0
  138. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/providers/credit/experian.py +0 -0
  139. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/providers/identity/stripe_identity.py +0 -0
  140. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/providers/market/alphavantage.py +0 -0
  141. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/providers/market/base.py +0 -0
  142. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/providers/market/ccxt_crypto.py +0 -0
  143. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/providers/market/coingecko.py +0 -0
  144. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/providers/market/yahoo.py +0 -0
  145. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/providers/registry.py +0 -0
  146. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/providers/tax/__init__.py +0 -0
  147. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/providers/tax/irs.py +0 -0
  148. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/providers/tax/mock.py +0 -0
  149. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/providers/tax/taxbit.py +0 -0
  150. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/py.typed +0 -0
  151. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/recurring/__init__.py +0 -0
  152. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/recurring/add.py +0 -0
  153. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/recurring/detector.py +0 -0
  154. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/recurring/detectors_llm.py +0 -0
  155. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/recurring/ease.py +0 -0
  156. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/recurring/insights.py +0 -0
  157. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/recurring/models.py +0 -0
  158. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/recurring/normalizer.py +0 -0
  159. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/recurring/normalizers.py +0 -0
  160. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/recurring/summary.py +0 -0
  161. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/scaffold/__init__.py +0 -0
  162. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/scaffold/budgets.py +0 -0
  163. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/scaffold/goals.py +0 -0
  164. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/security/__init__.py +0 -0
  165. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/security/add.py +0 -0
  166. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/security/audit.py +0 -0
  167. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/security/encryption.py +0 -0
  168. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/security/models.py +0 -0
  169. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/security/pii_filter.py +0 -0
  170. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/security/pii_patterns.py +0 -0
  171. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/security/token_store.py +0 -0
  172. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/settings.py +0 -0
  173. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/tax/__init__.py +0 -0
  174. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/tax/add.py +0 -0
  175. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/tax/tlh.py +0 -0
  176. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/utils/__init__.py +0 -0
  177. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/utils/deprecation.py +0 -0
  178. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/utils/http.py +0 -0
  179. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/utils/retry.py +0 -0
  180. {fin_infra-0.5.0 → fin_infra-0.6.0}/src/fin_infra/version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: fin-infra
3
- Version: 0.5.0
3
+ Version: 0.6.0
4
4
  Summary: Financial infrastructure toolkit: banking connections, market data, credit, cashflows, and brokerage integrations
5
5
  License: MIT
6
6
  Keywords: finance,banking,plaid,brokerage,markets,credit,tax,cashflow,fintech,infra
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "fin-infra"
3
- version = "0.5.0"
3
+ version = "0.6.0"
4
4
  description = "Financial infrastructure toolkit: banking connections, market data, credit, cashflows, and brokerage integrations"
5
5
  authors = ["Ali Khatami <aliikhatami94@gmail.com>"]
6
6
  license = "MIT"
@@ -89,6 +89,15 @@ class CreateLinkTokenResponse(BaseModel):
89
89
  link_token: str
90
90
 
91
91
 
92
+ class CreateUpdateLinkTokenRequest(BaseModel):
93
+ """Request model for creating a link token in update mode (re-authentication)."""
94
+
95
+ user_id: str
96
+ access_token: str = Field(
97
+ ..., description="Existing access token for the item requiring re-auth"
98
+ )
99
+
100
+
92
101
  class ExchangeTokenRequest(BaseModel):
93
102
  """Request model for exchanging public token."""
94
103
 
@@ -334,6 +343,23 @@ def add_banking(
334
343
  link_token = banking.create_link_token(user_id=request.user_id)
335
344
  return CreateLinkTokenResponse(link_token=link_token)
336
345
 
346
+ @router.post("/link/update", response_model=CreateLinkTokenResponse)
347
+ async def create_update_link_token(request: CreateUpdateLinkTokenRequest):
348
+ """Create link token in update mode for re-authentication.
349
+
350
+ Use this endpoint when a user's bank connection has expired
351
+ (ITEM_LOGIN_REQUIRED error). The returned link token will open
352
+ Plaid Link in update mode, allowing the user to re-authenticate
353
+ without creating a new connection.
354
+
355
+ After successful re-authentication, the existing access_token
356
+ remains valid and no token exchange is needed.
357
+ """
358
+ link_token = banking.create_link_token(
359
+ user_id=request.user_id, access_token=request.access_token
360
+ )
361
+ return CreateLinkTokenResponse(link_token=link_token)
362
+
337
363
  @router.post("/exchange", response_model=ExchangeTokenResponse)
338
364
  async def exchange_token(request: ExchangeTokenRequest):
339
365
  """Exchange public token for access token (Plaid flow)."""
@@ -343,8 +369,29 @@ def add_banking(
343
369
  @router.get("/accounts")
344
370
  async def get_accounts(access_token: str = Depends(get_access_token)):
345
371
  """List accounts for access token."""
346
- accounts = banking.accounts(access_token=access_token)
347
- return {"accounts": accounts}
372
+ try:
373
+ accounts = banking.accounts(access_token=access_token)
374
+ return {"accounts": accounts}
375
+ except Exception as e:
376
+ error_str = str(e)
377
+ # Check for Plaid-specific errors that require user action
378
+ if "ITEM_LOGIN_REQUIRED" in error_str:
379
+ raise HTTPException(
380
+ status_code=401,
381
+ detail="ITEM_LOGIN_REQUIRED: Your bank connection has expired. Please re-authenticate your bank account.",
382
+ )
383
+ elif "INVALID_ACCESS_TOKEN" in error_str:
384
+ raise HTTPException(
385
+ status_code=401,
386
+ detail="INVALID_ACCESS_TOKEN: The access token is invalid or expired. Please reconnect your bank account.",
387
+ )
388
+ elif "ITEM_NOT_FOUND" in error_str:
389
+ raise HTTPException(
390
+ status_code=404,
391
+ detail="ITEM_NOT_FOUND: This bank connection no longer exists. Please reconnect your bank account.",
392
+ )
393
+ # Re-raise other errors
394
+ raise
348
395
 
349
396
  @router.get("/transactions")
350
397
  async def get_transactions(
@@ -81,21 +81,41 @@ class PlaidClient(BankingProvider):
81
81
  api_client = plaid.ApiClient(configuration)
82
82
  self.client = plaid_api.PlaidApi(api_client)
83
83
 
84
- def create_link_token(self, user_id: str) -> str:
85
- request = LinkTokenCreateRequest(
86
- user=LinkTokenCreateRequestUser(client_user_id=user_id),
87
- client_name="fin-infra",
88
- products=[
84
+ def create_link_token(self, user_id: str, access_token: str | None = None) -> str:
85
+ """Create a Plaid Link token for new connections or re-authentication.
86
+
87
+ Args:
88
+ user_id: Client-defined user ID for the Link session
89
+ access_token: If provided, creates Link in update mode for re-authentication
90
+ (used when ITEM_LOGIN_REQUIRED error occurs)
91
+
92
+ Returns:
93
+ Link token string for Plaid Link initialization
94
+ """
95
+ # Build base request parameters
96
+ request_params = {
97
+ "user": LinkTokenCreateRequestUser(client_user_id=user_id),
98
+ "client_name": "fin-infra",
99
+ "country_codes": [CountryCode("US")],
100
+ "language": "en",
101
+ }
102
+
103
+ if access_token:
104
+ # Update mode: re-authenticate existing connection
105
+ # Don't include products - Plaid uses existing item's products
106
+ request_params["access_token"] = access_token
107
+ else:
108
+ # New connection: specify products to enable
109
+ request_params["products"] = [
89
110
  Products("auth"), # Account/routing numbers for ACH
90
111
  Products("transactions"), # Transaction history
91
112
  Products("liabilities"), # Credit cards, loans, student loans
92
113
  Products("investments"), # Brokerage, retirement accounts
93
114
  Products("assets"), # Asset reports for lending/verification
94
115
  Products("identity"), # Account holder info (name, email, phone)
95
- ],
96
- country_codes=[CountryCode("US")],
97
- language="en",
98
- )
116
+ ]
117
+
118
+ request = LinkTokenCreateRequest(**request_params)
99
119
  response = self.client.link_token_create(request)
100
120
  return cast("str", response["link_token"])
101
121
 
@@ -121,7 +121,7 @@ class TellerClient(BankingProvider):
121
121
  response.raise_for_status()
122
122
  return response.json()
123
123
 
124
- def create_link_token(self, user_id: str) -> str:
124
+ def create_link_token(self, user_id: str, access_token: str | None = None) -> str:
125
125
  """Create link token for user authentication.
126
126
 
127
127
  Note: Teller uses a simpler auth flow than Plaid. In production,
@@ -130,6 +130,9 @@ class TellerClient(BankingProvider):
130
130
 
131
131
  Args:
132
132
  user_id: Your application's user identifier
133
+ access_token: If provided, creates Link in update mode for re-authentication
134
+ (used when connection needs to be repaired). Teller handles
135
+ this differently than Plaid - see Teller Connect docs.
133
136
 
134
137
  Returns:
135
138
  Link token or enrollment ID for user to authenticate
@@ -138,13 +141,18 @@ class TellerClient(BankingProvider):
138
141
  httpx.HTTPStatusError: On HTTP errors
139
142
  """
140
143
  # Teller's enrollment endpoint for creating application links
144
+ payload: dict[str, Any] = {
145
+ "user_id": user_id,
146
+ "products": ["accounts", "transactions", "balances", "identity"],
147
+ }
148
+ # If access_token provided, add it for update mode (re-authentication)
149
+ if access_token:
150
+ payload["access_token"] = access_token
151
+
141
152
  response = self._request(
142
153
  "POST",
143
154
  "/enrollments",
144
- json={
145
- "user_id": user_id,
146
- "products": ["accounts", "transactions", "balances", "identity"],
147
- },
155
+ json=payload,
148
156
  )
149
157
  return cast("str", response.get("enrollment_id", ""))
150
158
 
@@ -32,6 +32,13 @@ from ..models import Candle, Quote
32
32
 
33
33
 
34
34
  class MarketDataProvider(ABC):
35
+ """Abstract base class for stock and equity market data providers.
36
+
37
+ Implement this class to integrate with market data sources like
38
+ Yahoo Finance, Alpha Vantage, or Polygon.io for real-time quotes
39
+ and historical price data.
40
+ """
41
+
35
42
  @abstractmethod
36
43
  def quote(self, symbol: str) -> Quote:
37
44
  pass
@@ -44,6 +51,12 @@ class MarketDataProvider(ABC):
44
51
 
45
52
 
46
53
  class CryptoDataProvider(ABC):
54
+ """Abstract base class for cryptocurrency market data providers.
55
+
56
+ Implement this class to integrate with crypto exchanges like
57
+ Binance, Coinbase, or Kraken for ticker data and OHLCV candles.
58
+ """
59
+
47
60
  @abstractmethod
48
61
  def ticker(self, symbol_pair: str) -> Any:
49
62
  pass
@@ -57,8 +70,17 @@ class BankingProvider(ABC):
57
70
  """Abstract provider for bank account aggregation (Teller, Plaid, MX)."""
58
71
 
59
72
  @abstractmethod
60
- def create_link_token(self, user_id: str) -> str:
61
- """Create a link/connect token for user to authenticate with their bank."""
73
+ def create_link_token(self, user_id: str, access_token: str | None = None) -> str:
74
+ """Create a link/connect token for user to authenticate with their bank.
75
+
76
+ Args:
77
+ user_id: Client-defined user ID for the Link session
78
+ access_token: If provided, creates Link in update mode for re-authentication
79
+ (used when ITEM_LOGIN_REQUIRED error occurs)
80
+
81
+ Returns:
82
+ Link token string for initializing the bank connection UI
83
+ """
62
84
  pass
63
85
 
64
86
  @abstractmethod
@@ -90,6 +112,13 @@ class BankingProvider(ABC):
90
112
 
91
113
 
92
114
  class BrokerageProvider(ABC):
115
+ """Abstract base class for brokerage trading integrations.
116
+
117
+ Implement this class to integrate with trading platforms like
118
+ Alpaca, Interactive Brokers, or TD Ameritrade for order execution,
119
+ position management, and portfolio tracking.
120
+ """
121
+
93
122
  @abstractmethod
94
123
  def submit_order(
95
124
  self,
@@ -185,6 +214,13 @@ class IdentityProvider(ABC):
185
214
 
186
215
 
187
216
  class CreditProvider(ABC):
217
+ """Abstract base class for credit data providers.
218
+
219
+ Implement this class to integrate with credit bureaus like
220
+ Experian, Equifax, or TransUnion for credit score retrieval
221
+ and full credit report access.
222
+ """
223
+
188
224
  @abstractmethod
189
225
  def get_credit_score(self, user_id: str, **kwargs: Any) -> Any:
190
226
  pass
File without changes
File without changes