mirror of
https://github.com/we-promise/sure.git
synced 2026-09-05 06:41:08 +00:00
* feat(bills): schema and domain core for the bills subsystem First of three chunks carved out of #3083. This one carries the schema and the domain layer: no bills pages, no calendar feed, no assistant tools. Nothing here is reachable from the UI yet, so it changes no user-visible behavior on its own. Schema, in a single migration with a full down: - recurrence_rules, recurring_occurrences, recurring_allocations, recurring_price_changes and recurring_match_rejections - bill columns on recurring_transactions (bill_type, payment_url, autopay, notes, anchor and end conditions, weekend adjustment, dedup scope) - the four data backfills, in their original order Domain layer: - Schedule, the pure date PORO every cadence resolves through, and FrequencyPreset for the labels - OccurrenceGenerator, Matcher, Allocator, PriceChangeDetector, Classifier, DeclaredBill, HistoryBackfiller and PaycheckPlanner - Pipeline, tying detection to generation, plus the nightly job and rake task Existing detection code changed in three places, each a bug this schema exposes: - Cleaner used a flat two-month staleness threshold, which silently retired every quarterly and annual series - SubscriptionAuditGenerator used a flat 45-day overdue threshold, meaningless at both ends of the frequency range - CashFlowWarningGenerator read one projected entry per series, which only equalled the monthly amount because every series was monthly; weekly bills were under-counted fourfold in its 30-day projection The JSON API travels with the model rather than the UI, because the status enum widens here. The API accepts only active and inactive on write; suggested, paused and ended are lifecycle states owned by detection, so the documented enum stays truthful. Uniqueness keys gain dedup_scope alongside amount, never instead of it: a series that is not price-forked carries a blank scope, so amount is what keeps two different prices apart. Suite 7,550 runs, 0 failures. Rubocop and brakeman clean. Eager loading verified, and the migration reverses and re-applies. Includes the first review round: orphan repair matches income and refuses coincidental twins, session imports persist occurrence mappings across chunks, semimonthly anchors canonicalize, classifier keywords match whole words, and the down refuses rather than failing when price-forked rows exist. * Address second review round Bound the cross-currency default allocation by the entry leftover and the occurrence remainder, matching the same-currency path. Let keyword stems carry a suffix again after the word-boundary fix silenced them. Skip an incoherent recurrence rule row instead of rolling back the whole import. Check rollback collisions per restored index so a refusal cannot land after the bills tables are dropped. Replay the closed_at test through a real second import. Preload the orphan repair associations and move the allocator errors to locale keys. * Match index NULL semantics in the rollback collision checks GROUP BY treats NULLs as equal but the restored unique indexes do not: account_id is nullable and indexed, so two accountless rows can never collide under any of them. Excluding NULL accounts keeps the guard from refusing a rollback PostgreSQL can perform. Verified live both ways: accountless duplicates roll back, a real collision still refuses. * Address maintainer review Scope the payable debt-destination subquery to the row and its family instead of scanning every account in the installation. Batch the cash flow generator remaining-amount sums into one grouped query, matching the two sibling sites. Enforce both window bounds in the after_count branch so a future-anchored plan cannot leak past the requested end date. Skip the explicit regeneration when the day column change will fire the model callback anyway. Add the missing locale entry for the allocation currency validation.
8278 lines
223 KiB
YAML
8278 lines
223 KiB
YAML
---
|
|
openapi: 3.0.3
|
|
info:
|
|
title: Sure API
|
|
version: v1
|
|
description: OpenAPI documentation generated from executable request specs.
|
|
servers:
|
|
- url: https://app.sure.am
|
|
description: Production
|
|
- url: http://localhost:3000
|
|
description: Local development
|
|
components:
|
|
securitySchemes:
|
|
apiKeyAuth:
|
|
type: apiKey
|
|
name: X-Api-Key
|
|
in: header
|
|
description: API key for authentication. Generate one from your account settings.
|
|
schemas:
|
|
Pagination:
|
|
type: object
|
|
required:
|
|
- page
|
|
- per_page
|
|
- total_count
|
|
- total_pages
|
|
properties:
|
|
page:
|
|
type: integer
|
|
minimum: 1
|
|
per_page:
|
|
type: integer
|
|
minimum: 1
|
|
total_count:
|
|
type: integer
|
|
minimum: 0
|
|
total_pages:
|
|
type: integer
|
|
minimum: 0
|
|
FamilyExportFile:
|
|
type: object
|
|
required:
|
|
- attached
|
|
properties:
|
|
attached:
|
|
type: boolean
|
|
byte_size:
|
|
type: integer
|
|
nullable: true
|
|
minimum: 0
|
|
content_type:
|
|
type: string
|
|
nullable: true
|
|
FamilyExport:
|
|
type: object
|
|
required:
|
|
- id
|
|
- status
|
|
- filename
|
|
- downloadable
|
|
- file
|
|
- created_at
|
|
- updated_at
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
status:
|
|
type: string
|
|
enum:
|
|
- pending
|
|
- processing
|
|
- completed
|
|
- failed
|
|
filename:
|
|
type: string
|
|
downloadable:
|
|
type: boolean
|
|
download_path:
|
|
type: string
|
|
nullable: true
|
|
file:
|
|
"$ref": "#/components/schemas/FamilyExportFile"
|
|
created_at:
|
|
type: string
|
|
format: date-time
|
|
updated_at:
|
|
type: string
|
|
format: date-time
|
|
FamilyExportResponse:
|
|
type: object
|
|
required:
|
|
- data
|
|
properties:
|
|
data:
|
|
"$ref": "#/components/schemas/FamilyExport"
|
|
FamilyExportCollection:
|
|
type: object
|
|
required:
|
|
- data
|
|
- meta
|
|
properties:
|
|
data:
|
|
type: array
|
|
maxItems: 100
|
|
items:
|
|
"$ref": "#/components/schemas/FamilyExport"
|
|
meta:
|
|
"$ref": "#/components/schemas/Pagination"
|
|
ErrorResponse:
|
|
type: object
|
|
required:
|
|
- error
|
|
properties:
|
|
error:
|
|
type: string
|
|
message:
|
|
type: string
|
|
nullable: true
|
|
details:
|
|
oneOf:
|
|
- type: array
|
|
items:
|
|
type: string
|
|
- type: object
|
|
nullable: true
|
|
errors:
|
|
type: array
|
|
items:
|
|
type: string
|
|
nullable: true
|
|
description: Validation error messages (alternative to details used by trades,
|
|
valuations, etc.)
|
|
ErrorResponseWithImportId:
|
|
type: object
|
|
required:
|
|
- error
|
|
- import_id
|
|
properties:
|
|
error:
|
|
type: string
|
|
message:
|
|
type: string
|
|
nullable: true
|
|
import_id:
|
|
type: string
|
|
format: uuid
|
|
description: Import ID preserved for retry or inspection after upload succeeds
|
|
but publish fails
|
|
MfaRequiredResponse:
|
|
type: object
|
|
required:
|
|
- error
|
|
- mfa_required
|
|
properties:
|
|
error:
|
|
type: string
|
|
mfa_required:
|
|
type: boolean
|
|
ToolCall:
|
|
type: object
|
|
required:
|
|
- id
|
|
- function_name
|
|
- function_arguments
|
|
- created_at
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
function_name:
|
|
type: string
|
|
function_arguments:
|
|
type: object
|
|
additionalProperties: true
|
|
function_result:
|
|
type: object
|
|
additionalProperties: true
|
|
nullable: true
|
|
created_at:
|
|
type: string
|
|
format: date-time
|
|
Message:
|
|
type: object
|
|
required:
|
|
- id
|
|
- type
|
|
- role
|
|
- content
|
|
- created_at
|
|
- updated_at
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
type:
|
|
type: string
|
|
enum:
|
|
- user_message
|
|
- assistant_message
|
|
role:
|
|
type: string
|
|
enum:
|
|
- user
|
|
- assistant
|
|
content:
|
|
type: string
|
|
model:
|
|
type: string
|
|
nullable: true
|
|
created_at:
|
|
type: string
|
|
format: date-time
|
|
updated_at:
|
|
type: string
|
|
format: date-time
|
|
tool_calls:
|
|
type: array
|
|
items:
|
|
"$ref": "#/components/schemas/ToolCall"
|
|
nullable: true
|
|
MessageResponse:
|
|
allOf:
|
|
- "$ref": "#/components/schemas/Message"
|
|
- type: object
|
|
required:
|
|
- chat_id
|
|
properties:
|
|
chat_id:
|
|
type: string
|
|
format: uuid
|
|
ai_response_status:
|
|
type: string
|
|
enum:
|
|
- pending
|
|
- complete
|
|
- failed
|
|
nullable: true
|
|
ai_response_message:
|
|
type: string
|
|
nullable: true
|
|
ChatResource:
|
|
type: object
|
|
required:
|
|
- id
|
|
- title
|
|
- created_at
|
|
- updated_at
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
title:
|
|
type: string
|
|
error:
|
|
type: string
|
|
nullable: true
|
|
created_at:
|
|
type: string
|
|
format: date-time
|
|
updated_at:
|
|
type: string
|
|
format: date-time
|
|
ChatSummary:
|
|
allOf:
|
|
- "$ref": "#/components/schemas/ChatResource"
|
|
- type: object
|
|
required:
|
|
- message_count
|
|
properties:
|
|
message_count:
|
|
type: integer
|
|
minimum: 0
|
|
last_message_at:
|
|
type: string
|
|
format: date-time
|
|
nullable: true
|
|
ChatDetail:
|
|
allOf:
|
|
- "$ref": "#/components/schemas/ChatResource"
|
|
- type: object
|
|
required:
|
|
- messages
|
|
properties:
|
|
messages:
|
|
type: array
|
|
items:
|
|
"$ref": "#/components/schemas/Message"
|
|
pagination:
|
|
"$ref": "#/components/schemas/Pagination"
|
|
nullable: true
|
|
ChatCollection:
|
|
type: object
|
|
required:
|
|
- chats
|
|
- pagination
|
|
properties:
|
|
chats:
|
|
type: array
|
|
items:
|
|
"$ref": "#/components/schemas/ChatSummary"
|
|
pagination:
|
|
"$ref": "#/components/schemas/Pagination"
|
|
Insight:
|
|
type: object
|
|
required:
|
|
- id
|
|
- type
|
|
- title
|
|
- body
|
|
- priority
|
|
- status
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
type:
|
|
type: string
|
|
title:
|
|
type: string
|
|
body:
|
|
type: string
|
|
priority:
|
|
type: string
|
|
enum:
|
|
- high
|
|
- medium
|
|
- low
|
|
status:
|
|
type: string
|
|
enum:
|
|
- active
|
|
- read
|
|
generated_at:
|
|
type: string
|
|
format: date-time
|
|
nullable: true
|
|
InsightCollection:
|
|
type: object
|
|
required:
|
|
- insights
|
|
properties:
|
|
insights:
|
|
type: array
|
|
items:
|
|
"$ref": "#/components/schemas/Insight"
|
|
PushSubscription:
|
|
type: object
|
|
required:
|
|
- id
|
|
- environment
|
|
- platform
|
|
- last_registered_at
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
environment:
|
|
type: string
|
|
enum:
|
|
- sandbox
|
|
- production
|
|
platform:
|
|
type: string
|
|
enum:
|
|
- ios
|
|
last_registered_at:
|
|
type: string
|
|
format: date-time
|
|
RetryResponse:
|
|
type: object
|
|
required:
|
|
- message
|
|
- message_id
|
|
properties:
|
|
message:
|
|
type: string
|
|
message_id:
|
|
type: string
|
|
format: uuid
|
|
Account:
|
|
type: object
|
|
required:
|
|
- id
|
|
- name
|
|
- account_type
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
name:
|
|
type: string
|
|
account_type:
|
|
type: string
|
|
nullable: true
|
|
status:
|
|
type: string
|
|
AccountDetail:
|
|
type: object
|
|
required:
|
|
- id
|
|
- name
|
|
- balance
|
|
- balance_cents
|
|
- cash_balance
|
|
- cash_balance_cents
|
|
- currency
|
|
- classification
|
|
- account_type
|
|
- status
|
|
- created_at
|
|
- updated_at
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
name:
|
|
type: string
|
|
balance:
|
|
type: string
|
|
balance_cents:
|
|
type: integer
|
|
description: Signed balance in minor currency units
|
|
cash_balance:
|
|
type: string
|
|
cash_balance_cents:
|
|
type: integer
|
|
description: Signed cash balance in minor currency units
|
|
currency:
|
|
type: string
|
|
classification:
|
|
type: string
|
|
account_type:
|
|
type: string
|
|
nullable: true
|
|
subtype:
|
|
type: string
|
|
nullable: true
|
|
status:
|
|
type: string
|
|
enum:
|
|
- active
|
|
- draft
|
|
- disabled
|
|
- pending_deletion
|
|
institution_name:
|
|
type: string
|
|
nullable: true
|
|
institution_domain:
|
|
type: string
|
|
nullable: true
|
|
created_at:
|
|
type: string
|
|
format: date-time
|
|
updated_at:
|
|
type: string
|
|
format: date-time
|
|
AccountCollection:
|
|
type: object
|
|
required:
|
|
- accounts
|
|
- pagination
|
|
properties:
|
|
accounts:
|
|
type: array
|
|
items:
|
|
"$ref": "#/components/schemas/AccountDetail"
|
|
pagination:
|
|
"$ref": "#/components/schemas/Pagination"
|
|
FamilySettings:
|
|
type: object
|
|
required:
|
|
- id
|
|
- currency
|
|
- locale
|
|
- date_format
|
|
- month_start_day
|
|
- moniker
|
|
- default_account_sharing
|
|
- custom_enabled_currencies
|
|
- enabled_currencies
|
|
- created_at
|
|
- updated_at
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
name:
|
|
type: string
|
|
nullable: true
|
|
currency:
|
|
type: string
|
|
locale:
|
|
type: string
|
|
date_format:
|
|
type: string
|
|
country:
|
|
type: string
|
|
nullable: true
|
|
timezone:
|
|
type: string
|
|
nullable: true
|
|
month_start_day:
|
|
type: integer
|
|
minimum: 1
|
|
maximum: 28
|
|
moniker:
|
|
type: string
|
|
enum:
|
|
- Family
|
|
- Group
|
|
default_account_sharing:
|
|
type: string
|
|
enum:
|
|
- shared
|
|
- private
|
|
custom_enabled_currencies:
|
|
type: boolean
|
|
enabled_currencies:
|
|
type: array
|
|
items:
|
|
type: string
|
|
created_at:
|
|
type: string
|
|
format: date-time
|
|
updated_at:
|
|
type: string
|
|
format: date-time
|
|
BudgetSummary:
|
|
type: object
|
|
required:
|
|
- id
|
|
- start_date
|
|
- end_date
|
|
- name
|
|
- currency
|
|
- initialized
|
|
- current
|
|
- created_at
|
|
- updated_at
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
start_date:
|
|
type: string
|
|
format: date
|
|
end_date:
|
|
type: string
|
|
format: date
|
|
name:
|
|
type: string
|
|
currency:
|
|
type: string
|
|
initialized:
|
|
type: boolean
|
|
current:
|
|
type: boolean
|
|
budgeted_spending:
|
|
type: string
|
|
nullable: true
|
|
budgeted_spending_cents:
|
|
type: integer
|
|
nullable: true
|
|
expected_income:
|
|
type: string
|
|
nullable: true
|
|
expected_income_cents:
|
|
type: integer
|
|
nullable: true
|
|
allocated_spending:
|
|
type: string
|
|
allocated_spending_cents:
|
|
type: integer
|
|
created_at:
|
|
type: string
|
|
format: date-time
|
|
updated_at:
|
|
type: string
|
|
format: date-time
|
|
Budget:
|
|
type: object
|
|
required:
|
|
- id
|
|
- start_date
|
|
- end_date
|
|
- name
|
|
- currency
|
|
- initialized
|
|
- current
|
|
- created_at
|
|
- updated_at
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
start_date:
|
|
type: string
|
|
format: date
|
|
end_date:
|
|
type: string
|
|
format: date
|
|
name:
|
|
type: string
|
|
currency:
|
|
type: string
|
|
initialized:
|
|
type: boolean
|
|
current:
|
|
type: boolean
|
|
budgeted_spending:
|
|
type: string
|
|
nullable: true
|
|
budgeted_spending_cents:
|
|
type: integer
|
|
nullable: true
|
|
expected_income:
|
|
type: string
|
|
nullable: true
|
|
expected_income_cents:
|
|
type: integer
|
|
nullable: true
|
|
allocated_spending:
|
|
type: string
|
|
allocated_spending_cents:
|
|
type: integer
|
|
actual_spending:
|
|
type: string
|
|
actual_spending_cents:
|
|
type: integer
|
|
actual_income:
|
|
type: string
|
|
actual_income_cents:
|
|
type: integer
|
|
available_to_spend:
|
|
type: string
|
|
available_to_spend_cents:
|
|
type: integer
|
|
available_to_allocate:
|
|
type: string
|
|
available_to_allocate_cents:
|
|
type: integer
|
|
created_at:
|
|
type: string
|
|
format: date-time
|
|
updated_at:
|
|
type: string
|
|
format: date-time
|
|
BudgetCollection:
|
|
type: object
|
|
required:
|
|
- budgets
|
|
- pagination
|
|
properties:
|
|
budgets:
|
|
type: array
|
|
items:
|
|
"$ref": "#/components/schemas/BudgetSummary"
|
|
pagination:
|
|
"$ref": "#/components/schemas/Pagination"
|
|
BudgetCategorySummary:
|
|
type: object
|
|
required:
|
|
- id
|
|
- budget_id
|
|
- currency
|
|
- subcategory
|
|
- inherits_parent_budget
|
|
- rollover_enabled
|
|
- category
|
|
- created_at
|
|
- updated_at
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
budget_id:
|
|
type: string
|
|
format: uuid
|
|
currency:
|
|
type: string
|
|
subcategory:
|
|
type: boolean
|
|
inherits_parent_budget:
|
|
type: boolean
|
|
rollover_enabled:
|
|
type: boolean
|
|
budgeted_spending:
|
|
type: string
|
|
budgeted_spending_cents:
|
|
type: integer
|
|
display_budgeted_spending:
|
|
type: string
|
|
display_budgeted_spending_cents:
|
|
type: integer
|
|
category:
|
|
type: object
|
|
required:
|
|
- id
|
|
- name
|
|
- color
|
|
- lucide_icon
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
name:
|
|
type: string
|
|
color:
|
|
type: string
|
|
lucide_icon:
|
|
type: string
|
|
parent_id:
|
|
type: string
|
|
format: uuid
|
|
nullable: true
|
|
created_at:
|
|
type: string
|
|
format: date-time
|
|
updated_at:
|
|
type: string
|
|
format: date-time
|
|
BudgetCategory:
|
|
type: object
|
|
required:
|
|
- id
|
|
- budget_id
|
|
- currency
|
|
- subcategory
|
|
- inherits_parent_budget
|
|
- rollover_enabled
|
|
- category
|
|
- created_at
|
|
- updated_at
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
budget_id:
|
|
type: string
|
|
format: uuid
|
|
currency:
|
|
type: string
|
|
subcategory:
|
|
type: boolean
|
|
inherits_parent_budget:
|
|
type: boolean
|
|
rollover_enabled:
|
|
type: boolean
|
|
budgeted_spending:
|
|
type: string
|
|
budgeted_spending_cents:
|
|
type: integer
|
|
display_budgeted_spending:
|
|
type: string
|
|
display_budgeted_spending_cents:
|
|
type: integer
|
|
actual_spending:
|
|
type: string
|
|
actual_spending_cents:
|
|
type: integer
|
|
rolled_over_amount:
|
|
type: string
|
|
rolled_over_amount_cents:
|
|
type: integer
|
|
available_to_spend:
|
|
type: string
|
|
available_to_spend_cents:
|
|
type: integer
|
|
category:
|
|
type: object
|
|
required:
|
|
- id
|
|
- name
|
|
- color
|
|
- lucide_icon
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
name:
|
|
type: string
|
|
color:
|
|
type: string
|
|
lucide_icon:
|
|
type: string
|
|
parent_id:
|
|
type: string
|
|
format: uuid
|
|
nullable: true
|
|
created_at:
|
|
type: string
|
|
format: date-time
|
|
updated_at:
|
|
type: string
|
|
format: date-time
|
|
BudgetCategoryCollection:
|
|
type: object
|
|
required:
|
|
- budget_categories
|
|
- pagination
|
|
properties:
|
|
budget_categories:
|
|
type: array
|
|
items:
|
|
"$ref": "#/components/schemas/BudgetCategorySummary"
|
|
pagination:
|
|
"$ref": "#/components/schemas/Pagination"
|
|
Balance:
|
|
type: object
|
|
required:
|
|
- id
|
|
- date
|
|
- currency
|
|
- flows_factor
|
|
- balance
|
|
- balance_cents
|
|
- start_balance
|
|
- start_balance_cents
|
|
- end_balance
|
|
- end_balance_cents
|
|
- account
|
|
- created_at
|
|
- updated_at
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
date:
|
|
type: string
|
|
format: date
|
|
currency:
|
|
type: string
|
|
flows_factor:
|
|
type: number
|
|
format: float
|
|
balance:
|
|
type: string
|
|
balance_cents:
|
|
type: integer
|
|
description: Balance in currency minor units
|
|
cash_balance:
|
|
type: string
|
|
nullable: true
|
|
cash_balance_cents:
|
|
type: integer
|
|
nullable: true
|
|
description: Cash balance in currency minor units
|
|
start_cash_balance:
|
|
type: string
|
|
start_cash_balance_cents:
|
|
type: integer
|
|
description: Starting cash balance in currency minor units
|
|
start_non_cash_balance:
|
|
type: string
|
|
start_non_cash_balance_cents:
|
|
type: integer
|
|
description: Starting non-cash balance in currency minor units
|
|
start_balance:
|
|
type: string
|
|
start_balance_cents:
|
|
type: integer
|
|
description: Starting total balance in currency minor units
|
|
cash_inflows:
|
|
type: string
|
|
cash_inflows_cents:
|
|
type: integer
|
|
description: Cash inflows in currency minor units
|
|
cash_outflows:
|
|
type: string
|
|
cash_outflows_cents:
|
|
type: integer
|
|
description: Cash outflows in currency minor units
|
|
non_cash_inflows:
|
|
type: string
|
|
non_cash_inflows_cents:
|
|
type: integer
|
|
description: Non-cash inflows in currency minor units
|
|
non_cash_outflows:
|
|
type: string
|
|
non_cash_outflows_cents:
|
|
type: integer
|
|
description: Non-cash outflows in currency minor units
|
|
net_market_flows:
|
|
type: string
|
|
net_market_flows_cents:
|
|
type: integer
|
|
description: Net market flows in currency minor units
|
|
cash_adjustments:
|
|
type: string
|
|
cash_adjustments_cents:
|
|
type: integer
|
|
description: Cash adjustments in currency minor units
|
|
non_cash_adjustments:
|
|
type: string
|
|
non_cash_adjustments_cents:
|
|
type: integer
|
|
description: Non-cash adjustments in currency minor units
|
|
end_cash_balance:
|
|
type: string
|
|
end_cash_balance_cents:
|
|
type: integer
|
|
description: Ending cash balance in currency minor units
|
|
end_non_cash_balance:
|
|
type: string
|
|
end_non_cash_balance_cents:
|
|
type: integer
|
|
description: Ending non-cash balance in currency minor units
|
|
end_balance:
|
|
type: string
|
|
end_balance_cents:
|
|
type: integer
|
|
description: Ending total balance in currency minor units
|
|
account:
|
|
"$ref": "#/components/schemas/BalanceAccount"
|
|
created_at:
|
|
type: string
|
|
format: date-time
|
|
updated_at:
|
|
type: string
|
|
format: date-time
|
|
BalanceAccount:
|
|
type: object
|
|
required:
|
|
- id
|
|
- name
|
|
- account_type
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
name:
|
|
type: string
|
|
account_type:
|
|
type: string
|
|
nullable: true
|
|
BalanceCollection:
|
|
type: object
|
|
required:
|
|
- balances
|
|
- pagination
|
|
properties:
|
|
balances:
|
|
type: array
|
|
items:
|
|
"$ref": "#/components/schemas/Balance"
|
|
pagination:
|
|
"$ref": "#/components/schemas/Pagination"
|
|
Category:
|
|
type: object
|
|
required:
|
|
- id
|
|
- name
|
|
- color
|
|
- icon
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
name:
|
|
type: string
|
|
color:
|
|
type: string
|
|
icon:
|
|
type: string
|
|
CategoryParent:
|
|
type: object
|
|
required:
|
|
- id
|
|
- name
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
name:
|
|
type: string
|
|
CategoryDetail:
|
|
type: object
|
|
required:
|
|
- id
|
|
- name
|
|
- color
|
|
- icon
|
|
- subcategories_count
|
|
- created_at
|
|
- updated_at
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
name:
|
|
type: string
|
|
color:
|
|
type: string
|
|
icon:
|
|
type: string
|
|
parent:
|
|
"$ref": "#/components/schemas/CategoryParent"
|
|
nullable: true
|
|
subcategories_count:
|
|
type: integer
|
|
minimum: 0
|
|
created_at:
|
|
type: string
|
|
format: date-time
|
|
updated_at:
|
|
type: string
|
|
format: date-time
|
|
CategoryCollection:
|
|
type: object
|
|
required:
|
|
- categories
|
|
- pagination
|
|
properties:
|
|
categories:
|
|
type: array
|
|
items:
|
|
"$ref": "#/components/schemas/CategoryDetail"
|
|
pagination:
|
|
"$ref": "#/components/schemas/Pagination"
|
|
CategoryCreateRequest:
|
|
type: object
|
|
required:
|
|
- category
|
|
properties:
|
|
category:
|
|
type: object
|
|
required:
|
|
- name
|
|
properties:
|
|
name:
|
|
type: string
|
|
description: Category name (required, unique within family)
|
|
color:
|
|
type: string
|
|
description: 'Hex color code (e.g. #22c55e). Defaults to #6172F3 if
|
|
omitted; subcategories inherit parent color.'
|
|
icon:
|
|
type: string
|
|
description: Lucide icon name (e.g. "coffee"). Auto-suggested from the
|
|
name when omitted.
|
|
parent_id:
|
|
type: string
|
|
format: uuid
|
|
nullable: true
|
|
description: Parent category ID. Must belong to the same family. Categories
|
|
support up to 2 levels of nesting.
|
|
Merchant:
|
|
type: object
|
|
required:
|
|
- id
|
|
- name
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
name:
|
|
type: string
|
|
MerchantDetail:
|
|
type: object
|
|
required:
|
|
- id
|
|
- name
|
|
- type
|
|
- created_at
|
|
- updated_at
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
name:
|
|
type: string
|
|
type:
|
|
type: string
|
|
enum:
|
|
- FamilyMerchant
|
|
- ProviderMerchant
|
|
created_at:
|
|
type: string
|
|
format: date-time
|
|
updated_at:
|
|
type: string
|
|
format: date-time
|
|
MerchantImportResult:
|
|
type: object
|
|
required:
|
|
- imported
|
|
- skipped
|
|
- merchants
|
|
properties:
|
|
imported:
|
|
type: integer
|
|
description: Number of merchants successfully created
|
|
skipped:
|
|
type: integer
|
|
description: Number of rows skipped (duplicates or invalid)
|
|
merchants:
|
|
type: array
|
|
items:
|
|
"$ref": "#/components/schemas/MerchantDetail"
|
|
Tag:
|
|
type: object
|
|
required:
|
|
- id
|
|
- name
|
|
- color
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
name:
|
|
type: string
|
|
color:
|
|
type: string
|
|
TagDetail:
|
|
type: object
|
|
required:
|
|
- id
|
|
- name
|
|
- color
|
|
- created_at
|
|
- updated_at
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
name:
|
|
type: string
|
|
color:
|
|
type: string
|
|
created_at:
|
|
type: string
|
|
format: date-time
|
|
updated_at:
|
|
type: string
|
|
format: date-time
|
|
TagCollection:
|
|
type: array
|
|
items:
|
|
"$ref": "#/components/schemas/TagDetail"
|
|
RuleAction:
|
|
type: object
|
|
required:
|
|
- id
|
|
- action_type
|
|
- created_at
|
|
- updated_at
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
action_type:
|
|
type: string
|
|
value:
|
|
type: string
|
|
nullable: true
|
|
created_at:
|
|
type: string
|
|
format: date-time
|
|
updated_at:
|
|
type: string
|
|
format: date-time
|
|
RuleCondition:
|
|
type: object
|
|
required:
|
|
- id
|
|
- condition_type
|
|
- operator
|
|
- sub_conditions
|
|
- created_at
|
|
- updated_at
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
condition_type:
|
|
type: string
|
|
operator:
|
|
type: string
|
|
value:
|
|
type: string
|
|
nullable: true
|
|
sub_conditions:
|
|
type: array
|
|
items:
|
|
"$ref": "#/components/schemas/RuleCondition"
|
|
created_at:
|
|
type: string
|
|
format: date-time
|
|
updated_at:
|
|
type: string
|
|
format: date-time
|
|
Rule:
|
|
type: object
|
|
required:
|
|
- id
|
|
- resource_type
|
|
- active
|
|
- conditions
|
|
- actions
|
|
- created_at
|
|
- updated_at
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
name:
|
|
type: string
|
|
nullable: true
|
|
resource_type:
|
|
type: string
|
|
enum:
|
|
- transaction
|
|
active:
|
|
type: boolean
|
|
effective_date:
|
|
type: string
|
|
format: date
|
|
nullable: true
|
|
conditions:
|
|
type: array
|
|
items:
|
|
"$ref": "#/components/schemas/RuleCondition"
|
|
actions:
|
|
type: array
|
|
items:
|
|
"$ref": "#/components/schemas/RuleAction"
|
|
created_at:
|
|
type: string
|
|
format: date-time
|
|
updated_at:
|
|
type: string
|
|
format: date-time
|
|
RuleResponse:
|
|
type: object
|
|
required:
|
|
- data
|
|
properties:
|
|
data:
|
|
"$ref": "#/components/schemas/Rule"
|
|
RuleCollection:
|
|
type: object
|
|
required:
|
|
- data
|
|
- meta
|
|
properties:
|
|
data:
|
|
type: array
|
|
items:
|
|
"$ref": "#/components/schemas/Rule"
|
|
meta:
|
|
type: object
|
|
required:
|
|
- current_page
|
|
- total_pages
|
|
- total_count
|
|
- per_page
|
|
properties:
|
|
current_page:
|
|
type: integer
|
|
next_page:
|
|
type: integer
|
|
nullable: true
|
|
prev_page:
|
|
type: integer
|
|
nullable: true
|
|
total_pages:
|
|
type: integer
|
|
total_count:
|
|
type: integer
|
|
per_page:
|
|
type: integer
|
|
RuleRun:
|
|
type: object
|
|
required:
|
|
- id
|
|
- rule_id
|
|
- rule_name
|
|
- execution_type
|
|
- status
|
|
- transactions_queued
|
|
- transactions_processed
|
|
- transactions_modified
|
|
- pending_jobs_count
|
|
- executed_at
|
|
- rule
|
|
- created_at
|
|
- updated_at
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
rule_id:
|
|
type: string
|
|
format: uuid
|
|
rule_name:
|
|
type: string
|
|
nullable: true
|
|
execution_type:
|
|
type: string
|
|
enum:
|
|
- manual
|
|
- scheduled
|
|
status:
|
|
type: string
|
|
enum:
|
|
- pending
|
|
- success
|
|
- failed
|
|
transactions_queued:
|
|
type: integer
|
|
minimum: 0
|
|
transactions_processed:
|
|
type: integer
|
|
minimum: 0
|
|
transactions_modified:
|
|
type: integer
|
|
minimum: 0
|
|
pending_jobs_count:
|
|
type: integer
|
|
minimum: 0
|
|
executed_at:
|
|
type: string
|
|
format: date-time
|
|
error_message:
|
|
type: string
|
|
nullable: true
|
|
rule:
|
|
type: object
|
|
nullable: true
|
|
required:
|
|
- id
|
|
- resource_type
|
|
- active
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
name:
|
|
type: string
|
|
nullable: true
|
|
resource_type:
|
|
type: string
|
|
active:
|
|
type: boolean
|
|
created_at:
|
|
type: string
|
|
format: date-time
|
|
updated_at:
|
|
type: string
|
|
format: date-time
|
|
RuleRunResponse:
|
|
type: object
|
|
required:
|
|
- data
|
|
properties:
|
|
data:
|
|
"$ref": "#/components/schemas/RuleRun"
|
|
RuleRunCollection:
|
|
type: object
|
|
required:
|
|
- data
|
|
- meta
|
|
properties:
|
|
data:
|
|
type: array
|
|
items:
|
|
"$ref": "#/components/schemas/RuleRun"
|
|
meta:
|
|
type: object
|
|
required:
|
|
- current_page
|
|
- total_pages
|
|
- total_count
|
|
- per_page
|
|
properties:
|
|
current_page:
|
|
type: integer
|
|
next_page:
|
|
type: integer
|
|
nullable: true
|
|
prev_page:
|
|
type: integer
|
|
nullable: true
|
|
total_pages:
|
|
type: integer
|
|
total_count:
|
|
type: integer
|
|
per_page:
|
|
type: integer
|
|
Transfer:
|
|
type: object
|
|
required:
|
|
- id
|
|
- amount
|
|
- currency
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
amount:
|
|
type: string
|
|
currency:
|
|
type: string
|
|
other_account:
|
|
"$ref": "#/components/schemas/Account"
|
|
nullable: true
|
|
RecurringTransaction:
|
|
type: object
|
|
required:
|
|
- id
|
|
- amount
|
|
- amount_cents
|
|
- currency
|
|
- expected_day_of_month
|
|
- last_occurrence_date
|
|
- next_expected_date
|
|
- status
|
|
- occurrence_count
|
|
- manual
|
|
- created_at
|
|
- updated_at
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
amount:
|
|
type: string
|
|
amount_cents:
|
|
type: integer
|
|
description: Amount in currency minor units
|
|
currency:
|
|
type: string
|
|
expected_day_of_month:
|
|
type: integer
|
|
minimum: 1
|
|
maximum: 31
|
|
last_occurrence_date:
|
|
type: string
|
|
format: date
|
|
next_expected_date:
|
|
type: string
|
|
format: date
|
|
status:
|
|
type: string
|
|
enum:
|
|
- suggested
|
|
- active
|
|
- paused
|
|
- inactive
|
|
- ended
|
|
occurrence_count:
|
|
type: integer
|
|
minimum: 0
|
|
name:
|
|
type: string
|
|
nullable: true
|
|
manual:
|
|
type: boolean
|
|
payment_url:
|
|
type: string
|
|
nullable: true
|
|
description: Link to the biller portal where this bill is paid. Only http
|
|
and https are accepted; a bare host is stored as https.
|
|
autopay:
|
|
type: boolean
|
|
description: Whether this bill pays itself automatically.
|
|
notes:
|
|
type: string
|
|
nullable: true
|
|
description: Free-text notes shown alongside the bill.
|
|
bill_type:
|
|
type: string
|
|
enum:
|
|
- bill
|
|
- subscription
|
|
- installment
|
|
- income
|
|
- transfer
|
|
- other
|
|
description: What kind of obligation this is.
|
|
category_id:
|
|
type: string
|
|
format: uuid
|
|
nullable: true
|
|
anchor_date:
|
|
type: string
|
|
format: date
|
|
nullable: true
|
|
description: Reference occurrence that phases every-N cadences.
|
|
weekend_adjust:
|
|
type: string
|
|
enum:
|
|
- none
|
|
- skip
|
|
- before
|
|
- after
|
|
end_mode:
|
|
type: string
|
|
enum:
|
|
- never
|
|
- on_date
|
|
- after_count
|
|
end_on:
|
|
type: string
|
|
format: date
|
|
nullable: true
|
|
end_after_count:
|
|
type: integer
|
|
nullable: true
|
|
renews_on:
|
|
type: string
|
|
format: date
|
|
nullable: true
|
|
trial_ends_on:
|
|
type: string
|
|
format: date
|
|
nullable: true
|
|
cancelled_on:
|
|
type: string
|
|
format: date
|
|
nullable: true
|
|
recurrence_rules:
|
|
type: array
|
|
description: Repetition patterns; multiple rows express semimonthly and
|
|
similar multi-pattern cadences. Empty means legacy monthly on expected_day_of_month.
|
|
items:
|
|
type: object
|
|
properties:
|
|
frequency:
|
|
type: string
|
|
enum:
|
|
- weekly
|
|
- monthly
|
|
- yearly
|
|
interval:
|
|
type: integer
|
|
day_of_month:
|
|
type: integer
|
|
nullable: true
|
|
description: "-1 means the last day of the month."
|
|
weekday:
|
|
type: integer
|
|
nullable: true
|
|
weekday_ordinal:
|
|
type: integer
|
|
nullable: true
|
|
description: "-1 means the last such weekday."
|
|
month_of_year:
|
|
type: integer
|
|
nullable: true
|
|
expected_amount_min:
|
|
type: string
|
|
nullable: true
|
|
expected_amount_min_cents:
|
|
type: integer
|
|
nullable: true
|
|
description: Minimum expected amount in currency minor units
|
|
expected_amount_max:
|
|
type: string
|
|
nullable: true
|
|
expected_amount_max_cents:
|
|
type: integer
|
|
nullable: true
|
|
description: Maximum expected amount in currency minor units
|
|
expected_amount_avg:
|
|
type: string
|
|
nullable: true
|
|
expected_amount_avg_cents:
|
|
type: integer
|
|
nullable: true
|
|
description: Average expected amount in currency minor units
|
|
account:
|
|
"$ref": "#/components/schemas/Account"
|
|
nullable: true
|
|
merchant:
|
|
"$ref": "#/components/schemas/Merchant"
|
|
nullable: true
|
|
created_at:
|
|
type: string
|
|
format: date-time
|
|
updated_at:
|
|
type: string
|
|
format: date-time
|
|
RecurringTransactionCollection:
|
|
type: object
|
|
required:
|
|
- recurring_transactions
|
|
- pagination
|
|
properties:
|
|
recurring_transactions:
|
|
type: array
|
|
items:
|
|
"$ref": "#/components/schemas/RecurringTransaction"
|
|
pagination:
|
|
"$ref": "#/components/schemas/Pagination"
|
|
Transaction:
|
|
type: object
|
|
required:
|
|
- id
|
|
- date
|
|
- amount
|
|
- currency
|
|
- name
|
|
- classification
|
|
- account
|
|
- tags
|
|
- created_at
|
|
- updated_at
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
date:
|
|
type: string
|
|
format: date
|
|
amount:
|
|
type: string
|
|
currency:
|
|
type: string
|
|
name:
|
|
type: string
|
|
notes:
|
|
type: string
|
|
nullable: true
|
|
external_id:
|
|
type: string
|
|
nullable: true
|
|
source:
|
|
type: string
|
|
nullable: true
|
|
user_modified:
|
|
type: boolean
|
|
classification:
|
|
type: string
|
|
account:
|
|
"$ref": "#/components/schemas/Account"
|
|
category:
|
|
"$ref": "#/components/schemas/Category"
|
|
nullable: true
|
|
merchant:
|
|
"$ref": "#/components/schemas/Merchant"
|
|
nullable: true
|
|
tags:
|
|
type: array
|
|
items:
|
|
"$ref": "#/components/schemas/Tag"
|
|
transfer:
|
|
"$ref": "#/components/schemas/Transfer"
|
|
nullable: true
|
|
created_at:
|
|
type: string
|
|
format: date-time
|
|
updated_at:
|
|
type: string
|
|
format: date-time
|
|
TransactionCollection:
|
|
type: object
|
|
required:
|
|
- transactions
|
|
- pagination
|
|
properties:
|
|
transactions:
|
|
type: array
|
|
items:
|
|
"$ref": "#/components/schemas/Transaction"
|
|
pagination:
|
|
"$ref": "#/components/schemas/Pagination"
|
|
TransferTransactionSide:
|
|
type: object
|
|
required:
|
|
- id
|
|
- entry_id
|
|
- date
|
|
- amount
|
|
- amount_cents
|
|
- currency
|
|
- name
|
|
- kind
|
|
- account
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
entry_id:
|
|
type: string
|
|
format: uuid
|
|
date:
|
|
type: string
|
|
format: date
|
|
amount:
|
|
type: string
|
|
amount_cents:
|
|
type: integer
|
|
description: Signed amount in currency minor units
|
|
currency:
|
|
type: string
|
|
name:
|
|
type: string
|
|
kind:
|
|
type: string
|
|
account:
|
|
type: object
|
|
required:
|
|
- id
|
|
- name
|
|
- account_type
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
name:
|
|
type: string
|
|
account_type:
|
|
type: string
|
|
nullable: true
|
|
TransferDecision:
|
|
type: object
|
|
required:
|
|
- id
|
|
- status
|
|
- date
|
|
- amount
|
|
- amount_cents
|
|
- currency
|
|
- transfer_type
|
|
- inflow_transaction
|
|
- outflow_transaction
|
|
- created_at
|
|
- updated_at
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
status:
|
|
type: string
|
|
enum:
|
|
- pending
|
|
- confirmed
|
|
date:
|
|
type: string
|
|
format: date
|
|
amount:
|
|
type: string
|
|
amount_cents:
|
|
type: integer
|
|
description: Absolute transfer amount in currency minor units
|
|
currency:
|
|
type: string
|
|
transfer_type:
|
|
type: string
|
|
enum:
|
|
- transfer
|
|
- liability_payment
|
|
- loan_payment
|
|
notes:
|
|
type: string
|
|
nullable: true
|
|
source_fee_amount:
|
|
type: string
|
|
nullable: true
|
|
description: Fee charged to the source account
|
|
source_fee_currency:
|
|
type: string
|
|
nullable: true
|
|
destination_fee_amount:
|
|
type: string
|
|
nullable: true
|
|
description: Fee deducted from the destination account
|
|
destination_fee_currency:
|
|
type: string
|
|
nullable: true
|
|
inflow_transaction:
|
|
"$ref": "#/components/schemas/TransferTransactionSide"
|
|
outflow_transaction:
|
|
"$ref": "#/components/schemas/TransferTransactionSide"
|
|
created_at:
|
|
type: string
|
|
format: date-time
|
|
updated_at:
|
|
type: string
|
|
format: date-time
|
|
TransferDecisionCollection:
|
|
type: object
|
|
required:
|
|
- transfers
|
|
- pagination
|
|
properties:
|
|
transfers:
|
|
type: array
|
|
items:
|
|
"$ref": "#/components/schemas/TransferDecision"
|
|
pagination:
|
|
"$ref": "#/components/schemas/Pagination"
|
|
RejectedTransfer:
|
|
type: object
|
|
required:
|
|
- id
|
|
- inflow_transaction
|
|
- outflow_transaction
|
|
- created_at
|
|
- updated_at
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
inflow_transaction:
|
|
"$ref": "#/components/schemas/TransferTransactionSide"
|
|
outflow_transaction:
|
|
"$ref": "#/components/schemas/TransferTransactionSide"
|
|
created_at:
|
|
type: string
|
|
format: date-time
|
|
updated_at:
|
|
type: string
|
|
format: date-time
|
|
RejectedTransferCollection:
|
|
type: object
|
|
required:
|
|
- rejected_transfers
|
|
- pagination
|
|
properties:
|
|
rejected_transfers:
|
|
type: array
|
|
items:
|
|
"$ref": "#/components/schemas/RejectedTransfer"
|
|
pagination:
|
|
"$ref": "#/components/schemas/Pagination"
|
|
Valuation:
|
|
type: object
|
|
required:
|
|
- id
|
|
- date
|
|
- amount
|
|
- currency
|
|
- kind
|
|
- account
|
|
- created_at
|
|
- updated_at
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
date:
|
|
type: string
|
|
format: date
|
|
amount:
|
|
type: string
|
|
currency:
|
|
type: string
|
|
notes:
|
|
type: string
|
|
nullable: true
|
|
kind:
|
|
type: string
|
|
account:
|
|
"$ref": "#/components/schemas/Account"
|
|
created_at:
|
|
type: string
|
|
format: date-time
|
|
updated_at:
|
|
type: string
|
|
format: date-time
|
|
ValuationCollection:
|
|
type: object
|
|
required:
|
|
- valuations
|
|
- pagination
|
|
properties:
|
|
valuations:
|
|
type: array
|
|
items:
|
|
"$ref": "#/components/schemas/Valuation"
|
|
pagination:
|
|
"$ref": "#/components/schemas/Pagination"
|
|
DeleteResponse:
|
|
type: object
|
|
required:
|
|
- message
|
|
properties:
|
|
message:
|
|
type: string
|
|
TransactionResponse:
|
|
type: object
|
|
required:
|
|
- id
|
|
- date
|
|
- amount
|
|
- currency
|
|
- name
|
|
- entryable_type
|
|
- account
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
date:
|
|
type: string
|
|
format: date
|
|
amount:
|
|
type: string
|
|
currency:
|
|
type: string
|
|
name:
|
|
type: string
|
|
entryable_type:
|
|
type: string
|
|
account:
|
|
type: object
|
|
required:
|
|
- id
|
|
- name
|
|
- account_type
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
name:
|
|
type: string
|
|
account_type:
|
|
type: string
|
|
nullable: true
|
|
ImportConfiguration:
|
|
type: object
|
|
properties:
|
|
date_col_label:
|
|
type: string
|
|
nullable: true
|
|
amount_col_label:
|
|
type: string
|
|
nullable: true
|
|
name_col_label:
|
|
type: string
|
|
nullable: true
|
|
category_col_label:
|
|
type: string
|
|
nullable: true
|
|
tags_col_label:
|
|
type: string
|
|
nullable: true
|
|
notes_col_label:
|
|
type: string
|
|
nullable: true
|
|
account_col_label:
|
|
type: string
|
|
nullable: true
|
|
date_format:
|
|
type: string
|
|
nullable: true
|
|
number_format:
|
|
type: string
|
|
nullable: true
|
|
signage_convention:
|
|
type: string
|
|
nullable: true
|
|
ImportStats:
|
|
type: object
|
|
required:
|
|
- rows_count
|
|
- valid_rows_count
|
|
- invalid_rows_count
|
|
- mappings_count
|
|
- unassigned_mappings_count
|
|
properties:
|
|
rows_count:
|
|
type: integer
|
|
minimum: 0
|
|
valid_rows_count:
|
|
type: integer
|
|
minimum: 0
|
|
invalid_rows_count:
|
|
type: integer
|
|
minimum: 0
|
|
mappings_count:
|
|
type: integer
|
|
minimum: 0
|
|
unassigned_mappings_count:
|
|
type: integer
|
|
minimum: 0
|
|
ImportVerificationReadback:
|
|
type: object
|
|
description: SureImport only. Expected NDJSON counts compared to family-scoped
|
|
database readback after publish.
|
|
properties:
|
|
status:
|
|
type: string
|
|
enum:
|
|
- not_verified
|
|
- matched
|
|
- mismatch
|
|
- failed
|
|
- reverted
|
|
checked_at:
|
|
type: string
|
|
format: date-time
|
|
nullable: true
|
|
expected_record_counts:
|
|
type: object
|
|
additionalProperties:
|
|
type: integer
|
|
before_counts:
|
|
type: object
|
|
additionalProperties:
|
|
type: integer
|
|
after_counts:
|
|
type: object
|
|
additionalProperties:
|
|
type: integer
|
|
actual_delta_counts:
|
|
type: object
|
|
additionalProperties:
|
|
type: integer
|
|
checked_counts:
|
|
type: object
|
|
additionalProperties:
|
|
type: integer
|
|
mismatches:
|
|
type: object
|
|
additionalProperties:
|
|
type: object
|
|
required:
|
|
- expected
|
|
- actual
|
|
properties:
|
|
expected:
|
|
type: integer
|
|
actual:
|
|
type: integer
|
|
error:
|
|
type: string
|
|
nullable: true
|
|
ImportVerification:
|
|
type: object
|
|
description: SureImport only. Captured at upload and completed after import
|
|
publish.
|
|
required:
|
|
- expected_record_counts
|
|
- readback
|
|
properties:
|
|
expected_record_counts:
|
|
type: object
|
|
additionalProperties:
|
|
type: integer
|
|
readback:
|
|
"$ref": "#/components/schemas/ImportVerificationReadback"
|
|
ImportPreflightContent:
|
|
type: object
|
|
required:
|
|
- filename
|
|
- content_type
|
|
- byte_size
|
|
properties:
|
|
filename:
|
|
type: string
|
|
content_type:
|
|
type: string
|
|
byte_size:
|
|
type: integer
|
|
minimum: 0
|
|
ImportPreflightError:
|
|
type: object
|
|
required:
|
|
- code
|
|
- message
|
|
properties:
|
|
code:
|
|
type: string
|
|
message:
|
|
type: string
|
|
ImportPreflightStats:
|
|
type: object
|
|
required:
|
|
- rows_count
|
|
properties:
|
|
rows_count:
|
|
type: integer
|
|
minimum: 0
|
|
description: CSV parsed non-header rows, or nonblank Sure NDJSON lines.
|
|
valid_rows_count:
|
|
type: integer
|
|
minimum: 0
|
|
description: SureImport only. Valid NDJSON records.
|
|
invalid_rows_count:
|
|
type: integer
|
|
minimum: 0
|
|
description: SureImport only. Invalid NDJSON records. CSV malformed content
|
|
returns a 422 instead.
|
|
entity_counts:
|
|
type: object
|
|
additionalProperties:
|
|
type: integer
|
|
nullable: true
|
|
record_type_counts:
|
|
type: object
|
|
additionalProperties:
|
|
type: integer
|
|
nullable: true
|
|
ImportPreflight:
|
|
type: object
|
|
required:
|
|
- type
|
|
- valid
|
|
- content
|
|
- stats
|
|
- errors
|
|
- warnings
|
|
properties:
|
|
type:
|
|
type: string
|
|
enum:
|
|
- TransactionImport
|
|
- TradeImport
|
|
- AccountImport
|
|
- MintImport
|
|
- ActualImport
|
|
- YnabImport
|
|
- CategoryImport
|
|
- RuleImport
|
|
- MerchantImport
|
|
- PdfImport
|
|
- QifImport
|
|
- SureImport
|
|
valid:
|
|
type: boolean
|
|
content:
|
|
"$ref": "#/components/schemas/ImportPreflightContent"
|
|
stats:
|
|
"$ref": "#/components/schemas/ImportPreflightStats"
|
|
headers:
|
|
type: array
|
|
items:
|
|
type: string
|
|
nullable: true
|
|
required_headers:
|
|
type: array
|
|
items:
|
|
type: string
|
|
nullable: true
|
|
missing_required_headers:
|
|
type: array
|
|
items:
|
|
type: string
|
|
nullable: true
|
|
errors:
|
|
type: array
|
|
items:
|
|
"$ref": "#/components/schemas/ImportPreflightError"
|
|
warnings:
|
|
type: array
|
|
items:
|
|
type: string
|
|
ImportPreflightResponse:
|
|
type: object
|
|
required:
|
|
- data
|
|
properties:
|
|
data:
|
|
"$ref": "#/components/schemas/ImportPreflight"
|
|
ImportStatusSummary:
|
|
type: object
|
|
required:
|
|
- uploaded
|
|
- configured
|
|
- terminal
|
|
properties:
|
|
uploaded:
|
|
type: boolean
|
|
configured:
|
|
type: boolean
|
|
terminal:
|
|
type: boolean
|
|
ImportStatusDetail:
|
|
allOf:
|
|
- "$ref": "#/components/schemas/ImportStatusSummary"
|
|
- type: object
|
|
required:
|
|
- cleaned
|
|
- publishable
|
|
- revertable
|
|
properties:
|
|
cleaned:
|
|
type: boolean
|
|
publishable:
|
|
type: boolean
|
|
revertable:
|
|
type: boolean
|
|
ImportSummary:
|
|
type: object
|
|
required:
|
|
- id
|
|
- type
|
|
- status
|
|
- created_at
|
|
- updated_at
|
|
- status_detail
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
type:
|
|
type: string
|
|
enum:
|
|
- TransactionImport
|
|
- TradeImport
|
|
- AccountImport
|
|
- MintImport
|
|
- ActualImport
|
|
- YnabImport
|
|
- CategoryImport
|
|
- RuleImport
|
|
- MerchantImport
|
|
- PdfImport
|
|
- QifImport
|
|
- SureImport
|
|
status:
|
|
type: string
|
|
enum:
|
|
- pending
|
|
- complete
|
|
- importing
|
|
- reverting
|
|
- revert_failed
|
|
- failed
|
|
created_at:
|
|
type: string
|
|
format: date-time
|
|
updated_at:
|
|
type: string
|
|
format: date-time
|
|
account_id:
|
|
type: string
|
|
format: uuid
|
|
nullable: true
|
|
rows_count:
|
|
type: integer
|
|
minimum: 0
|
|
error:
|
|
type: string
|
|
nullable: true
|
|
status_detail:
|
|
"$ref": "#/components/schemas/ImportStatusSummary"
|
|
ImportDetail:
|
|
type: object
|
|
required:
|
|
- id
|
|
- type
|
|
- status
|
|
- created_at
|
|
- updated_at
|
|
- status_detail
|
|
- configuration
|
|
- stats
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
type:
|
|
type: string
|
|
enum:
|
|
- TransactionImport
|
|
- TradeImport
|
|
- AccountImport
|
|
- MintImport
|
|
- ActualImport
|
|
- YnabImport
|
|
- CategoryImport
|
|
- RuleImport
|
|
- MerchantImport
|
|
- PdfImport
|
|
- QifImport
|
|
- SureImport
|
|
status:
|
|
type: string
|
|
enum:
|
|
- pending
|
|
- complete
|
|
- importing
|
|
- reverting
|
|
- revert_failed
|
|
- failed
|
|
created_at:
|
|
type: string
|
|
format: date-time
|
|
updated_at:
|
|
type: string
|
|
format: date-time
|
|
account_id:
|
|
type: string
|
|
format: uuid
|
|
nullable: true
|
|
error:
|
|
type: string
|
|
nullable: true
|
|
status_detail:
|
|
"$ref": "#/components/schemas/ImportStatusDetail"
|
|
configuration:
|
|
"$ref": "#/components/schemas/ImportConfiguration"
|
|
stats:
|
|
"$ref": "#/components/schemas/ImportStats"
|
|
verification:
|
|
"$ref": "#/components/schemas/ImportVerification"
|
|
ImportCollection:
|
|
type: object
|
|
required:
|
|
- data
|
|
- meta
|
|
properties:
|
|
data:
|
|
type: array
|
|
items:
|
|
"$ref": "#/components/schemas/ImportSummary"
|
|
meta:
|
|
type: object
|
|
required:
|
|
- current_page
|
|
- total_pages
|
|
- total_count
|
|
- per_page
|
|
properties:
|
|
current_page:
|
|
type: integer
|
|
minimum: 1
|
|
next_page:
|
|
type: integer
|
|
nullable: true
|
|
prev_page:
|
|
type: integer
|
|
nullable: true
|
|
total_pages:
|
|
type: integer
|
|
minimum: 0
|
|
total_count:
|
|
type: integer
|
|
minimum: 0
|
|
per_page:
|
|
type: integer
|
|
minimum: 1
|
|
ImportResponse:
|
|
type: object
|
|
required:
|
|
- data
|
|
properties:
|
|
data:
|
|
"$ref": "#/components/schemas/ImportDetail"
|
|
ImportSessionChunk:
|
|
type: object
|
|
required:
|
|
- id
|
|
- sequence
|
|
- status
|
|
- rows_count
|
|
- summary
|
|
- created_at
|
|
- updated_at
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
sequence:
|
|
type: integer
|
|
minimum: 1
|
|
client_chunk_id:
|
|
type: string
|
|
nullable: true
|
|
status:
|
|
type: string
|
|
enum:
|
|
- pending
|
|
- importing
|
|
- complete
|
|
- failed
|
|
rows_count:
|
|
type: integer
|
|
minimum: 0
|
|
summary:
|
|
type: object
|
|
additionalProperties:
|
|
type: object
|
|
additionalProperties:
|
|
type: integer
|
|
error:
|
|
type: object
|
|
nullable: true
|
|
additionalProperties: true
|
|
created_at:
|
|
type: string
|
|
format: date-time
|
|
updated_at:
|
|
type: string
|
|
format: date-time
|
|
ImportSession:
|
|
type: object
|
|
required:
|
|
- id
|
|
- type
|
|
- status
|
|
- chunks_count
|
|
- summary
|
|
- chunks
|
|
- created_at
|
|
- updated_at
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
type:
|
|
type: string
|
|
enum:
|
|
- SureImport
|
|
status:
|
|
type: string
|
|
enum:
|
|
- pending
|
|
- importing
|
|
- complete
|
|
- failed
|
|
client_session_id:
|
|
type: string
|
|
nullable: true
|
|
expected_chunks:
|
|
type: integer
|
|
nullable: true
|
|
minimum: 1
|
|
chunks_count:
|
|
type: integer
|
|
minimum: 0
|
|
summary:
|
|
type: object
|
|
additionalProperties:
|
|
type: object
|
|
additionalProperties:
|
|
type: integer
|
|
error:
|
|
type: object
|
|
nullable: true
|
|
additionalProperties: true
|
|
chunks:
|
|
type: array
|
|
items:
|
|
"$ref": "#/components/schemas/ImportSessionChunk"
|
|
created_at:
|
|
type: string
|
|
format: date-time
|
|
updated_at:
|
|
type: string
|
|
format: date-time
|
|
ImportSessionResponse:
|
|
type: object
|
|
required:
|
|
- data
|
|
properties:
|
|
data:
|
|
"$ref": "#/components/schemas/ImportSession"
|
|
ProviderConnectionInstitution:
|
|
type: object
|
|
required:
|
|
- name
|
|
properties:
|
|
name:
|
|
type: string
|
|
nullable: true
|
|
domain:
|
|
type: string
|
|
nullable: true
|
|
url:
|
|
type: string
|
|
nullable: true
|
|
ProviderConnectionAccounts:
|
|
type: object
|
|
required:
|
|
- total_count
|
|
- linked_count
|
|
- unlinked_count
|
|
properties:
|
|
total_count:
|
|
type: integer
|
|
minimum: 0
|
|
linked_count:
|
|
type: integer
|
|
minimum: 0
|
|
unlinked_count:
|
|
type: integer
|
|
minimum: 0
|
|
ProviderConnectionSyncLatest:
|
|
type: object
|
|
required:
|
|
- id
|
|
- status
|
|
- created_at
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
status:
|
|
type: string
|
|
created_at:
|
|
type: string
|
|
format: date-time
|
|
syncing_at:
|
|
type: string
|
|
format: date-time
|
|
nullable: true
|
|
completed_at:
|
|
type: string
|
|
format: date-time
|
|
nullable: true
|
|
failed_at:
|
|
type: string
|
|
format: date-time
|
|
nullable: true
|
|
error:
|
|
type: object
|
|
nullable: true
|
|
description: Sanitized latest sync error summary. Null when the latest sync
|
|
is not failed or stale.
|
|
required:
|
|
- present
|
|
properties:
|
|
present:
|
|
type: boolean
|
|
description: Always true when this object is present.
|
|
message:
|
|
type: string
|
|
nullable: true
|
|
description: Stable sanitized error category message; raw provider error
|
|
text is never exposed.
|
|
ProviderConnectionSync:
|
|
type: object
|
|
required:
|
|
- syncing
|
|
properties:
|
|
syncing:
|
|
type: boolean
|
|
status_summary:
|
|
type: string
|
|
nullable: true
|
|
last_synced_at:
|
|
type: string
|
|
format: date-time
|
|
nullable: true
|
|
latest:
|
|
allOf:
|
|
- "$ref": "#/components/schemas/ProviderConnectionSyncLatest"
|
|
nullable: true
|
|
ProviderConnection:
|
|
type: object
|
|
required:
|
|
- id
|
|
- provider
|
|
- provider_type
|
|
- name
|
|
- status
|
|
- requires_update
|
|
- credentials_configured
|
|
- scheduled_for_deletion
|
|
- pending_account_setup
|
|
- institution
|
|
- accounts
|
|
- sync
|
|
- created_at
|
|
- updated_at
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
provider:
|
|
type: string
|
|
provider_type:
|
|
type: string
|
|
name:
|
|
type: string
|
|
status:
|
|
type: string
|
|
nullable: true
|
|
requires_update:
|
|
type: boolean
|
|
nullable: true
|
|
description: False when the provider item does not expose this status.
|
|
credentials_configured:
|
|
type: boolean
|
|
nullable: true
|
|
description: False when credential readiness is unknown.
|
|
scheduled_for_deletion:
|
|
type: boolean
|
|
nullable: true
|
|
description: False when the provider item does not expose this status.
|
|
pending_account_setup:
|
|
type: boolean
|
|
nullable: true
|
|
description: False when account setup state is unknown.
|
|
institution:
|
|
"$ref": "#/components/schemas/ProviderConnectionInstitution"
|
|
accounts:
|
|
"$ref": "#/components/schemas/ProviderConnectionAccounts"
|
|
sync:
|
|
"$ref": "#/components/schemas/ProviderConnectionSync"
|
|
created_at:
|
|
type: string
|
|
format: date-time
|
|
updated_at:
|
|
type: string
|
|
format: date-time
|
|
ProviderConnectionCollection:
|
|
type: object
|
|
required:
|
|
- data
|
|
properties:
|
|
data:
|
|
type: array
|
|
items:
|
|
"$ref": "#/components/schemas/ProviderConnection"
|
|
ImportRowMapping:
|
|
type: object
|
|
required:
|
|
- key
|
|
- type
|
|
- value
|
|
- create_when_empty
|
|
- creatable
|
|
- mappable
|
|
properties:
|
|
key:
|
|
type: string
|
|
nullable: true
|
|
type:
|
|
type: string
|
|
value:
|
|
type: string
|
|
nullable: true
|
|
create_when_empty:
|
|
type: boolean
|
|
creatable:
|
|
type: boolean
|
|
mappable:
|
|
type: object
|
|
nullable: true
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
type:
|
|
type: string
|
|
name:
|
|
type: string
|
|
nullable: true
|
|
ImportRowDiagnostic:
|
|
type: object
|
|
required:
|
|
- id
|
|
- row_number
|
|
- valid
|
|
- errors
|
|
- fields
|
|
- mappings
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
row_number:
|
|
type: integer
|
|
minimum: 1
|
|
valid:
|
|
type: boolean
|
|
errors:
|
|
type: array
|
|
items:
|
|
type: string
|
|
fields:
|
|
type: object
|
|
properties:
|
|
account:
|
|
type: string
|
|
nullable: true
|
|
date:
|
|
type: string
|
|
nullable: true
|
|
qty:
|
|
type: string
|
|
nullable: true
|
|
ticker:
|
|
type: string
|
|
nullable: true
|
|
exchange_operating_mic:
|
|
type: string
|
|
nullable: true
|
|
price:
|
|
type: string
|
|
nullable: true
|
|
amount:
|
|
type: string
|
|
nullable: true
|
|
currency:
|
|
type: string
|
|
nullable: true
|
|
name:
|
|
type: string
|
|
nullable: true
|
|
category:
|
|
type: string
|
|
nullable: true
|
|
tags:
|
|
type: string
|
|
nullable: true
|
|
entity_type:
|
|
type: string
|
|
nullable: true
|
|
notes:
|
|
type: string
|
|
nullable: true
|
|
active:
|
|
type: boolean
|
|
nullable: true
|
|
effective_date:
|
|
type: string
|
|
nullable: true
|
|
conditions:
|
|
type: string
|
|
nullable: true
|
|
actions:
|
|
type: string
|
|
nullable: true
|
|
mappings:
|
|
type: object
|
|
properties:
|
|
account:
|
|
"$ref": "#/components/schemas/ImportRowMapping"
|
|
category:
|
|
"$ref": "#/components/schemas/ImportRowMapping"
|
|
account_type:
|
|
"$ref": "#/components/schemas/ImportRowMapping"
|
|
tags:
|
|
type: array
|
|
items:
|
|
"$ref": "#/components/schemas/ImportRowMapping"
|
|
ImportRowDiagnosticCollection:
|
|
type: object
|
|
required:
|
|
- data
|
|
- meta
|
|
properties:
|
|
data:
|
|
type: array
|
|
items:
|
|
"$ref": "#/components/schemas/ImportRowDiagnostic"
|
|
meta:
|
|
type: object
|
|
required:
|
|
- current_page
|
|
- total_pages
|
|
- total_count
|
|
- per_page
|
|
properties:
|
|
current_page:
|
|
type: integer
|
|
minimum: 1
|
|
next_page:
|
|
type: integer
|
|
nullable: true
|
|
prev_page:
|
|
type: integer
|
|
nullable: true
|
|
total_pages:
|
|
type: integer
|
|
minimum: 0
|
|
total_count:
|
|
type: integer
|
|
minimum: 0
|
|
per_page:
|
|
type: integer
|
|
minimum: 1
|
|
SyncableSummary:
|
|
type: object
|
|
required:
|
|
- type
|
|
- id
|
|
properties:
|
|
type:
|
|
type: string
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
name:
|
|
type: string
|
|
nullable: true
|
|
SyncErrorSummary:
|
|
type: object
|
|
required:
|
|
- message
|
|
properties:
|
|
message:
|
|
type: string
|
|
SyncResource:
|
|
type: object
|
|
required:
|
|
- id
|
|
- status
|
|
- in_progress
|
|
- terminal
|
|
- syncable
|
|
- children_count
|
|
- created_at
|
|
- updated_at
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
status:
|
|
type: string
|
|
enum:
|
|
- pending
|
|
- syncing
|
|
- completed
|
|
- failed
|
|
- stale
|
|
in_progress:
|
|
type: boolean
|
|
terminal:
|
|
type: boolean
|
|
syncable:
|
|
"$ref": "#/components/schemas/SyncableSummary"
|
|
parent_id:
|
|
type: string
|
|
format: uuid
|
|
nullable: true
|
|
children_count:
|
|
type: integer
|
|
minimum: 0
|
|
window_start_date:
|
|
type: string
|
|
format: date
|
|
nullable: true
|
|
window_end_date:
|
|
type: string
|
|
format: date
|
|
nullable: true
|
|
pending_at:
|
|
type: string
|
|
format: date-time
|
|
nullable: true
|
|
syncing_at:
|
|
type: string
|
|
format: date-time
|
|
nullable: true
|
|
completed_at:
|
|
type: string
|
|
format: date-time
|
|
nullable: true
|
|
failed_at:
|
|
type: string
|
|
format: date-time
|
|
nullable: true
|
|
error:
|
|
nullable: true
|
|
allOf:
|
|
- "$ref": "#/components/schemas/SyncErrorSummary"
|
|
created_at:
|
|
type: string
|
|
format: date-time
|
|
updated_at:
|
|
type: string
|
|
format: date-time
|
|
SyncResponse:
|
|
type: object
|
|
required:
|
|
- data
|
|
properties:
|
|
data:
|
|
nullable: true
|
|
allOf:
|
|
- "$ref": "#/components/schemas/SyncResource"
|
|
SyncCollection:
|
|
type: object
|
|
required:
|
|
- data
|
|
- meta
|
|
properties:
|
|
data:
|
|
type: array
|
|
maxItems: 100
|
|
items:
|
|
"$ref": "#/components/schemas/SyncResource"
|
|
meta:
|
|
"$ref": "#/components/schemas/Pagination"
|
|
Trade:
|
|
type: object
|
|
required:
|
|
- id
|
|
- date
|
|
- amount
|
|
- currency
|
|
- name
|
|
- qty
|
|
- price
|
|
- account
|
|
- created_at
|
|
- updated_at
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
date:
|
|
type: string
|
|
format: date
|
|
amount:
|
|
type: string
|
|
currency:
|
|
type: string
|
|
name:
|
|
type: string
|
|
notes:
|
|
type: string
|
|
nullable: true
|
|
qty:
|
|
type: string
|
|
price:
|
|
type: string
|
|
investment_activity_label:
|
|
type: string
|
|
nullable: true
|
|
account:
|
|
"$ref": "#/components/schemas/Account"
|
|
security:
|
|
type: object
|
|
nullable: true
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
ticker:
|
|
type: string
|
|
name:
|
|
type: string
|
|
nullable: true
|
|
category:
|
|
type: object
|
|
nullable: true
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
name:
|
|
type: string
|
|
created_at:
|
|
type: string
|
|
format: date-time
|
|
updated_at:
|
|
type: string
|
|
format: date-time
|
|
TradeCollection:
|
|
type: object
|
|
required:
|
|
- trades
|
|
- pagination
|
|
properties:
|
|
trades:
|
|
type: array
|
|
items:
|
|
"$ref": "#/components/schemas/Trade"
|
|
pagination:
|
|
"$ref": "#/components/schemas/Pagination"
|
|
Holding:
|
|
type: object
|
|
required:
|
|
- id
|
|
- date
|
|
- qty
|
|
- price
|
|
- amount
|
|
- currency
|
|
- account
|
|
- security
|
|
- created_at
|
|
- updated_at
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
date:
|
|
type: string
|
|
format: date
|
|
qty:
|
|
type: string
|
|
description: Quantity of shares held
|
|
price:
|
|
type: string
|
|
description: Formatted price per share
|
|
amount:
|
|
type: string
|
|
currency:
|
|
type: string
|
|
cost_basis_source:
|
|
type: string
|
|
nullable: true
|
|
account:
|
|
"$ref": "#/components/schemas/Account"
|
|
security:
|
|
type: object
|
|
required:
|
|
- id
|
|
- ticker
|
|
- name
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
ticker:
|
|
type: string
|
|
name:
|
|
type: string
|
|
nullable: true
|
|
avg_cost:
|
|
type: string
|
|
nullable: true
|
|
created_at:
|
|
type: string
|
|
format: date-time
|
|
updated_at:
|
|
type: string
|
|
format: date-time
|
|
HoldingCollection:
|
|
type: object
|
|
required:
|
|
- holdings
|
|
- pagination
|
|
properties:
|
|
holdings:
|
|
type: array
|
|
items:
|
|
"$ref": "#/components/schemas/Holding"
|
|
pagination:
|
|
"$ref": "#/components/schemas/Pagination"
|
|
Security:
|
|
type: object
|
|
required:
|
|
- id
|
|
- ticker
|
|
- kind
|
|
- offline
|
|
- created_at
|
|
- updated_at
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
ticker:
|
|
type: string
|
|
name:
|
|
type: string
|
|
nullable: true
|
|
kind:
|
|
type: string
|
|
enum:
|
|
- standard
|
|
- cash
|
|
country_code:
|
|
type: string
|
|
nullable: true
|
|
exchange_mic:
|
|
type: string
|
|
nullable: true
|
|
exchange_acronym:
|
|
type: string
|
|
nullable: true
|
|
exchange_operating_mic:
|
|
type: string
|
|
nullable: true
|
|
exchange_name:
|
|
type: string
|
|
nullable: true
|
|
offline:
|
|
type: boolean
|
|
offline_reason:
|
|
type: string
|
|
nullable: true
|
|
website_url:
|
|
type: string
|
|
nullable: true
|
|
logo_url:
|
|
type: string
|
|
nullable: true
|
|
first_provider_price_on:
|
|
type: string
|
|
format: date
|
|
nullable: true
|
|
created_at:
|
|
type: string
|
|
format: date-time
|
|
updated_at:
|
|
type: string
|
|
format: date-time
|
|
SecurityCollection:
|
|
type: object
|
|
required:
|
|
- securities
|
|
- pagination
|
|
properties:
|
|
securities:
|
|
type: array
|
|
items:
|
|
"$ref": "#/components/schemas/Security"
|
|
pagination:
|
|
"$ref": "#/components/schemas/Pagination"
|
|
SecurityPrice:
|
|
type: object
|
|
required:
|
|
- id
|
|
- date
|
|
- price
|
|
- price_amount
|
|
- currency
|
|
- provisional
|
|
- security
|
|
- created_at
|
|
- updated_at
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
date:
|
|
type: string
|
|
format: date
|
|
price:
|
|
type: string
|
|
description: Formatted security price
|
|
price_amount:
|
|
type: string
|
|
description: Exact decimal security price
|
|
currency:
|
|
type: string
|
|
provisional:
|
|
type: boolean
|
|
security:
|
|
type: object
|
|
required:
|
|
- id
|
|
- ticker
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
ticker:
|
|
type: string
|
|
name:
|
|
type: string
|
|
nullable: true
|
|
exchange_operating_mic:
|
|
type: string
|
|
nullable: true
|
|
created_at:
|
|
type: string
|
|
format: date-time
|
|
updated_at:
|
|
type: string
|
|
format: date-time
|
|
SecurityPriceCollection:
|
|
type: object
|
|
required:
|
|
- security_prices
|
|
- pagination
|
|
properties:
|
|
security_prices:
|
|
type: array
|
|
items:
|
|
"$ref": "#/components/schemas/SecurityPrice"
|
|
pagination:
|
|
"$ref": "#/components/schemas/Pagination"
|
|
Money:
|
|
type: object
|
|
required:
|
|
- amount
|
|
- currency
|
|
- formatted
|
|
properties:
|
|
amount:
|
|
type: string
|
|
description: Numeric amount as string
|
|
currency:
|
|
type: string
|
|
description: ISO 4217 currency code
|
|
formatted:
|
|
type: string
|
|
description: Locale-formatted money string
|
|
BalanceSheet:
|
|
type: object
|
|
required:
|
|
- currency
|
|
- net_worth
|
|
- assets
|
|
- liabilities
|
|
properties:
|
|
currency:
|
|
type: string
|
|
description: Family primary currency
|
|
net_worth:
|
|
"$ref": "#/components/schemas/Money"
|
|
assets:
|
|
"$ref": "#/components/schemas/Money"
|
|
liabilities:
|
|
"$ref": "#/components/schemas/Money"
|
|
SuccessMessage:
|
|
type: object
|
|
required:
|
|
- message
|
|
properties:
|
|
message:
|
|
type: string
|
|
ResetInitiatedResponse:
|
|
type: object
|
|
required:
|
|
- message
|
|
- status
|
|
- job_id
|
|
- family_id
|
|
- status_url
|
|
properties:
|
|
message:
|
|
type: string
|
|
status:
|
|
type: string
|
|
enum:
|
|
- queued
|
|
job_id:
|
|
type: string
|
|
description: Informational Active Job identifier returned by the queue adapter;
|
|
reset status is family-scoped, not job-scoped.
|
|
family_id:
|
|
type: string
|
|
format: uuid
|
|
description: UUID of the family being reset.
|
|
status_url:
|
|
type: string
|
|
ResetStatusResponse:
|
|
type: object
|
|
required:
|
|
- status
|
|
- family_id
|
|
- reset_complete
|
|
- counts
|
|
properties:
|
|
status:
|
|
type: string
|
|
enum:
|
|
- complete
|
|
- data_remaining
|
|
description: Counts-based family reset status at response time.
|
|
family_id:
|
|
type: string
|
|
format: uuid
|
|
description: UUID of the family whose reset target counts were checked.
|
|
reset_complete:
|
|
type: boolean
|
|
description: True when all reset target counts are zero at response time.
|
|
This is a family data snapshot, not a durable per-job completion record.
|
|
counts:
|
|
type: object
|
|
required:
|
|
- account_statements
|
|
- family_exports
|
|
- imports
|
|
- import_sessions
|
|
- import_source_mappings
|
|
- import_rows
|
|
- import_mappings
|
|
- accounts
|
|
- account_shares
|
|
- account_providers
|
|
- entries
|
|
- transactions
|
|
- transfers
|
|
- rejected_transfers
|
|
- valuations
|
|
- trades
|
|
- holdings
|
|
- balances
|
|
- recurring_transactions
|
|
- rules
|
|
- rule_actions
|
|
- rule_conditions
|
|
- rule_runs
|
|
- budgets
|
|
- budget_categories
|
|
- categories
|
|
- tags
|
|
- taggings
|
|
- merchants
|
|
- family_merchant_associations
|
|
- provider_items
|
|
- active_storage_attachments
|
|
- plaid_items
|
|
additionalProperties:
|
|
type: integer
|
|
minimum: 0
|
|
properties:
|
|
account_statements:
|
|
type: integer
|
|
minimum: 0
|
|
family_exports:
|
|
type: integer
|
|
minimum: 0
|
|
imports:
|
|
type: integer
|
|
minimum: 0
|
|
import_sessions:
|
|
type: integer
|
|
minimum: 0
|
|
import_source_mappings:
|
|
type: integer
|
|
minimum: 0
|
|
import_rows:
|
|
type: integer
|
|
minimum: 0
|
|
import_mappings:
|
|
type: integer
|
|
minimum: 0
|
|
accounts:
|
|
type: integer
|
|
minimum: 0
|
|
account_shares:
|
|
type: integer
|
|
minimum: 0
|
|
account_providers:
|
|
type: integer
|
|
minimum: 0
|
|
entries:
|
|
type: integer
|
|
minimum: 0
|
|
transactions:
|
|
type: integer
|
|
minimum: 0
|
|
transfers:
|
|
type: integer
|
|
minimum: 0
|
|
rejected_transfers:
|
|
type: integer
|
|
minimum: 0
|
|
valuations:
|
|
type: integer
|
|
minimum: 0
|
|
trades:
|
|
type: integer
|
|
minimum: 0
|
|
holdings:
|
|
type: integer
|
|
minimum: 0
|
|
balances:
|
|
type: integer
|
|
minimum: 0
|
|
recurring_transactions:
|
|
type: integer
|
|
minimum: 0
|
|
rules:
|
|
type: integer
|
|
minimum: 0
|
|
rule_actions:
|
|
type: integer
|
|
minimum: 0
|
|
rule_conditions:
|
|
type: integer
|
|
minimum: 0
|
|
rule_runs:
|
|
type: integer
|
|
minimum: 0
|
|
budgets:
|
|
type: integer
|
|
minimum: 0
|
|
budget_categories:
|
|
type: integer
|
|
minimum: 0
|
|
categories:
|
|
type: integer
|
|
minimum: 0
|
|
tags:
|
|
type: integer
|
|
minimum: 0
|
|
taggings:
|
|
type: integer
|
|
minimum: 0
|
|
merchants:
|
|
type: integer
|
|
minimum: 0
|
|
family_merchant_associations:
|
|
type: integer
|
|
minimum: 0
|
|
provider_items:
|
|
type: integer
|
|
minimum: 0
|
|
active_storage_attachments:
|
|
type: integer
|
|
minimum: 0
|
|
plaid_items:
|
|
type: integer
|
|
minimum: 0
|
|
paths:
|
|
"/api/v1/accounts":
|
|
get:
|
|
summary: List accounts
|
|
tags:
|
|
- Accounts
|
|
security:
|
|
- apiKeyAuth: []
|
|
parameters:
|
|
- name: page
|
|
in: query
|
|
required: false
|
|
description: 'Page number (default: 1)'
|
|
schema:
|
|
type: integer
|
|
- name: per_page
|
|
in: query
|
|
required: false
|
|
description: 'Items per page (default: 25, max: 100)'
|
|
schema:
|
|
type: integer
|
|
- name: include_disabled
|
|
in: query
|
|
required: false
|
|
description: Include disabled accounts in the response. Defaults to false.
|
|
schema:
|
|
type: boolean
|
|
responses:
|
|
'200':
|
|
description: accounts paginated
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/AccountCollection"
|
|
"/api/v1/accounts/{id}":
|
|
parameters:
|
|
- name: id
|
|
in: path
|
|
required: true
|
|
description: Account ID
|
|
schema:
|
|
type: string
|
|
format: uuid
|
|
get:
|
|
summary: Retrieve an account
|
|
tags:
|
|
- Accounts
|
|
security:
|
|
- apiKeyAuth: []
|
|
parameters:
|
|
- name: include_disabled
|
|
in: query
|
|
required: false
|
|
description: Allow retrieving a disabled account. Defaults to false.
|
|
schema:
|
|
type: boolean
|
|
responses:
|
|
'200':
|
|
description: account retrieved
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/AccountDetail"
|
|
'401':
|
|
description: unauthorized
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'403':
|
|
description: insufficient scope
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'404':
|
|
description: account not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
"/api/v1/auth/signup":
|
|
post:
|
|
summary: Sign up a new user
|
|
tags:
|
|
- Auth
|
|
description: Creates a new user and family. The first user on a fresh instance
|
|
is assigned the super_admin role; later family creators are assigned an admin
|
|
role.
|
|
parameters: []
|
|
responses:
|
|
'201':
|
|
description: user created
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
access_token:
|
|
type: string
|
|
refresh_token:
|
|
type: string
|
|
token_type:
|
|
type: string
|
|
expires_in:
|
|
type: integer
|
|
created_at:
|
|
type: integer
|
|
user:
|
|
type: object
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
email:
|
|
type: string
|
|
first_name:
|
|
type: string
|
|
last_name:
|
|
type: string
|
|
ui_layout:
|
|
type: string
|
|
enum:
|
|
- dashboard
|
|
- intro
|
|
ai_enabled:
|
|
type: boolean
|
|
'422':
|
|
description: validation error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'403':
|
|
description: invite code required or invalid
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
requestBody:
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
user:
|
|
type: object
|
|
properties:
|
|
email:
|
|
type: string
|
|
format: email
|
|
description: User email address
|
|
password:
|
|
type: string
|
|
description: Password (min 8 chars, mixed case, number, special
|
|
char)
|
|
first_name:
|
|
type: string
|
|
last_name:
|
|
type: string
|
|
required:
|
|
- email
|
|
- password
|
|
device:
|
|
type: object
|
|
properties:
|
|
device_id:
|
|
type: string
|
|
description: Unique device identifier
|
|
device_name:
|
|
type: string
|
|
description: Human-readable device name
|
|
device_type:
|
|
type: string
|
|
description: Device type (e.g. ios, android)
|
|
os_version:
|
|
type: string
|
|
app_version:
|
|
type: string
|
|
required:
|
|
- device_id
|
|
- device_name
|
|
- device_type
|
|
- os_version
|
|
- app_version
|
|
invite_code:
|
|
type: string
|
|
nullable: true
|
|
description: Invite code (required when invites are enforced)
|
|
required:
|
|
- user
|
|
- device
|
|
required: true
|
|
"/api/v1/auth/login":
|
|
post:
|
|
summary: Log in with email and password
|
|
tags:
|
|
- Auth
|
|
parameters: []
|
|
responses:
|
|
'200':
|
|
description: login successful
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
access_token:
|
|
type: string
|
|
refresh_token:
|
|
type: string
|
|
token_type:
|
|
type: string
|
|
expires_in:
|
|
type: integer
|
|
created_at:
|
|
type: integer
|
|
user:
|
|
type: object
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
email:
|
|
type: string
|
|
first_name:
|
|
type: string
|
|
last_name:
|
|
type: string
|
|
ui_layout:
|
|
type: string
|
|
enum:
|
|
- dashboard
|
|
- intro
|
|
ai_enabled:
|
|
type: boolean
|
|
'401':
|
|
description: invalid credentials or MFA required
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
requestBody:
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
email:
|
|
type: string
|
|
format: email
|
|
password:
|
|
type: string
|
|
otp_code:
|
|
type: string
|
|
nullable: true
|
|
description: TOTP code if MFA is enabled
|
|
device:
|
|
type: object
|
|
properties:
|
|
device_id:
|
|
type: string
|
|
device_name:
|
|
type: string
|
|
device_type:
|
|
type: string
|
|
os_version:
|
|
type: string
|
|
app_version:
|
|
type: string
|
|
required:
|
|
- device_id
|
|
- device_name
|
|
- device_type
|
|
- os_version
|
|
- app_version
|
|
required:
|
|
- email
|
|
- password
|
|
- device
|
|
required: true
|
|
"/api/v1/auth/sso_exchange":
|
|
post:
|
|
summary: Exchange mobile SSO authorization code for tokens
|
|
tags:
|
|
- Auth
|
|
description: Exchanges a one-time authorization code (received via deep link
|
|
after mobile SSO) for OAuth tokens. The code is single-use and expires after
|
|
5 minutes.
|
|
parameters: []
|
|
responses:
|
|
'200':
|
|
description: tokens issued
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
access_token:
|
|
type: string
|
|
refresh_token:
|
|
type: string
|
|
token_type:
|
|
type: string
|
|
expires_in:
|
|
type: integer
|
|
created_at:
|
|
type: integer
|
|
user:
|
|
type: object
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
email:
|
|
type: string
|
|
first_name:
|
|
type: string
|
|
last_name:
|
|
type: string
|
|
ui_layout:
|
|
type: string
|
|
enum:
|
|
- dashboard
|
|
- intro
|
|
ai_enabled:
|
|
type: boolean
|
|
'401':
|
|
description: invalid or expired code
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
requestBody:
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
code:
|
|
type: string
|
|
description: One-time authorization code from mobile SSO callback
|
|
required:
|
|
- code
|
|
required: true
|
|
"/api/v1/auth/refresh":
|
|
post:
|
|
summary: Refresh an access token
|
|
tags:
|
|
- Auth
|
|
parameters: []
|
|
responses:
|
|
'200':
|
|
description: token refreshed
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
access_token:
|
|
type: string
|
|
refresh_token:
|
|
type: string
|
|
token_type:
|
|
type: string
|
|
expires_in:
|
|
type: integer
|
|
created_at:
|
|
type: integer
|
|
'401':
|
|
description: invalid refresh token
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'400':
|
|
description: missing refresh token
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
requestBody:
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
refresh_token:
|
|
type: string
|
|
description: The refresh token from a previous login or refresh
|
|
device:
|
|
type: object
|
|
properties:
|
|
device_id:
|
|
type: string
|
|
required:
|
|
- device_id
|
|
required:
|
|
- refresh_token
|
|
- device
|
|
required: true
|
|
"/api/v1/auth/sso_link":
|
|
post:
|
|
summary: Link an existing account via SSO
|
|
tags:
|
|
- Auth
|
|
description: Authenticates with email/password and links the SSO identity from
|
|
a previously issued linking code. Creates an OidcIdentity, logs the link via
|
|
SsoAuditLog, and issues mobile OAuth tokens.
|
|
parameters: []
|
|
responses:
|
|
'200':
|
|
description: account linked and tokens issued
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
access_token:
|
|
type: string
|
|
refresh_token:
|
|
type: string
|
|
token_type:
|
|
type: string
|
|
expires_in:
|
|
type: integer
|
|
created_at:
|
|
type: integer
|
|
user:
|
|
type: object
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
email:
|
|
type: string
|
|
first_name:
|
|
type: string
|
|
last_name:
|
|
type: string
|
|
ui_layout:
|
|
type: string
|
|
enum:
|
|
- dashboard
|
|
- intro
|
|
ai_enabled:
|
|
type: boolean
|
|
'400':
|
|
description: missing linking code
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'401':
|
|
description: invalid credentials or expired linking code
|
|
content:
|
|
application/json:
|
|
schema:
|
|
oneOf:
|
|
- "$ref": "#/components/schemas/ErrorResponse"
|
|
- "$ref": "#/components/schemas/MfaRequiredResponse"
|
|
'403':
|
|
description: SSO identity removed by an administrator
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
requestBody:
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
linking_code:
|
|
type: string
|
|
description: One-time linking code from mobile SSO onboarding redirect
|
|
email:
|
|
type: string
|
|
format: email
|
|
description: Email of the existing account to link
|
|
password:
|
|
type: string
|
|
description: Password for the existing account
|
|
required:
|
|
- linking_code
|
|
- email
|
|
- password
|
|
required: true
|
|
"/api/v1/auth/sso_create_account":
|
|
post:
|
|
summary: Create a new account via SSO
|
|
tags:
|
|
- Auth
|
|
description: Creates a new user and family from a previously issued linking
|
|
code. Links the SSO identity via OidcIdentity, logs the JIT account creation
|
|
via SsoAuditLog, and issues mobile OAuth tokens. The linking code must have
|
|
allow_account_creation enabled. The first user on a fresh instance is assigned
|
|
the super_admin role; later family creators are assigned an admin-capable
|
|
role.
|
|
parameters: []
|
|
responses:
|
|
'200':
|
|
description: account created and tokens issued
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
access_token:
|
|
type: string
|
|
refresh_token:
|
|
type: string
|
|
token_type:
|
|
type: string
|
|
expires_in:
|
|
type: integer
|
|
created_at:
|
|
type: integer
|
|
user:
|
|
type: object
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
email:
|
|
type: string
|
|
first_name:
|
|
type: string
|
|
last_name:
|
|
type: string
|
|
ui_layout:
|
|
type: string
|
|
enum:
|
|
- dashboard
|
|
- intro
|
|
ai_enabled:
|
|
type: boolean
|
|
'400':
|
|
description: missing linking code
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'401':
|
|
description: invalid or expired linking code
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'403':
|
|
description: SSO identity removed or account creation disabled
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'422':
|
|
description: user validation error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
errors:
|
|
type: array
|
|
items:
|
|
type: string
|
|
requestBody:
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
linking_code:
|
|
type: string
|
|
description: One-time linking code from mobile SSO onboarding redirect
|
|
first_name:
|
|
type: string
|
|
description: First name (overrides value from SSO provider if provided)
|
|
last_name:
|
|
type: string
|
|
description: Last name (overrides value from SSO provider if provided)
|
|
required:
|
|
- linking_code
|
|
required: true
|
|
"/api/v1/auth/enable_ai":
|
|
patch:
|
|
summary: Enable AI features for the authenticated user
|
|
tags:
|
|
- Auth
|
|
security:
|
|
- apiKeyAuth: []
|
|
responses:
|
|
'200':
|
|
description: ai enabled
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
user:
|
|
type: object
|
|
properties:
|
|
id:
|
|
type: string
|
|
format: uuid
|
|
email:
|
|
type: string
|
|
first_name:
|
|
type: string
|
|
nullable: true
|
|
last_name:
|
|
type: string
|
|
nullable: true
|
|
ui_layout:
|
|
type: string
|
|
enum:
|
|
- dashboard
|
|
- intro
|
|
ai_enabled:
|
|
type: boolean
|
|
'401':
|
|
description: unauthorized
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'403':
|
|
description: insufficient scope
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
"/api/v1/balance_sheet":
|
|
get:
|
|
summary: Show balance sheet
|
|
tags:
|
|
- Balance Sheet
|
|
description: Returns the family balance sheet including net worth, total assets,
|
|
and total liabilities with amounts converted to the family's primary currency.
|
|
security:
|
|
- apiKeyAuth: []
|
|
responses:
|
|
'200':
|
|
description: balance sheet returned
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/BalanceSheet"
|
|
'401':
|
|
description: unauthorized
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
"/api/v1/balances":
|
|
get:
|
|
summary: List balance history records
|
|
tags:
|
|
- Balances
|
|
security:
|
|
- apiKeyAuth: []
|
|
parameters:
|
|
- name: page
|
|
in: query
|
|
required: false
|
|
description: 'Page number (default: 1)'
|
|
schema:
|
|
type: integer
|
|
- name: per_page
|
|
in: query
|
|
required: false
|
|
description: 'Items per page (default: 25, max: 100)'
|
|
schema:
|
|
type: integer
|
|
- name: account_id
|
|
in: query
|
|
required: false
|
|
description: Filter by account ID
|
|
schema:
|
|
type: string
|
|
format: uuid
|
|
- name: currency
|
|
in: query
|
|
required: false
|
|
description: Filter by currency code
|
|
schema:
|
|
type: string
|
|
- name: start_date
|
|
in: query
|
|
required: false
|
|
description: Filter balances from this date
|
|
schema:
|
|
type: string
|
|
format: date
|
|
- name: end_date
|
|
in: query
|
|
required: false
|
|
description: Filter balances until this date
|
|
schema:
|
|
type: string
|
|
format: date
|
|
responses:
|
|
'200':
|
|
description: balances listed
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/BalanceCollection"
|
|
'401':
|
|
description: unauthorized
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'403':
|
|
description: insufficient scope
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'422':
|
|
description: invalid filter
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
"/api/v1/balances/{id}":
|
|
parameters:
|
|
- name: id
|
|
in: path
|
|
required: true
|
|
description: Balance ID
|
|
schema:
|
|
type: string
|
|
format: uuid
|
|
get:
|
|
summary: Retrieve a balance history record
|
|
tags:
|
|
- Balances
|
|
security:
|
|
- apiKeyAuth: []
|
|
responses:
|
|
'200':
|
|
description: balance retrieved
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/Balance"
|
|
'401':
|
|
description: unauthorized
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'403':
|
|
description: insufficient scope
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'404':
|
|
description: balance not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
"/api/v1/budget_categories":
|
|
get:
|
|
summary: List budget categories
|
|
tags:
|
|
- Budget Categories
|
|
security:
|
|
- apiKeyAuth: []
|
|
parameters:
|
|
- name: page
|
|
in: query
|
|
required: false
|
|
description: 'Page number (default: 1)'
|
|
schema:
|
|
type: integer
|
|
- name: per_page
|
|
in: query
|
|
required: false
|
|
description: 'Items per page (default: 25, max: 100)'
|
|
schema:
|
|
type: integer
|
|
- name: budget_id
|
|
in: query
|
|
required: false
|
|
schema:
|
|
type: string
|
|
format: uuid
|
|
description: Filter by budget ID
|
|
- name: category_id
|
|
in: query
|
|
required: false
|
|
schema:
|
|
type: string
|
|
format: uuid
|
|
description: Filter by category ID
|
|
- name: start_date
|
|
in: query
|
|
required: false
|
|
schema:
|
|
type: string
|
|
format: date
|
|
description: Filter budget categories whose budget starts on or after this
|
|
date
|
|
- name: end_date
|
|
in: query
|
|
required: false
|
|
schema:
|
|
type: string
|
|
format: date
|
|
description: Filter budget categories whose budget ends on or before this
|
|
date
|
|
responses:
|
|
'200':
|
|
description: budget categories listed
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/BudgetCategoryCollection"
|
|
'401':
|
|
description: unauthorized
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'403':
|
|
description: insufficient scope
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'422':
|
|
description: invalid filter
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
"/api/v1/budget_categories/{id}":
|
|
parameters:
|
|
- name: id
|
|
in: path
|
|
required: true
|
|
description: Budget category ID
|
|
schema:
|
|
type: string
|
|
format: uuid
|
|
get:
|
|
summary: Retrieve a budget category
|
|
tags:
|
|
- Budget Categories
|
|
security:
|
|
- apiKeyAuth: []
|
|
responses:
|
|
'200':
|
|
description: budget category retrieved
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/BudgetCategory"
|
|
'401':
|
|
description: unauthorized
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'403':
|
|
description: insufficient scope
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'404':
|
|
description: budget category not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
"/api/v1/budgets":
|
|
get:
|
|
summary: List budgets
|
|
tags:
|
|
- Budgets
|
|
security:
|
|
- apiKeyAuth: []
|
|
parameters:
|
|
- name: page
|
|
in: query
|
|
required: false
|
|
description: 'Page number (default: 1)'
|
|
schema:
|
|
type: integer
|
|
- name: per_page
|
|
in: query
|
|
required: false
|
|
description: 'Items per page (default: 25, max: 100)'
|
|
schema:
|
|
type: integer
|
|
- name: start_date
|
|
in: query
|
|
required: false
|
|
schema:
|
|
type: string
|
|
format: date
|
|
description: Filter budgets starting on or after this date
|
|
- name: end_date
|
|
in: query
|
|
required: false
|
|
schema:
|
|
type: string
|
|
format: date
|
|
description: Filter budgets ending on or before this date
|
|
responses:
|
|
'200':
|
|
description: budgets listed
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/BudgetCollection"
|
|
'401':
|
|
description: unauthorized
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'403':
|
|
description: insufficient scope
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'422':
|
|
description: invalid date filter
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
"/api/v1/budgets/{id}":
|
|
parameters:
|
|
- name: id
|
|
in: path
|
|
required: true
|
|
description: Budget ID
|
|
schema:
|
|
type: string
|
|
format: uuid
|
|
get:
|
|
summary: Retrieve a budget
|
|
tags:
|
|
- Budgets
|
|
security:
|
|
- apiKeyAuth: []
|
|
responses:
|
|
'200':
|
|
description: budget retrieved
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/Budget"
|
|
'401':
|
|
description: unauthorized
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'403':
|
|
description: insufficient scope
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'404':
|
|
description: budget not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
"/api/v1/categories":
|
|
get:
|
|
summary: List categories
|
|
tags:
|
|
- Categories
|
|
security:
|
|
- apiKeyAuth: []
|
|
parameters:
|
|
- name: page
|
|
in: query
|
|
required: false
|
|
description: 'Page number (default: 1)'
|
|
schema:
|
|
type: integer
|
|
- name: per_page
|
|
in: query
|
|
required: false
|
|
description: 'Items per page (default: 25, max: 100)'
|
|
schema:
|
|
type: integer
|
|
- name: roots_only
|
|
in: query
|
|
required: false
|
|
description: Return only root categories (no parent)
|
|
schema:
|
|
type: boolean
|
|
- name: parent_id
|
|
in: query
|
|
required: false
|
|
description: Filter by parent category ID
|
|
schema:
|
|
type: string
|
|
format: uuid
|
|
responses:
|
|
'200':
|
|
description: categories filtered by parent
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/CategoryCollection"
|
|
post:
|
|
summary: Create category
|
|
tags:
|
|
- Categories
|
|
security:
|
|
- apiKeyAuth: []
|
|
parameters: []
|
|
responses:
|
|
'201':
|
|
description: subcategory created with parent
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/CategoryDetail"
|
|
'422':
|
|
description: validation error - duplicate name
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'403':
|
|
description: forbidden - api key missing read_write scope
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'400':
|
|
description: bad request - missing category payload
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'401':
|
|
description: unauthorized - missing api key
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
requestBody:
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/CategoryCreateRequest"
|
|
required: true
|
|
"/api/v1/categories/{id}":
|
|
parameters:
|
|
- name: id
|
|
in: path
|
|
required: true
|
|
description: Category ID
|
|
schema:
|
|
type: string
|
|
get:
|
|
summary: Retrieve a category
|
|
tags:
|
|
- Categories
|
|
security:
|
|
- apiKeyAuth: []
|
|
responses:
|
|
'200':
|
|
description: subcategory retrieved with parent
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/CategoryDetail"
|
|
'404':
|
|
description: category not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
"/api/v1/chats":
|
|
get:
|
|
summary: List chats
|
|
tags:
|
|
- Chats
|
|
security:
|
|
- apiKeyAuth: []
|
|
responses:
|
|
'200':
|
|
description: chats listed
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ChatCollection"
|
|
'403':
|
|
description: AI features disabled
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
post:
|
|
summary: Create chat
|
|
tags:
|
|
- Chats
|
|
security:
|
|
- apiKeyAuth: []
|
|
parameters: []
|
|
responses:
|
|
'201':
|
|
description: chat created
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ChatDetail"
|
|
'422':
|
|
description: validation error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
requestBody:
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
title:
|
|
type: string
|
|
example: Monthly budget review
|
|
message:
|
|
type: string
|
|
description: Optional initial message in the chat
|
|
model:
|
|
type: string
|
|
description: Optional OpenAI model identifier
|
|
required:
|
|
- title
|
|
required: true
|
|
"/api/v1/chats/{id}":
|
|
parameters:
|
|
- name: id
|
|
in: path
|
|
required: true
|
|
description: Chat ID
|
|
schema:
|
|
type: string
|
|
get:
|
|
summary: Retrieve a chat
|
|
tags:
|
|
- Chats
|
|
security:
|
|
- apiKeyAuth: []
|
|
responses:
|
|
'200':
|
|
description: chat retrieved
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ChatDetail"
|
|
'404':
|
|
description: chat not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
patch:
|
|
summary: Update a chat
|
|
tags:
|
|
- Chats
|
|
security:
|
|
- apiKeyAuth: []
|
|
parameters: []
|
|
responses:
|
|
'200':
|
|
description: chat updated
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ChatDetail"
|
|
'404':
|
|
description: chat not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'422':
|
|
description: validation error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
requestBody:
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
title:
|
|
type: string
|
|
example: Updated chat title
|
|
required: true
|
|
delete:
|
|
summary: Delete a chat
|
|
tags:
|
|
- Chats
|
|
security:
|
|
- apiKeyAuth: []
|
|
responses:
|
|
'204':
|
|
description: chat deleted
|
|
'404':
|
|
description: chat not found
|
|
"/api/v1/chats/{chat_id}/messages":
|
|
parameters:
|
|
- name: chat_id
|
|
in: path
|
|
required: true
|
|
description: Chat ID
|
|
schema:
|
|
type: string
|
|
post:
|
|
summary: Create a message
|
|
tags:
|
|
- Chat Messages
|
|
security:
|
|
- apiKeyAuth: []
|
|
parameters: []
|
|
responses:
|
|
'201':
|
|
description: message created
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/MessageResponse"
|
|
'404':
|
|
description: chat not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'422':
|
|
description: validation error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
requestBody:
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
content:
|
|
type: string
|
|
model:
|
|
type: string
|
|
required:
|
|
- content
|
|
required: true
|
|
"/api/v1/chats/{chat_id}/messages/retry":
|
|
parameters:
|
|
- name: chat_id
|
|
in: path
|
|
required: true
|
|
description: Chat ID
|
|
schema:
|
|
type: string
|
|
post:
|
|
summary: Retry the last assistant response
|
|
tags:
|
|
- Chat Messages
|
|
security:
|
|
- apiKeyAuth: []
|
|
responses:
|
|
'202':
|
|
description: retry started
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/RetryResponse"
|
|
'404':
|
|
description: chat not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'422':
|
|
description: no assistant message available
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
"/api/v1/family_exports":
|
|
get:
|
|
summary: Lists family exports
|
|
tags:
|
|
- Family Exports
|
|
security:
|
|
- apiKeyAuth: []
|
|
parameters:
|
|
- name: page
|
|
in: query
|
|
required: false
|
|
description: 'Page number (default: 1)'
|
|
schema:
|
|
type: integer
|
|
- name: per_page
|
|
in: query
|
|
required: false
|
|
description: 'Items per page (default: 25, max: 100)'
|
|
schema:
|
|
type: integer
|
|
responses:
|
|
'200':
|
|
description: family exports listed
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/FamilyExportCollection"
|
|
'401':
|
|
description: unauthorized
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'403':
|
|
description: forbidden
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
post:
|
|
summary: Queues a family export
|
|
tags:
|
|
- Family Exports
|
|
security:
|
|
- apiKeyAuth: []
|
|
parameters: []
|
|
responses:
|
|
'202':
|
|
description: family export queued
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/FamilyExportResponse"
|
|
'401':
|
|
description: unauthorized
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'403':
|
|
description: forbidden
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'422':
|
|
description: invalid params
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
requestBody:
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
additionalProperties: false
|
|
description: Family export creation does not accept request parameters.
|
|
"/api/v1/family_exports/{id}":
|
|
parameters:
|
|
- name: id
|
|
in: path
|
|
format: uuid
|
|
required: true
|
|
schema:
|
|
type: string
|
|
get:
|
|
summary: Shows a family export
|
|
tags:
|
|
- Family Exports
|
|
security:
|
|
- apiKeyAuth: []
|
|
responses:
|
|
'200':
|
|
description: family export shown
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/FamilyExportResponse"
|
|
'401':
|
|
description: unauthorized
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'403':
|
|
description: forbidden
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'404':
|
|
description: not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
"/api/v1/family_exports/{id}/download":
|
|
parameters:
|
|
- name: id
|
|
in: path
|
|
format: uuid
|
|
required: true
|
|
schema:
|
|
type: string
|
|
get:
|
|
summary: Downloads a completed family export
|
|
tags:
|
|
- Family Exports
|
|
security:
|
|
- apiKeyAuth: []
|
|
responses:
|
|
'302':
|
|
description: family export download redirected
|
|
'401':
|
|
description: unauthorized
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'403':
|
|
description: forbidden
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'404':
|
|
description: not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'409':
|
|
description: export not ready
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
"/api/v1/family_settings":
|
|
get:
|
|
summary: Retrieve family settings
|
|
description: Retrieve a read-only snapshot of non-secret family configuration.
|
|
tags:
|
|
- Family Settings
|
|
security:
|
|
- apiKeyAuth: []
|
|
responses:
|
|
'200':
|
|
description: family settings retrieved
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/FamilySettings"
|
|
'401':
|
|
description: unauthorized
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'403':
|
|
description: insufficient scope
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
"/api/v1/holdings":
|
|
get:
|
|
summary: List holdings
|
|
tags:
|
|
- Holdings
|
|
security:
|
|
- apiKeyAuth: []
|
|
parameters:
|
|
- name: page
|
|
in: query
|
|
required: false
|
|
description: 'Page number (default: 1)'
|
|
schema:
|
|
type: integer
|
|
- name: per_page
|
|
in: query
|
|
required: false
|
|
description: 'Items per page (default: 25, max: 100)'
|
|
schema:
|
|
type: integer
|
|
- name: account_id
|
|
in: query
|
|
required: false
|
|
description: Filter by account ID
|
|
schema:
|
|
type: string
|
|
- name: account_ids
|
|
in: query
|
|
required: false
|
|
description: Filter by multiple account IDs
|
|
schema:
|
|
type: array
|
|
items:
|
|
type: string
|
|
- name: date
|
|
in: query
|
|
required: false
|
|
description: Filter by exact date
|
|
schema:
|
|
type: string
|
|
format: date
|
|
- name: start_date
|
|
in: query
|
|
required: false
|
|
description: Filter holdings from this date (inclusive)
|
|
schema:
|
|
type: string
|
|
format: date
|
|
- name: end_date
|
|
in: query
|
|
required: false
|
|
description: Filter holdings until this date (inclusive)
|
|
schema:
|
|
type: string
|
|
format: date
|
|
- name: security_id
|
|
in: query
|
|
required: false
|
|
description: Filter by security ID
|
|
schema:
|
|
type: string
|
|
responses:
|
|
'200':
|
|
description: holdings paginated
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/HoldingCollection"
|
|
'401':
|
|
description: unauthorized
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'422':
|
|
description: invalid date filter
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
"/api/v1/holdings/{id}":
|
|
parameters:
|
|
- name: id
|
|
in: path
|
|
required: true
|
|
description: Holding ID
|
|
schema:
|
|
type: string
|
|
get:
|
|
summary: Retrieve holding
|
|
tags:
|
|
- Holdings
|
|
security:
|
|
- apiKeyAuth: []
|
|
responses:
|
|
'200':
|
|
description: holding retrieved
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/Holding"
|
|
'401':
|
|
description: unauthorized
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'404':
|
|
description: holding not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
"/api/v1/import_sessions":
|
|
post:
|
|
summary: Create import session
|
|
description: Create or idempotently retrieve a multi-file SureImport session
|
|
keyed by client_session_id.
|
|
tags:
|
|
- Import Sessions
|
|
security:
|
|
- apiKeyAuth: []
|
|
parameters: []
|
|
responses:
|
|
'201':
|
|
description: import session created
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ImportSessionResponse"
|
|
'401':
|
|
description: unauthorized
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'403':
|
|
description: insufficient scope
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'409':
|
|
description: client session conflict
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'422':
|
|
description: validation error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
requestBody:
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
type:
|
|
type: string
|
|
enum:
|
|
- SureImport
|
|
description: Import session type. Only SureImport is supported.
|
|
client_session_id:
|
|
type: string
|
|
nullable: true
|
|
description: Client-provided idempotency key for the full import
|
|
session.
|
|
expected_chunks:
|
|
type: integer
|
|
minimum: 1
|
|
nullable: true
|
|
description: Expected number of ordered chunks before publish is
|
|
allowed.
|
|
"/api/v1/import_sessions/{id}":
|
|
parameters:
|
|
- name: id
|
|
in: path
|
|
required: true
|
|
description: Import session ID
|
|
schema:
|
|
type: string
|
|
get:
|
|
summary: Retrieve import session
|
|
description: Retrieve import session status, chunk status, per-entity summary
|
|
counts, and safe error details.
|
|
tags:
|
|
- Import Sessions
|
|
security:
|
|
- apiKeyAuth: []
|
|
responses:
|
|
'200':
|
|
description: import session retrieved
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ImportSessionResponse"
|
|
'401':
|
|
description: unauthorized
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'403':
|
|
description: insufficient scope
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'404':
|
|
description: import session not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
"/api/v1/import_sessions/{id}/chunks":
|
|
parameters:
|
|
- name: id
|
|
in: path
|
|
required: true
|
|
description: Import session ID
|
|
schema:
|
|
type: string
|
|
post:
|
|
summary: Upload import session chunk
|
|
description: Attach an ordered Sure NDJSON chunk to an import session. Chunks
|
|
are idempotent by sequence and client_chunk_id with content verification.
|
|
tags:
|
|
- Import Sessions
|
|
security:
|
|
- apiKeyAuth: []
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required:
|
|
- sequence
|
|
- raw_file_content
|
|
properties:
|
|
sequence:
|
|
type: integer
|
|
minimum: 1
|
|
description: One-based chunk sequence. Earlier dependency chunks
|
|
must have lower sequence numbers.
|
|
client_chunk_id:
|
|
type: string
|
|
nullable: true
|
|
description: Client-provided idempotency key for this chunk.
|
|
raw_file_content:
|
|
type: string
|
|
description: Raw Sure NDJSON content. Each chunk is limited to 10MB.
|
|
multipart/form-data:
|
|
schema:
|
|
type: object
|
|
required:
|
|
- sequence
|
|
- file
|
|
properties:
|
|
sequence:
|
|
type: integer
|
|
minimum: 1
|
|
description: One-based chunk sequence. Earlier dependency chunks
|
|
must have lower sequence numbers.
|
|
client_chunk_id:
|
|
type: string
|
|
nullable: true
|
|
description: Client-provided idempotency key for this chunk.
|
|
file:
|
|
type: string
|
|
format: binary
|
|
description: Multipart Sure NDJSON file upload. Each chunk is limited
|
|
to 10MB.
|
|
parameters: []
|
|
responses:
|
|
'201':
|
|
description: chunk uploaded
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ImportSessionResponse"
|
|
'401':
|
|
description: unauthorized
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'403':
|
|
description: insufficient scope
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'409':
|
|
description: chunk conflict
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'404':
|
|
description: import session not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'422':
|
|
description: missing or invalid content
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
"/api/v1/import_sessions/{id}/publish":
|
|
parameters:
|
|
- name: id
|
|
in: path
|
|
required: true
|
|
description: Import session ID
|
|
schema:
|
|
type: string
|
|
post:
|
|
summary: Publish import session
|
|
description: Queue ordered chunk processing for a SureImport session. Later
|
|
chunks can reference source IDs mapped by earlier chunks.
|
|
tags:
|
|
- Import Sessions
|
|
security:
|
|
- apiKeyAuth: []
|
|
responses:
|
|
'202':
|
|
description: import session publish queued
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ImportSessionResponse"
|
|
'401':
|
|
description: unauthorized
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'403':
|
|
description: insufficient scope
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'422':
|
|
description: max_row_count_exceeded
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'409':
|
|
description: missing expected chunks
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'503':
|
|
description: enqueue failed
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'404':
|
|
description: import session not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
"/api/v1/imports":
|
|
get:
|
|
summary: List imports
|
|
description: List all imports for the user's family with pagination and filtering.
|
|
tags:
|
|
- Imports
|
|
security:
|
|
- apiKeyAuth: []
|
|
parameters:
|
|
- name: page
|
|
in: query
|
|
required: false
|
|
description: 'Page number (default: 1)'
|
|
schema:
|
|
type: integer
|
|
- name: per_page
|
|
in: query
|
|
required: false
|
|
description: 'Items per page (default: 25, max: 100)'
|
|
schema:
|
|
type: integer
|
|
- name: status
|
|
in: query
|
|
required: false
|
|
description: Filter by status
|
|
schema:
|
|
type: string
|
|
enum:
|
|
- pending
|
|
- complete
|
|
- importing
|
|
- reverting
|
|
- revert_failed
|
|
- failed
|
|
- name: type
|
|
in: query
|
|
required: false
|
|
description: Filter by import type
|
|
schema:
|
|
type: string
|
|
enum:
|
|
- TransactionImport
|
|
- TradeImport
|
|
- AccountImport
|
|
- MintImport
|
|
- ActualImport
|
|
- YnabImport
|
|
- CategoryImport
|
|
- RuleImport
|
|
- MerchantImport
|
|
- PdfImport
|
|
- QifImport
|
|
- SureImport
|
|
responses:
|
|
'200':
|
|
description: imports filtered by type
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ImportCollection"
|
|
post:
|
|
summary: Create import
|
|
description: Create a new import from raw CSV content, inline Sure NDJSON content,
|
|
or an uploaded Sure NDJSON file. CSV content is limited to 10MB.
|
|
tags:
|
|
- Imports
|
|
security:
|
|
- apiKeyAuth: []
|
|
parameters: []
|
|
responses:
|
|
'201':
|
|
description: import created
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ImportResponse"
|
|
'422':
|
|
description: validation error or publish rejection
|
|
content:
|
|
application/json:
|
|
schema:
|
|
oneOf:
|
|
- "$ref": "#/components/schemas/ErrorResponse"
|
|
- "$ref": "#/components/schemas/ErrorResponseWithImportId"
|
|
'500':
|
|
description: import uploaded but publish enqueue failed
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponseWithImportId"
|
|
requestBody:
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
raw_file_content:
|
|
type: string
|
|
description: Raw CSV or Sure NDJSON content as a string. CSV content
|
|
is limited to 10MB. Required for SureImport unless a multipart
|
|
file is uploaded.
|
|
type:
|
|
type: string
|
|
enum:
|
|
- TransactionImport
|
|
- TradeImport
|
|
- AccountImport
|
|
- MintImport
|
|
- ActualImport
|
|
- YnabImport
|
|
- CategoryImport
|
|
- RuleImport
|
|
- MerchantImport
|
|
- PdfImport
|
|
- QifImport
|
|
- SureImport
|
|
description: Import type (defaults to TransactionImport)
|
|
account_id:
|
|
type: string
|
|
format: uuid
|
|
description: Account ID to import into
|
|
publish:
|
|
type: string
|
|
description: Set to "true" to automatically queue for processing
|
|
if configuration is valid
|
|
date_col_label:
|
|
type: string
|
|
description: CSV imports only. Header name for the date column
|
|
amount_col_label:
|
|
type: string
|
|
description: CSV imports only. Header name for the amount column
|
|
name_col_label:
|
|
type: string
|
|
description: CSV imports only. Header name for the transaction name
|
|
column
|
|
category_col_label:
|
|
type: string
|
|
description: CSV imports only. Header name for the category column
|
|
tags_col_label:
|
|
type: string
|
|
description: CSV imports only. Header name for the tags column
|
|
notes_col_label:
|
|
type: string
|
|
description: CSV imports only. Header name for the notes column
|
|
account_col_label:
|
|
type: string
|
|
description: CSV imports only. Header name for the account column
|
|
when importing rows across multiple accounts
|
|
qty_col_label:
|
|
type: string
|
|
description: CSV trade imports only. Header name for the quantity
|
|
column
|
|
ticker_col_label:
|
|
type: string
|
|
description: CSV trade imports only. Header name for the ticker
|
|
column
|
|
price_col_label:
|
|
type: string
|
|
description: CSV trade imports only. Header name for the price column
|
|
entity_type_col_label:
|
|
type: string
|
|
description: CSV imports only. Header name for the entity type column
|
|
currency_col_label:
|
|
type: string
|
|
description: CSV imports only. Header name for the currency column
|
|
exchange_operating_mic_col_label:
|
|
type: string
|
|
description: CSV trade imports only. Header name for the exchange
|
|
operating MIC column
|
|
date_format:
|
|
type: string
|
|
description: CSV imports only. Date format pattern (e.g., "%m/%d/%Y")
|
|
number_format:
|
|
type: string
|
|
enum:
|
|
- '1,234.56'
|
|
- 1.234,56
|
|
- 1 234,56
|
|
- '1,234'
|
|
description: CSV imports only. Number format for parsing amounts
|
|
signage_convention:
|
|
type: string
|
|
enum:
|
|
- inflows_positive
|
|
- inflows_negative
|
|
description: CSV imports only. How to interpret positive/negative
|
|
amounts
|
|
col_sep:
|
|
type: string
|
|
enum:
|
|
- ","
|
|
- ";"
|
|
description: CSV imports only. Column separator
|
|
amount_type_strategy:
|
|
type: string
|
|
enum:
|
|
- signed_amount
|
|
- custom_column
|
|
description: CSV imports only. Amount parsing strategy
|
|
amount_type_inflow_value:
|
|
type: string
|
|
description: CSV imports only. Column value that marks an amount
|
|
as an inflow when using custom_column strategy
|
|
multipart/form-data:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
raw_file_content:
|
|
type: string
|
|
description: Raw CSV or Sure NDJSON content as a string. CSV content
|
|
is limited to 10MB. Required for SureImport unless a multipart
|
|
file is uploaded.
|
|
type:
|
|
type: string
|
|
enum:
|
|
- TransactionImport
|
|
- TradeImport
|
|
- AccountImport
|
|
- MintImport
|
|
- ActualImport
|
|
- YnabImport
|
|
- CategoryImport
|
|
- RuleImport
|
|
- MerchantImport
|
|
- PdfImport
|
|
- QifImport
|
|
- SureImport
|
|
description: Import type (defaults to TransactionImport)
|
|
account_id:
|
|
type: string
|
|
format: uuid
|
|
description: Account ID to import into
|
|
publish:
|
|
type: string
|
|
description: Set to "true" to automatically queue for processing
|
|
if configuration is valid
|
|
date_col_label:
|
|
type: string
|
|
description: CSV imports only. Header name for the date column
|
|
amount_col_label:
|
|
type: string
|
|
description: CSV imports only. Header name for the amount column
|
|
name_col_label:
|
|
type: string
|
|
description: CSV imports only. Header name for the transaction name
|
|
column
|
|
category_col_label:
|
|
type: string
|
|
description: CSV imports only. Header name for the category column
|
|
tags_col_label:
|
|
type: string
|
|
description: CSV imports only. Header name for the tags column
|
|
notes_col_label:
|
|
type: string
|
|
description: CSV imports only. Header name for the notes column
|
|
account_col_label:
|
|
type: string
|
|
description: CSV imports only. Header name for the account column
|
|
when importing rows across multiple accounts
|
|
qty_col_label:
|
|
type: string
|
|
description: CSV trade imports only. Header name for the quantity
|
|
column
|
|
ticker_col_label:
|
|
type: string
|
|
description: CSV trade imports only. Header name for the ticker
|
|
column
|
|
price_col_label:
|
|
type: string
|
|
description: CSV trade imports only. Header name for the price column
|
|
entity_type_col_label:
|
|
type: string
|
|
description: CSV imports only. Header name for the entity type column
|
|
currency_col_label:
|
|
type: string
|
|
description: CSV imports only. Header name for the currency column
|
|
exchange_operating_mic_col_label:
|
|
type: string
|
|
description: CSV trade imports only. Header name for the exchange
|
|
operating MIC column
|
|
date_format:
|
|
type: string
|
|
description: CSV imports only. Date format pattern (e.g., "%m/%d/%Y")
|
|
number_format:
|
|
type: string
|
|
enum:
|
|
- '1,234.56'
|
|
- 1.234,56
|
|
- 1 234,56
|
|
- '1,234'
|
|
description: CSV imports only. Number format for parsing amounts
|
|
signage_convention:
|
|
type: string
|
|
enum:
|
|
- inflows_positive
|
|
- inflows_negative
|
|
description: CSV imports only. How to interpret positive/negative
|
|
amounts
|
|
col_sep:
|
|
type: string
|
|
enum:
|
|
- ","
|
|
- ";"
|
|
description: CSV imports only. Column separator
|
|
amount_type_strategy:
|
|
type: string
|
|
enum:
|
|
- signed_amount
|
|
- custom_column
|
|
description: CSV imports only. Amount parsing strategy
|
|
amount_type_inflow_value:
|
|
type: string
|
|
description: CSV imports only. Column value that marks an amount
|
|
as an inflow when using custom_column strategy
|
|
"/api/v1/imports/{id}":
|
|
parameters:
|
|
- name: id
|
|
in: path
|
|
required: true
|
|
description: Import ID
|
|
schema:
|
|
type: string
|
|
get:
|
|
summary: Retrieve an import
|
|
description: Retrieve detailed information about a specific import, including
|
|
configuration, row statistics, and SureImport readback verification when available.
|
|
tags:
|
|
- Imports
|
|
security:
|
|
- apiKeyAuth: []
|
|
responses:
|
|
'200':
|
|
description: import retrieved
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ImportResponse"
|
|
'404':
|
|
description: import not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
"/api/v1/imports/{id}/rows":
|
|
parameters:
|
|
- name: id
|
|
in: path
|
|
required: true
|
|
description: Import ID
|
|
schema:
|
|
type: string
|
|
get:
|
|
summary: List import row diagnostics
|
|
description: List sanitized import rows with validation errors and mapping resolution
|
|
state.
|
|
tags:
|
|
- Imports
|
|
security:
|
|
- apiKeyAuth: []
|
|
parameters:
|
|
- name: page
|
|
in: query
|
|
required: false
|
|
description: 'Page number (default: 1)'
|
|
schema:
|
|
type: integer
|
|
- name: per_page
|
|
in: query
|
|
required: false
|
|
description: 'Items per page (default: 25, max: 100)'
|
|
schema:
|
|
type: integer
|
|
responses:
|
|
'200':
|
|
description: import rows listed
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ImportRowDiagnosticCollection"
|
|
'401':
|
|
description: unauthorized
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'403':
|
|
description: insufficient scope
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'404':
|
|
description: import not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'500':
|
|
description: internal server error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
"/api/v1/imports/preflight":
|
|
post:
|
|
summary: Validate import content without creating an import
|
|
description: Validate CSV or Sure NDJSON import content and return counts, headers,
|
|
warnings, and validation errors without persisting an import or enqueueing
|
|
jobs. CSV content is limited to 10MB.
|
|
tags:
|
|
- Imports
|
|
security:
|
|
- apiKeyAuth: []
|
|
parameters: []
|
|
responses:
|
|
'200':
|
|
description: import content preflighted
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ImportPreflightResponse"
|
|
'401':
|
|
description: unauthorized
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'422':
|
|
description: missing or invalid content
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'404':
|
|
description: account not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
requestBody:
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
raw_file_content:
|
|
type: string
|
|
description: Raw CSV or Sure NDJSON content as a string. CSV content
|
|
is limited to 10MB.
|
|
file:
|
|
type: string
|
|
format: binary
|
|
description: CSV or Sure NDJSON upload when using multipart/form-data.
|
|
CSV files are limited to 10MB.
|
|
type:
|
|
type: string
|
|
enum:
|
|
- TransactionImport
|
|
- TradeImport
|
|
- AccountImport
|
|
- MintImport
|
|
- ActualImport
|
|
- YnabImport
|
|
- CategoryImport
|
|
- RuleImport
|
|
- MerchantImport
|
|
- PdfImport
|
|
- QifImport
|
|
- SureImport
|
|
description: Import type to validate (defaults to TransactionImport)
|
|
account_id:
|
|
type: string
|
|
format: uuid
|
|
description: Account ID used for account-scoped CSV import validation
|
|
date_col_label:
|
|
type: string
|
|
description: CSV imports only. Header name for the date column
|
|
amount_col_label:
|
|
type: string
|
|
description: CSV imports only. Header name for the amount column
|
|
name_col_label:
|
|
type: string
|
|
description: CSV imports only. Header name for the transaction name
|
|
column
|
|
category_col_label:
|
|
type: string
|
|
description: CSV imports only. Header name for the category column
|
|
tags_col_label:
|
|
type: string
|
|
description: CSV imports only. Header name for the tags column
|
|
notes_col_label:
|
|
type: string
|
|
description: CSV imports only. Header name for the notes column
|
|
account_col_label:
|
|
type: string
|
|
description: CSV imports only. Header name for the account column
|
|
qty_col_label:
|
|
type: string
|
|
description: CSV trade imports only. Header name for the quantity
|
|
column
|
|
ticker_col_label:
|
|
type: string
|
|
description: CSV trade imports only. Header name for the ticker
|
|
column
|
|
price_col_label:
|
|
type: string
|
|
description: CSV trade imports only. Header name for the price column
|
|
entity_type_col_label:
|
|
type: string
|
|
description: CSV imports only. Header name for the entity type column
|
|
currency_col_label:
|
|
type: string
|
|
description: CSV imports only. Header name for the currency column
|
|
exchange_operating_mic_col_label:
|
|
type: string
|
|
description: CSV trade imports only. Header name for the exchange
|
|
operating MIC column
|
|
date_format:
|
|
type: string
|
|
description: CSV imports only. Date format pattern
|
|
number_format:
|
|
type: string
|
|
enum:
|
|
- '1,234.56'
|
|
- 1.234,56
|
|
- 1 234,56
|
|
- '1,234'
|
|
description: CSV imports only. Number format for parsing amounts
|
|
signage_convention:
|
|
type: string
|
|
enum:
|
|
- inflows_positive
|
|
- inflows_negative
|
|
description: CSV imports only. How to interpret positive/negative
|
|
amounts
|
|
col_sep:
|
|
type: string
|
|
enum:
|
|
- ","
|
|
- ";"
|
|
description: CSV imports only. Column separator
|
|
rows_to_skip:
|
|
type: integer
|
|
minimum: 0
|
|
description: CSV imports only. Number of leading rows to skip before
|
|
reading headers
|
|
amount_type_strategy:
|
|
type: string
|
|
enum:
|
|
- signed_amount
|
|
- custom_column
|
|
description: CSV imports only. Amount parsing strategy
|
|
amount_type_inflow_value:
|
|
type: string
|
|
description: CSV imports only. Column value that marks an amount
|
|
as an inflow when using custom_column strategy
|
|
multipart/form-data:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
raw_file_content:
|
|
type: string
|
|
description: Raw CSV or Sure NDJSON content as a string. CSV content
|
|
is limited to 10MB.
|
|
file:
|
|
type: string
|
|
format: binary
|
|
description: CSV or Sure NDJSON upload when using multipart/form-data.
|
|
CSV files are limited to 10MB.
|
|
type:
|
|
type: string
|
|
enum:
|
|
- TransactionImport
|
|
- TradeImport
|
|
- AccountImport
|
|
- MintImport
|
|
- ActualImport
|
|
- YnabImport
|
|
- CategoryImport
|
|
- RuleImport
|
|
- MerchantImport
|
|
- PdfImport
|
|
- QifImport
|
|
- SureImport
|
|
description: Import type to validate (defaults to TransactionImport)
|
|
account_id:
|
|
type: string
|
|
format: uuid
|
|
description: Account ID used for account-scoped CSV import validation
|
|
date_col_label:
|
|
type: string
|
|
description: CSV imports only. Header name for the date column
|
|
amount_col_label:
|
|
type: string
|
|
description: CSV imports only. Header name for the amount column
|
|
name_col_label:
|
|
type: string
|
|
description: CSV imports only. Header name for the transaction name
|
|
column
|
|
category_col_label:
|
|
type: string
|
|
description: CSV imports only. Header name for the category column
|
|
tags_col_label:
|
|
type: string
|
|
description: CSV imports only. Header name for the tags column
|
|
notes_col_label:
|
|
type: string
|
|
description: CSV imports only. Header name for the notes column
|
|
account_col_label:
|
|
type: string
|
|
description: CSV imports only. Header name for the account column
|
|
qty_col_label:
|
|
type: string
|
|
description: CSV trade imports only. Header name for the quantity
|
|
column
|
|
ticker_col_label:
|
|
type: string
|
|
description: CSV trade imports only. Header name for the ticker
|
|
column
|
|
price_col_label:
|
|
type: string
|
|
description: CSV trade imports only. Header name for the price column
|
|
entity_type_col_label:
|
|
type: string
|
|
description: CSV imports only. Header name for the entity type column
|
|
currency_col_label:
|
|
type: string
|
|
description: CSV imports only. Header name for the currency column
|
|
exchange_operating_mic_col_label:
|
|
type: string
|
|
description: CSV trade imports only. Header name for the exchange
|
|
operating MIC column
|
|
date_format:
|
|
type: string
|
|
description: CSV imports only. Date format pattern
|
|
number_format:
|
|
type: string
|
|
enum:
|
|
- '1,234.56'
|
|
- 1.234,56
|
|
- 1 234,56
|
|
- '1,234'
|
|
description: CSV imports only. Number format for parsing amounts
|
|
signage_convention:
|
|
type: string
|
|
enum:
|
|
- inflows_positive
|
|
- inflows_negative
|
|
description: CSV imports only. How to interpret positive/negative
|
|
amounts
|
|
col_sep:
|
|
type: string
|
|
enum:
|
|
- ","
|
|
- ";"
|
|
description: CSV imports only. Column separator
|
|
rows_to_skip:
|
|
type: integer
|
|
minimum: 0
|
|
description: CSV imports only. Number of leading rows to skip before
|
|
reading headers
|
|
amount_type_strategy:
|
|
type: string
|
|
enum:
|
|
- signed_amount
|
|
- custom_column
|
|
description: CSV imports only. Amount parsing strategy
|
|
amount_type_inflow_value:
|
|
type: string
|
|
description: CSV imports only. Column value that marks an amount
|
|
as an inflow when using custom_column strategy
|
|
"/api/v1/insights":
|
|
get:
|
|
summary: List proactive insights
|
|
tags:
|
|
- Insights
|
|
security:
|
|
- apiKeyAuth: []
|
|
responses:
|
|
'200':
|
|
description: insights listed
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/InsightCollection"
|
|
'403':
|
|
description: preview features disabled
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
"/api/v1/merchants":
|
|
get:
|
|
summary: List merchants
|
|
tags:
|
|
- Merchants
|
|
security:
|
|
- apiKeyAuth: []
|
|
responses:
|
|
'200':
|
|
description: merchants listed
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: array
|
|
items:
|
|
"$ref": "#/components/schemas/MerchantDetail"
|
|
post:
|
|
summary: Import merchants from CSV
|
|
tags:
|
|
- Merchants
|
|
security:
|
|
- apiKeyAuth: []
|
|
parameters: []
|
|
responses:
|
|
'201':
|
|
description: merchants imported
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/MerchantImportResult"
|
|
'401':
|
|
description: unauthorized
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'422':
|
|
description: missing file or invalid CSV
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
requestBody:
|
|
content:
|
|
multipart/form-data:
|
|
schema:
|
|
type: object
|
|
required:
|
|
- file
|
|
properties:
|
|
file:
|
|
type: string
|
|
format: binary
|
|
required: true
|
|
description: 'CSV file with columns: name* (required), color, website_url'
|
|
"/api/v1/merchants/{id}":
|
|
parameters:
|
|
- name: id
|
|
in: path
|
|
required: true
|
|
description: Merchant ID
|
|
schema:
|
|
type: string
|
|
get:
|
|
summary: Retrieve a merchant
|
|
tags:
|
|
- Merchants
|
|
security:
|
|
- apiKeyAuth: []
|
|
responses:
|
|
'200':
|
|
description: merchant retrieved
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/MerchantDetail"
|
|
'404':
|
|
description: merchant not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
"/api/v1/provider_connections":
|
|
get:
|
|
summary: Lists provider connection status summaries
|
|
description: List safe provider connection status metadata for the authenticated
|
|
user's family without exposing credentials, raw provider payloads, or raw
|
|
sync errors.
|
|
tags:
|
|
- Provider Connections
|
|
security:
|
|
- apiKeyAuth: []
|
|
responses:
|
|
'200':
|
|
description: provider connection status summaries listed
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ProviderConnectionCollection"
|
|
'401':
|
|
description: unauthorized
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'403':
|
|
description: insufficient scope
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
"/api/v1/push_subscriptions":
|
|
post:
|
|
summary: Register an APNs device token
|
|
tags:
|
|
- Push Subscriptions
|
|
security:
|
|
- apiKeyAuth: []
|
|
parameters: []
|
|
responses:
|
|
'201':
|
|
description: token registered
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/PushSubscription"
|
|
'422':
|
|
description: invalid or conflicting subscription
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
requestBody:
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required:
|
|
- token
|
|
- environment
|
|
- platform
|
|
properties:
|
|
token:
|
|
type: string
|
|
environment:
|
|
type: string
|
|
enum:
|
|
- sandbox
|
|
- production
|
|
platform:
|
|
type: string
|
|
enum:
|
|
- ios
|
|
required: true
|
|
"/api/v1/push_subscriptions/{id}":
|
|
parameters:
|
|
- name: id
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
delete:
|
|
summary: Unregister an APNs device token
|
|
tags:
|
|
- Push Subscriptions
|
|
security:
|
|
- apiKeyAuth: []
|
|
responses:
|
|
'204':
|
|
description: token unregistered
|
|
"/api/v1/recurring_transactions":
|
|
get:
|
|
summary: List recurring transactions
|
|
tags:
|
|
- Recurring Transactions
|
|
security:
|
|
- apiKeyAuth: []
|
|
parameters:
|
|
- name: page
|
|
in: query
|
|
required: false
|
|
description: 'Page number (default: 1)'
|
|
schema:
|
|
type: integer
|
|
- name: per_page
|
|
in: query
|
|
required: false
|
|
description: 'Items per page (default: 25, max: 100)'
|
|
schema:
|
|
type: integer
|
|
- name: status
|
|
in: query
|
|
required: false
|
|
description: Filter by recurring status
|
|
schema:
|
|
type: string
|
|
enum:
|
|
- active
|
|
- inactive
|
|
- name: account_id
|
|
in: query
|
|
required: false
|
|
description: Filter by account ID
|
|
schema:
|
|
type: string
|
|
format: uuid
|
|
responses:
|
|
'200':
|
|
description: recurring transactions listed
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/RecurringTransactionCollection"
|
|
'401':
|
|
description: unauthorized
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'422':
|
|
description: validation error - malformed account filter
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
post:
|
|
summary: Create recurring transaction
|
|
tags:
|
|
- Recurring Transactions
|
|
security:
|
|
- apiKeyAuth: []
|
|
parameters: []
|
|
responses:
|
|
'201':
|
|
description: recurring transaction created
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/RecurringTransaction"
|
|
'401':
|
|
description: unauthorized
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'403':
|
|
description: forbidden - requires read_write scope
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'404':
|
|
description: account not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'422':
|
|
description: validation error - negative occurrence count
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
requestBody:
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
recurring_transaction:
|
|
type: object
|
|
properties:
|
|
account_id:
|
|
type: string
|
|
format: uuid
|
|
nullable: true
|
|
merchant_id:
|
|
type: string
|
|
format: uuid
|
|
nullable: true
|
|
name:
|
|
type: string
|
|
nullable: true
|
|
amount:
|
|
type: number
|
|
currency:
|
|
type: string
|
|
expected_day_of_month:
|
|
type: integer
|
|
minimum: 1
|
|
maximum: 31
|
|
last_occurrence_date:
|
|
type: string
|
|
format: date
|
|
next_expected_date:
|
|
type: string
|
|
format: date
|
|
status:
|
|
type: string
|
|
enum:
|
|
- active
|
|
- inactive
|
|
occurrence_count:
|
|
type: integer
|
|
minimum: 0
|
|
manual:
|
|
type: boolean
|
|
payment_url:
|
|
type: string
|
|
nullable: true
|
|
description: Link to the biller portal. Only http and https
|
|
are accepted; a bare host is stored as https.
|
|
autopay:
|
|
type: boolean
|
|
description: Whether this bill pays itself automatically.
|
|
notes:
|
|
type: string
|
|
nullable: true
|
|
description: Free-text notes shown alongside the bill.
|
|
expected_amount_min:
|
|
type: number
|
|
nullable: true
|
|
expected_amount_max:
|
|
type: number
|
|
nullable: true
|
|
expected_amount_avg:
|
|
type: number
|
|
nullable: true
|
|
required:
|
|
- amount
|
|
- currency
|
|
- expected_day_of_month
|
|
- last_occurrence_date
|
|
- next_expected_date
|
|
anyOf:
|
|
- required:
|
|
- name
|
|
- required:
|
|
- merchant_id
|
|
required:
|
|
- recurring_transaction
|
|
required: true
|
|
"/api/v1/recurring_transactions/{id}":
|
|
parameters:
|
|
- name: id
|
|
in: path
|
|
required: true
|
|
description: Recurring transaction ID
|
|
schema:
|
|
type: string
|
|
get:
|
|
summary: Retrieve recurring transaction
|
|
tags:
|
|
- Recurring Transactions
|
|
security:
|
|
- apiKeyAuth: []
|
|
responses:
|
|
'200':
|
|
description: recurring transaction retrieved
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/RecurringTransaction"
|
|
'401':
|
|
description: unauthorized
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'404':
|
|
description: recurring transaction not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
patch:
|
|
summary: Update recurring transaction
|
|
tags:
|
|
- Recurring Transactions
|
|
security:
|
|
- apiKeyAuth: []
|
|
parameters: []
|
|
responses:
|
|
'200':
|
|
description: recurring transaction updated
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/RecurringTransaction"
|
|
'401':
|
|
description: unauthorized
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'403':
|
|
description: forbidden - requires read_write scope
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'404':
|
|
description: recurring transaction not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'422':
|
|
description: validation error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
requestBody:
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
recurring_transaction:
|
|
type: object
|
|
properties:
|
|
status:
|
|
type: string
|
|
enum:
|
|
- active
|
|
- inactive
|
|
expected_day_of_month:
|
|
type: integer
|
|
minimum: 1
|
|
maximum: 31
|
|
next_expected_date:
|
|
type: string
|
|
format: date
|
|
payment_url:
|
|
type: string
|
|
nullable: true
|
|
description: Link to the biller portal. Only http and https
|
|
are accepted; a bare host is stored as https. Send an empty
|
|
string to clear it.
|
|
autopay:
|
|
type: boolean
|
|
description: Whether this bill pays itself automatically.
|
|
notes:
|
|
type: string
|
|
nullable: true
|
|
description: Free-text notes shown alongside the bill.
|
|
required: true
|
|
delete:
|
|
summary: Delete recurring transaction
|
|
tags:
|
|
- Recurring Transactions
|
|
security:
|
|
- apiKeyAuth: []
|
|
responses:
|
|
'200':
|
|
description: recurring transaction deleted
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/SuccessMessage"
|
|
'401':
|
|
description: unauthorized
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'403':
|
|
description: forbidden - requires read_write scope
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'404':
|
|
description: recurring transaction not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
"/api/v1/rejected_transfers":
|
|
get:
|
|
summary: List rejected transfers
|
|
tags:
|
|
- Rejected Transfers
|
|
security:
|
|
- apiKeyAuth: []
|
|
parameters:
|
|
- name: page
|
|
in: query
|
|
required: false
|
|
description: 'Page number (default: 1)'
|
|
schema:
|
|
type: integer
|
|
- name: per_page
|
|
in: query
|
|
required: false
|
|
description: 'Items per page (default: 25, max: 100)'
|
|
schema:
|
|
type: integer
|
|
- name: account_id
|
|
in: query
|
|
required: false
|
|
schema:
|
|
type: string
|
|
format: uuid
|
|
description: Filter rejected transfers involving this account
|
|
- name: start_date
|
|
in: query
|
|
required: false
|
|
schema:
|
|
type: string
|
|
format: date
|
|
description: Filter rejected transfers from this date
|
|
- name: end_date
|
|
in: query
|
|
required: false
|
|
schema:
|
|
type: string
|
|
format: date
|
|
description: Filter rejected transfers until this date
|
|
responses:
|
|
'200':
|
|
description: rejected transfers listed
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/RejectedTransferCollection"
|
|
'401':
|
|
description: unauthorized
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'403':
|
|
description: insufficient scope
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'422':
|
|
description: invalid filter
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
"/api/v1/rejected_transfers/{id}":
|
|
parameters:
|
|
- name: id
|
|
in: path
|
|
required: true
|
|
description: Rejected transfer ID
|
|
schema:
|
|
type: string
|
|
get:
|
|
summary: Retrieve a rejected transfer
|
|
tags:
|
|
- Rejected Transfers
|
|
security:
|
|
- apiKeyAuth: []
|
|
responses:
|
|
'200':
|
|
description: rejected transfer retrieved
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/RejectedTransfer"
|
|
'401':
|
|
description: unauthorized
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'403':
|
|
description: insufficient scope
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'404':
|
|
description: rejected transfer not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
"/api/v1/rule_runs":
|
|
get:
|
|
summary: List rule runs
|
|
description: List rule run history for the authenticated user family.
|
|
tags:
|
|
- Rule Runs
|
|
security:
|
|
- apiKeyAuth: []
|
|
parameters:
|
|
- name: page
|
|
in: query
|
|
required: false
|
|
description: 'Page number (default: 1)'
|
|
schema:
|
|
type: integer
|
|
- name: per_page
|
|
in: query
|
|
required: false
|
|
description: 'Items per page (default: 25, max: 100)'
|
|
schema:
|
|
type: integer
|
|
- name: rule_id
|
|
in: query
|
|
required: false
|
|
description: Filter by rule ID
|
|
schema:
|
|
type: string
|
|
format: uuid
|
|
- name: status
|
|
in: query
|
|
required: false
|
|
description: Filter by run status
|
|
schema:
|
|
type: string
|
|
enum:
|
|
- pending
|
|
- success
|
|
- failed
|
|
- name: execution_type
|
|
in: query
|
|
required: false
|
|
description: Filter by execution type
|
|
schema:
|
|
type: string
|
|
enum:
|
|
- manual
|
|
- scheduled
|
|
- name: start_executed_at
|
|
in: query
|
|
required: false
|
|
description: Filter runs executed at or after this timestamp
|
|
schema:
|
|
type: string
|
|
format: date-time
|
|
- name: end_executed_at
|
|
in: query
|
|
required: false
|
|
description: Filter runs executed at or before this timestamp
|
|
schema:
|
|
type: string
|
|
format: date-time
|
|
responses:
|
|
'200':
|
|
description: rule runs listed
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/RuleRunCollection"
|
|
'401':
|
|
description: unauthorized
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'403':
|
|
description: insufficient scope
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'422':
|
|
description: invalid filter
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
"/api/v1/rule_runs/{id}":
|
|
parameters:
|
|
- name: id
|
|
in: path
|
|
required: true
|
|
description: Rule run ID
|
|
schema:
|
|
type: string
|
|
format: uuid
|
|
get:
|
|
summary: Retrieve a rule run
|
|
description: Retrieve one rule run from the authenticated user family.
|
|
tags:
|
|
- Rule Runs
|
|
security:
|
|
- apiKeyAuth: []
|
|
responses:
|
|
'200':
|
|
description: rule run retrieved
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/RuleRunResponse"
|
|
'401':
|
|
description: unauthorized
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'403':
|
|
description: insufficient scope
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'404':
|
|
description: rule run not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
"/api/v1/rules":
|
|
get:
|
|
summary: List rules
|
|
tags:
|
|
- Rules
|
|
security:
|
|
- apiKeyAuth: []
|
|
parameters:
|
|
- name: page
|
|
in: query
|
|
required: false
|
|
description: 'Page number (default: 1)'
|
|
schema:
|
|
type: integer
|
|
- name: per_page
|
|
in: query
|
|
required: false
|
|
description: 'Items per page (default: 25, max: 100)'
|
|
schema:
|
|
type: integer
|
|
- name: resource_type
|
|
in: query
|
|
required: false
|
|
description: Filter by rule resource type
|
|
schema:
|
|
type: string
|
|
enum:
|
|
- transaction
|
|
- name: active
|
|
in: query
|
|
required: false
|
|
description: Filter by active status
|
|
schema:
|
|
type: boolean
|
|
responses:
|
|
'200':
|
|
description: rules listed
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/RuleCollection"
|
|
'401':
|
|
description: unauthorized
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'403':
|
|
description: forbidden - requires read scope
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'422':
|
|
description: unsupported resource type
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
"/api/v1/rules/{id}":
|
|
parameters:
|
|
- name: id
|
|
in: path
|
|
required: true
|
|
description: Rule ID
|
|
schema:
|
|
type: string
|
|
get:
|
|
summary: Retrieve a rule
|
|
tags:
|
|
- Rules
|
|
security:
|
|
- apiKeyAuth: []
|
|
responses:
|
|
'200':
|
|
description: rule retrieved
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/RuleResponse"
|
|
'401':
|
|
description: unauthorized
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'403':
|
|
description: forbidden - requires read scope
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'404':
|
|
description: rule not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
"/api/v1/securities":
|
|
get:
|
|
summary: List securities referenced by family investment data
|
|
tags:
|
|
- Securities
|
|
security:
|
|
- apiKeyAuth: []
|
|
parameters:
|
|
- name: page
|
|
in: query
|
|
required: false
|
|
description: 'Page number (default: 1)'
|
|
schema:
|
|
type: integer
|
|
- name: per_page
|
|
in: query
|
|
required: false
|
|
description: 'Items per page (default: 25, max: 100)'
|
|
schema:
|
|
type: integer
|
|
- name: ticker
|
|
in: query
|
|
required: false
|
|
description: Filter by ticker symbol
|
|
schema:
|
|
type: string
|
|
- name: exchange_operating_mic
|
|
in: query
|
|
required: false
|
|
description: Filter by exchange operating MIC
|
|
schema:
|
|
type: string
|
|
- name: kind
|
|
in: query
|
|
required: false
|
|
description: Filter by security kind
|
|
schema:
|
|
type: string
|
|
enum:
|
|
- standard
|
|
- cash
|
|
- name: offline
|
|
in: query
|
|
required: false
|
|
description: Filter by offline status. When supplied, must be true or false.
|
|
schema:
|
|
type: boolean
|
|
responses:
|
|
'200':
|
|
description: securities listed
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/SecurityCollection"
|
|
'401':
|
|
description: unauthorized
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'403':
|
|
description: insufficient scope
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'422':
|
|
description: invalid filter
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
"/api/v1/securities/{id}":
|
|
parameters:
|
|
- name: id
|
|
in: path
|
|
required: true
|
|
description: Security ID
|
|
schema:
|
|
type: string
|
|
format: uuid
|
|
get:
|
|
summary: Retrieve a security referenced by family investment data
|
|
tags:
|
|
- Securities
|
|
security:
|
|
- apiKeyAuth: []
|
|
responses:
|
|
'200':
|
|
description: security retrieved
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/Security"
|
|
'401':
|
|
description: unauthorized
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'403':
|
|
description: insufficient scope
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'404':
|
|
description: security not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
"/api/v1/security_prices":
|
|
get:
|
|
summary: List security price history referenced by family investment data
|
|
tags:
|
|
- Security Prices
|
|
security:
|
|
- apiKeyAuth: []
|
|
parameters:
|
|
- name: page
|
|
in: query
|
|
required: false
|
|
description: 'Page number (default: 1)'
|
|
schema:
|
|
type: integer
|
|
- name: per_page
|
|
in: query
|
|
required: false
|
|
description: 'Items per page (default: 25, max: 100)'
|
|
schema:
|
|
type: integer
|
|
- name: security_id
|
|
in: query
|
|
required: false
|
|
description: Filter by security ID
|
|
schema:
|
|
type: string
|
|
format: uuid
|
|
- name: currency
|
|
in: query
|
|
required: false
|
|
description: Filter by currency code
|
|
schema:
|
|
type: string
|
|
- name: start_date
|
|
in: query
|
|
required: false
|
|
description: Filter prices from this date
|
|
schema:
|
|
type: string
|
|
format: date
|
|
- name: end_date
|
|
in: query
|
|
required: false
|
|
description: Filter prices until this date
|
|
schema:
|
|
type: string
|
|
format: date
|
|
- name: provisional
|
|
in: query
|
|
required: false
|
|
description: Filter by provisional price status. When supplied, must be true
|
|
or false.
|
|
schema:
|
|
type: boolean
|
|
responses:
|
|
'200':
|
|
description: security prices listed
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/SecurityPriceCollection"
|
|
'401':
|
|
description: unauthorized
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'403':
|
|
description: insufficient scope
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'422':
|
|
description: invalid filter
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
"/api/v1/security_prices/{id}":
|
|
parameters:
|
|
- name: id
|
|
in: path
|
|
required: true
|
|
description: Security price ID
|
|
schema:
|
|
type: string
|
|
format: uuid
|
|
get:
|
|
summary: Retrieve a security price referenced by family investment data
|
|
tags:
|
|
- Security Prices
|
|
security:
|
|
- apiKeyAuth: []
|
|
responses:
|
|
'200':
|
|
description: security price retrieved
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/SecurityPrice"
|
|
'401':
|
|
description: unauthorized
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'403':
|
|
description: insufficient scope
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'404':
|
|
description: security price not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
"/api/v1/syncs":
|
|
get:
|
|
summary: Lists sync history
|
|
description: List sanitized sync status history for the authenticated user's
|
|
family, accounts, and provider connections.
|
|
tags:
|
|
- Syncs
|
|
security:
|
|
- apiKeyAuth: []
|
|
parameters:
|
|
- name: page
|
|
in: query
|
|
required: false
|
|
description: 'Page number (default: 1)'
|
|
schema:
|
|
type: integer
|
|
- name: per_page
|
|
in: query
|
|
required: false
|
|
description: 'Items per page (default: 25, max: 100)'
|
|
schema:
|
|
type: integer
|
|
responses:
|
|
'200':
|
|
description: syncs listed
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/SyncCollection"
|
|
'401':
|
|
description: unauthorized
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'403':
|
|
description: forbidden
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
"/api/v1/syncs/latest":
|
|
get:
|
|
summary: Shows the latest sync
|
|
description: 'Return the most recently created sanitized sync status for the
|
|
authenticated user''s family, or data: null when no sync exists.'
|
|
tags:
|
|
- Syncs
|
|
security:
|
|
- apiKeyAuth: []
|
|
responses:
|
|
'200':
|
|
description: latest sync shown
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/SyncResponse"
|
|
'401':
|
|
description: unauthorized
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'403':
|
|
description: forbidden
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
"/api/v1/syncs/{id}":
|
|
parameters:
|
|
- name: id
|
|
in: path
|
|
format: uuid
|
|
required: true
|
|
schema:
|
|
type: string
|
|
get:
|
|
summary: Shows a sync
|
|
description: Return sanitized status metadata for a single family-scoped sync.
|
|
tags:
|
|
- Syncs
|
|
security:
|
|
- apiKeyAuth: []
|
|
responses:
|
|
'200':
|
|
description: sync shown
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/SyncResponse"
|
|
'401':
|
|
description: unauthorized
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'403':
|
|
description: forbidden
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'404':
|
|
description: not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
"/api/v1/tags":
|
|
get:
|
|
summary: List tags
|
|
tags:
|
|
- Tags
|
|
security:
|
|
- apiKeyAuth: []
|
|
responses:
|
|
'200':
|
|
description: tags listed
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/TagCollection"
|
|
post:
|
|
summary: Create tag
|
|
tags:
|
|
- Tags
|
|
security:
|
|
- apiKeyAuth: []
|
|
parameters: []
|
|
responses:
|
|
'201':
|
|
description: tag created with auto-assigned color
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/TagDetail"
|
|
'422':
|
|
description: validation error - missing name
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
requestBody:
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
tag:
|
|
type: object
|
|
properties:
|
|
name:
|
|
type: string
|
|
description: Tag name (required)
|
|
color:
|
|
type: string
|
|
description: Hex color code (optional, auto-assigned if not
|
|
provided)
|
|
required:
|
|
- name
|
|
required:
|
|
- tag
|
|
required: true
|
|
"/api/v1/tags/{id}":
|
|
parameters:
|
|
- name: id
|
|
in: path
|
|
required: true
|
|
description: Tag ID
|
|
schema:
|
|
type: string
|
|
get:
|
|
summary: Retrieve a tag
|
|
tags:
|
|
- Tags
|
|
security:
|
|
- apiKeyAuth: []
|
|
responses:
|
|
'200':
|
|
description: tag retrieved
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/TagDetail"
|
|
'404':
|
|
description: tag not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
patch:
|
|
summary: Update a tag
|
|
tags:
|
|
- Tags
|
|
security:
|
|
- apiKeyAuth: []
|
|
parameters: []
|
|
responses:
|
|
'200':
|
|
description: tag updated
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/TagDetail"
|
|
'404':
|
|
description: tag not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
requestBody:
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
tag:
|
|
type: object
|
|
properties:
|
|
name:
|
|
type: string
|
|
color:
|
|
type: string
|
|
required: true
|
|
delete:
|
|
summary: Delete a tag
|
|
tags:
|
|
- Tags
|
|
security:
|
|
- apiKeyAuth: []
|
|
responses:
|
|
'204':
|
|
description: tag deleted
|
|
'404':
|
|
description: tag not found
|
|
"/api/v1/trades":
|
|
get:
|
|
summary: List trades
|
|
tags:
|
|
- Trades
|
|
security:
|
|
- apiKeyAuth: []
|
|
parameters:
|
|
- name: page
|
|
in: query
|
|
required: false
|
|
description: 'Page number (default: 1)'
|
|
schema:
|
|
type: integer
|
|
- name: per_page
|
|
in: query
|
|
required: false
|
|
description: 'Items per page (default: 25, max: 100)'
|
|
schema:
|
|
type: integer
|
|
- name: account_id
|
|
in: query
|
|
required: false
|
|
description: Filter by account ID
|
|
schema:
|
|
type: string
|
|
- name: account_ids
|
|
in: query
|
|
required: false
|
|
description: Filter by multiple account IDs
|
|
schema:
|
|
type: array
|
|
items:
|
|
type: string
|
|
- name: start_date
|
|
in: query
|
|
required: false
|
|
description: Filter trades from this date (inclusive)
|
|
schema:
|
|
type: string
|
|
format: date
|
|
- name: end_date
|
|
in: query
|
|
required: false
|
|
description: Filter trades until this date (inclusive)
|
|
schema:
|
|
type: string
|
|
format: date
|
|
responses:
|
|
'200':
|
|
description: trades paginated
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/TradeCollection"
|
|
'401':
|
|
description: unauthorized
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'422':
|
|
description: invalid date filter
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
post:
|
|
summary: Create trade
|
|
tags:
|
|
- Trades
|
|
security:
|
|
- apiKeyAuth: []
|
|
parameters: []
|
|
responses:
|
|
'201':
|
|
description: interest created
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/TransactionResponse"
|
|
'403':
|
|
description: forbidden - api key missing read_write scope
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'401':
|
|
description: unauthorized - missing api key
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'422':
|
|
description: deposit without amount returns error
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'404':
|
|
description: account not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
requestBody:
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
trade:
|
|
type: object
|
|
properties:
|
|
account_id:
|
|
type: string
|
|
format: uuid
|
|
description: Account ID (required)
|
|
date:
|
|
type: string
|
|
format: date
|
|
description: Trade date (required)
|
|
qty:
|
|
type: number
|
|
description: Quantity (required for buy/sell)
|
|
price:
|
|
type: number
|
|
description: Price (required for buy/sell)
|
|
amount:
|
|
type: number
|
|
description: Amount (required for dividend, deposit, withdrawal,
|
|
interest)
|
|
type:
|
|
type: string
|
|
enum:
|
|
- buy
|
|
- sell
|
|
- dividend
|
|
- deposit
|
|
- withdrawal
|
|
- interest
|
|
description: Trade type (required)
|
|
security_id:
|
|
type: string
|
|
format: uuid
|
|
description: Security ID (one of security_id, ticker, manual_ticker
|
|
required)
|
|
ticker:
|
|
type: string
|
|
description: Ticker symbol
|
|
manual_ticker:
|
|
type: string
|
|
description: Manual ticker for offline securities
|
|
currency:
|
|
type: string
|
|
description: Currency (defaults to account currency)
|
|
investment_activity_label:
|
|
type: string
|
|
description: Activity label (e.g. Buy, Sell)
|
|
category_id:
|
|
type: string
|
|
format: uuid
|
|
description: Category ID
|
|
transfer_account_id:
|
|
type: string
|
|
format: uuid
|
|
description: Destination/source account ID for linked transfers
|
|
required:
|
|
- account_id
|
|
- date
|
|
- type
|
|
required:
|
|
- trade
|
|
required: true
|
|
"/api/v1/trades/{id}":
|
|
parameters:
|
|
- name: id
|
|
in: path
|
|
required: true
|
|
description: Trade ID
|
|
schema:
|
|
type: string
|
|
get:
|
|
summary: Retrieve trade
|
|
tags:
|
|
- Trades
|
|
security:
|
|
- apiKeyAuth: []
|
|
responses:
|
|
'200':
|
|
description: trade retrieved
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/Trade"
|
|
'401':
|
|
description: unauthorized
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'404':
|
|
description: trade not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
patch:
|
|
summary: Update trade
|
|
tags:
|
|
- Trades
|
|
security:
|
|
- apiKeyAuth: []
|
|
parameters: []
|
|
responses:
|
|
'200':
|
|
description: trade updated
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/Trade"
|
|
'401':
|
|
description: unauthorized
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'403':
|
|
description: forbidden - api key missing read_write scope
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'404':
|
|
description: trade not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
requestBody:
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
trade:
|
|
type: object
|
|
properties:
|
|
date:
|
|
type: string
|
|
format: date
|
|
qty:
|
|
type: number
|
|
price:
|
|
type: number
|
|
type:
|
|
type: string
|
|
enum:
|
|
- buy
|
|
- sell
|
|
- dividend
|
|
- deposit
|
|
- withdrawal
|
|
- interest
|
|
nature:
|
|
type: string
|
|
enum:
|
|
- inflow
|
|
- outflow
|
|
name:
|
|
type: string
|
|
notes:
|
|
type: string
|
|
currency:
|
|
type: string
|
|
investment_activity_label:
|
|
type: string
|
|
category_id:
|
|
type: string
|
|
format: uuid
|
|
required: true
|
|
delete:
|
|
summary: Delete trade
|
|
tags:
|
|
- Trades
|
|
security:
|
|
- apiKeyAuth: []
|
|
responses:
|
|
'200':
|
|
description: trade deleted
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/DeleteResponse"
|
|
'401':
|
|
description: unauthorized
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'403':
|
|
description: forbidden - api key missing read_write scope
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'404':
|
|
description: trade not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
"/api/v1/transactions":
|
|
get:
|
|
summary: List transactions
|
|
tags:
|
|
- Transactions
|
|
security:
|
|
- apiKeyAuth: []
|
|
description: Returns global ledger history for accessible accounts, including
|
|
disabled accounts but excluding accounts pending deletion.
|
|
parameters:
|
|
- name: page
|
|
in: query
|
|
required: false
|
|
description: 'Page number (default: 1)'
|
|
schema:
|
|
type: integer
|
|
- name: per_page
|
|
in: query
|
|
required: false
|
|
description: 'Items per page (default: 25, max: 100)'
|
|
schema:
|
|
type: integer
|
|
- name: account_id
|
|
in: query
|
|
required: false
|
|
description: Filter by account ID
|
|
schema:
|
|
type: string
|
|
- name: category_id
|
|
in: query
|
|
required: false
|
|
description: Filter by category ID
|
|
schema:
|
|
type: string
|
|
- name: merchant_id
|
|
in: query
|
|
required: false
|
|
description: Filter by merchant ID
|
|
schema:
|
|
type: string
|
|
- name: start_date
|
|
in: query
|
|
required: false
|
|
description: Filter transactions from this date
|
|
schema:
|
|
type: string
|
|
format: date
|
|
- name: end_date
|
|
in: query
|
|
required: false
|
|
description: Filter transactions until this date
|
|
schema:
|
|
type: string
|
|
format: date
|
|
- name: min_amount
|
|
in: query
|
|
required: false
|
|
description: Filter by minimum amount
|
|
schema:
|
|
type: number
|
|
- name: max_amount
|
|
in: query
|
|
required: false
|
|
description: Filter by maximum amount
|
|
schema:
|
|
type: number
|
|
- name: type
|
|
in: query
|
|
required: false
|
|
description: Filter by transaction type
|
|
schema:
|
|
type: string
|
|
enum:
|
|
- income
|
|
- expense
|
|
- name: search
|
|
in: query
|
|
required: false
|
|
description: Search by name, notes, or merchant name
|
|
schema:
|
|
type: string
|
|
- name: account_ids
|
|
in: query
|
|
required: false
|
|
description: Filter by multiple account IDs
|
|
schema:
|
|
type: array
|
|
items:
|
|
type: string
|
|
- name: category_ids
|
|
in: query
|
|
required: false
|
|
description: Filter by multiple category IDs
|
|
schema:
|
|
type: array
|
|
items:
|
|
type: string
|
|
- name: merchant_ids
|
|
in: query
|
|
required: false
|
|
description: Filter by multiple merchant IDs
|
|
schema:
|
|
type: array
|
|
items:
|
|
type: string
|
|
- name: tag_ids
|
|
in: query
|
|
required: false
|
|
description: Filter by tag IDs
|
|
schema:
|
|
type: array
|
|
items:
|
|
type: string
|
|
responses:
|
|
'200':
|
|
description: transactions filtered by date range
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/TransactionCollection"
|
|
post:
|
|
summary: Create transaction
|
|
tags:
|
|
- Transactions
|
|
security:
|
|
- apiKeyAuth: []
|
|
parameters: []
|
|
responses:
|
|
'201':
|
|
description: transaction created
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/Transaction"
|
|
'200':
|
|
description: transaction already exists for external idempotency key
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/Transaction"
|
|
'422':
|
|
description: validation error - missing required fields
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
requestBody:
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
transaction:
|
|
type: object
|
|
properties:
|
|
account_id:
|
|
type: string
|
|
format: uuid
|
|
description: Account ID (required)
|
|
date:
|
|
type: string
|
|
format: date
|
|
description: Transaction date
|
|
amount:
|
|
type: number
|
|
description: Transaction amount
|
|
name:
|
|
type: string
|
|
description: Transaction name/description
|
|
description:
|
|
type: string
|
|
description: Alternative to name field
|
|
notes:
|
|
type: string
|
|
description: Additional notes
|
|
currency:
|
|
type: string
|
|
description: Currency code (defaults to family currency)
|
|
category_id:
|
|
type: string
|
|
format: uuid
|
|
description: Category ID
|
|
merchant_id:
|
|
type: string
|
|
format: uuid
|
|
description: Merchant ID
|
|
nature:
|
|
type: string
|
|
enum:
|
|
- income
|
|
- expense
|
|
- inflow
|
|
- outflow
|
|
description: Transaction nature (determines sign)
|
|
external_id:
|
|
type: string
|
|
description: Optional external idempotency key scoped to account
|
|
and source
|
|
source:
|
|
type: string
|
|
description: Optional source namespace for external_id. Requires
|
|
external_id and defaults to api when external_id is provided
|
|
user_modified:
|
|
type: boolean
|
|
description: Whether provider syncs should preserve user-supplied
|
|
transaction changes
|
|
tag_ids:
|
|
type: array
|
|
items:
|
|
type: string
|
|
format: uuid
|
|
description: Array of tag IDs
|
|
required:
|
|
- account_id
|
|
- date
|
|
- amount
|
|
- name
|
|
required:
|
|
- transaction
|
|
required: true
|
|
"/api/v1/transactions/{id}":
|
|
parameters:
|
|
- name: id
|
|
in: path
|
|
schema:
|
|
type: string
|
|
format: uuid
|
|
required: true
|
|
description: Transaction ID
|
|
get:
|
|
summary: Retrieve a transaction
|
|
tags:
|
|
- Transactions
|
|
security:
|
|
- apiKeyAuth: []
|
|
responses:
|
|
'200':
|
|
description: transaction retrieved
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/Transaction"
|
|
'404':
|
|
description: transaction not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
patch:
|
|
summary: Update a transaction
|
|
tags:
|
|
- Transactions
|
|
security:
|
|
- apiKeyAuth: []
|
|
parameters: []
|
|
responses:
|
|
'200':
|
|
description: transaction updated
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/Transaction"
|
|
'404':
|
|
description: transaction not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
requestBody:
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
transaction:
|
|
type: object
|
|
properties:
|
|
date:
|
|
type: string
|
|
format: date
|
|
amount:
|
|
type: number
|
|
name:
|
|
type: string
|
|
description:
|
|
type: string
|
|
description: Alternative to name field
|
|
notes:
|
|
type: string
|
|
currency:
|
|
type: string
|
|
description: Currency code
|
|
category_id:
|
|
type: string
|
|
format: uuid
|
|
merchant_id:
|
|
type: string
|
|
format: uuid
|
|
nature:
|
|
type: string
|
|
enum:
|
|
- income
|
|
- expense
|
|
- inflow
|
|
- outflow
|
|
tag_ids:
|
|
type: array
|
|
items:
|
|
type: string
|
|
format: uuid
|
|
description: Array of tag IDs to assign. Omit to preserve existing
|
|
tags; use [] to clear all tags.
|
|
required: true
|
|
delete:
|
|
summary: Delete a transaction
|
|
tags:
|
|
- Transactions
|
|
security:
|
|
- apiKeyAuth: []
|
|
responses:
|
|
'200':
|
|
description: transaction deleted
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/DeleteResponse"
|
|
'404':
|
|
description: transaction not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
"/api/v1/transfers":
|
|
get:
|
|
summary: List transfers
|
|
tags:
|
|
- Transfers
|
|
security:
|
|
- apiKeyAuth: []
|
|
parameters:
|
|
- name: page
|
|
in: query
|
|
required: false
|
|
description: 'Page number (default: 1)'
|
|
schema:
|
|
type: integer
|
|
- name: per_page
|
|
in: query
|
|
required: false
|
|
description: 'Items per page (default: 25, max: 100)'
|
|
schema:
|
|
type: integer
|
|
- name: status
|
|
in: query
|
|
required: false
|
|
schema:
|
|
type: string
|
|
enum:
|
|
- pending
|
|
- confirmed
|
|
description: Filter by transfer status
|
|
- name: account_id
|
|
in: query
|
|
required: false
|
|
schema:
|
|
type: string
|
|
format: uuid
|
|
description: Filter transfers involving this account
|
|
- name: start_date
|
|
in: query
|
|
required: false
|
|
schema:
|
|
type: string
|
|
format: date
|
|
description: Filter transfers from this date
|
|
- name: end_date
|
|
in: query
|
|
required: false
|
|
schema:
|
|
type: string
|
|
format: date
|
|
description: Filter transfers until this date
|
|
responses:
|
|
'200':
|
|
description: transfers listed
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/TransferDecisionCollection"
|
|
'401':
|
|
description: unauthorized
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'403':
|
|
description: insufficient scope
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'422':
|
|
description: invalid filter
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
"/api/v1/transfers/{id}":
|
|
parameters:
|
|
- name: id
|
|
in: path
|
|
required: true
|
|
description: Transfer ID
|
|
schema:
|
|
type: string
|
|
get:
|
|
summary: Retrieve a transfer
|
|
tags:
|
|
- Transfers
|
|
security:
|
|
- apiKeyAuth: []
|
|
responses:
|
|
'200':
|
|
description: transfer retrieved
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/TransferDecision"
|
|
'401':
|
|
description: unauthorized
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'403':
|
|
description: insufficient scope
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'404':
|
|
description: transfer not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
"/api/v1/users/reset":
|
|
delete:
|
|
summary: Reset account
|
|
tags:
|
|
- Users
|
|
description: Resets all financial data (accounts, categories, merchants, tags,
|
|
etc.) for the current user's family while keeping the user account intact.
|
|
The reset runs asynchronously in the background. The returned job_id is informational
|
|
only; reset status is family-scoped, not job-scoped. Requires admin role.
|
|
security:
|
|
- apiKeyAuth: []
|
|
responses:
|
|
'200':
|
|
description: account reset initiated
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ResetInitiatedResponse"
|
|
'401':
|
|
description: unauthorized
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'403':
|
|
description: forbidden - requires read_write scope and admin role
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'500':
|
|
description: reset enqueue failed
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
"/api/v1/users/reset/status":
|
|
get:
|
|
summary: Retrieve reset status
|
|
tags:
|
|
- Users
|
|
description: Returns counts of family-owned data targeted by account reset.
|
|
Use this after DELETE /api/v1/users/reset to decide whether reset materialization
|
|
has completed. Completion is a counts-based family snapshot and may change
|
|
if new data is created after reset.
|
|
security:
|
|
- apiKeyAuth: []
|
|
responses:
|
|
'200':
|
|
description: reset status returned
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ResetStatusResponse"
|
|
'401':
|
|
description: unauthorized
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'403':
|
|
description: forbidden - requires admin role
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
"/api/v1/users/me":
|
|
delete:
|
|
summary: Delete account
|
|
tags:
|
|
- Users
|
|
description: Permanently deactivates the current user account and all associated
|
|
data. This action cannot be undone.
|
|
security:
|
|
- apiKeyAuth: []
|
|
responses:
|
|
'200':
|
|
description: account deleted
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/SuccessMessage"
|
|
'401':
|
|
description: unauthorized
|
|
'403':
|
|
description: insufficient scope
|
|
'422':
|
|
description: deactivation failed
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
"/api/v1/valuations":
|
|
get:
|
|
summary: List valuations
|
|
tags:
|
|
- Valuations
|
|
security:
|
|
- apiKeyAuth: []
|
|
parameters:
|
|
- name: page
|
|
in: query
|
|
required: false
|
|
description: 'Page number (default: 1)'
|
|
schema:
|
|
type: integer
|
|
- name: per_page
|
|
in: query
|
|
required: false
|
|
description: 'Items per page (default: 25, max: 100)'
|
|
schema:
|
|
type: integer
|
|
- name: account_id
|
|
in: query
|
|
required: false
|
|
description: Filter by account ID
|
|
schema:
|
|
type: string
|
|
format: uuid
|
|
- name: start_date
|
|
in: query
|
|
required: false
|
|
description: Filter valuations from this date
|
|
schema:
|
|
type: string
|
|
format: date
|
|
- name: end_date
|
|
in: query
|
|
required: false
|
|
description: Filter valuations until this date
|
|
schema:
|
|
type: string
|
|
format: date
|
|
responses:
|
|
'200':
|
|
description: valuations listed
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ValuationCollection"
|
|
'401':
|
|
description: unauthorized
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'422':
|
|
description: invalid account filter
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
post:
|
|
summary: Create valuation
|
|
tags:
|
|
- Valuations
|
|
security:
|
|
- apiKeyAuth: []
|
|
parameters: []
|
|
responses:
|
|
'201':
|
|
description: valuation created
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/Valuation"
|
|
'200':
|
|
description: existing valuation upserted
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/Valuation"
|
|
'422':
|
|
description: validation error - missing date
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'404':
|
|
description: account not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
requestBody:
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
valuation:
|
|
type: object
|
|
properties:
|
|
account_id:
|
|
type: string
|
|
format: uuid
|
|
description: Account ID (required)
|
|
amount:
|
|
type: number
|
|
description: Valuation amount (required)
|
|
date:
|
|
type: string
|
|
format: date
|
|
description: Valuation date (required)
|
|
notes:
|
|
type: string
|
|
description: Additional notes
|
|
upsert:
|
|
type: boolean
|
|
description: Nested alternative to the top-level response-status
|
|
flag. Top-level upsert takes precedence when both are provided.
|
|
required:
|
|
- account_id
|
|
- amount
|
|
- date
|
|
upsert:
|
|
type: boolean
|
|
description: Response-status signal only. When true and a same-account
|
|
same-date valuation exists before the request, the endpoint returns
|
|
200 OK instead of 201 Created. The underlying reconciliation write
|
|
path is unchanged; this flag does not add duplicate-prevention
|
|
or safe-retry guarantees beyond existing same-date reconciliation
|
|
behavior.
|
|
required:
|
|
- valuation
|
|
required: true
|
|
"/api/v1/valuations/{id}":
|
|
parameters:
|
|
- name: id
|
|
in: path
|
|
required: true
|
|
description: Valuation ID (entry ID)
|
|
schema:
|
|
type: string
|
|
get:
|
|
summary: Retrieve a valuation
|
|
tags:
|
|
- Valuations
|
|
security:
|
|
- apiKeyAuth: []
|
|
responses:
|
|
'200':
|
|
description: valuation retrieved
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/Valuation"
|
|
'404':
|
|
description: valuation not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
patch:
|
|
summary: Update a valuation
|
|
tags:
|
|
- Valuations
|
|
security:
|
|
- apiKeyAuth: []
|
|
parameters: []
|
|
responses:
|
|
'200':
|
|
description: valuation updated with amount and date
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/Valuation"
|
|
'422':
|
|
description: validation error - only one of amount/date provided
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
'404':
|
|
description: valuation not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
|
requestBody:
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
valuation:
|
|
type: object
|
|
properties:
|
|
amount:
|
|
type: number
|
|
description: New valuation amount (must provide with date)
|
|
date:
|
|
type: string
|
|
format: date
|
|
description: New valuation date (must provide with amount)
|
|
notes:
|
|
type: string
|
|
description: Additional notes
|
|
required: true
|