Files
sure/docs/api/openapi.yaml
ghost e93b1f1fd7 feat(api): expose family settings (#1645)
* feat(api): expose family settings

* test(api): assert family settings moniker

* test(api): align family settings api key helper

* fix(api): tighten family settings schema
2026-05-03 23:10:46 +02:00

4422 lines
118 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"
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
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"
Merchant:
type: object
required:
- id
- name
properties:
id:
type: string
format: uuid
name:
type: string
MerchantDetail:
type: object
required:
- id
- name
- type
- created_at
- updated_at
properties:
id:
type: string
format: uuid
name:
type: string
type:
type: string
enum:
- FamilyMerchant
- ProviderMerchant
created_at:
type: string
format: date-time
updated_at:
type: string
format: date-time
Tag:
type: object
required:
- id
- name
- color
properties:
id:
type: string
format: uuid
name:
type: string
color:
type: string
TagDetail:
type: object
required:
- id
- name
- color
- created_at
- updated_at
properties:
id:
type: string
format: uuid
name:
type: string
color:
type: string
created_at:
type: string
format: date-time
updated_at:
type: string
format: date-time
TagCollection:
type: array
items:
"$ref": "#/components/schemas/TagDetail"
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
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:
- active
- inactive
occurrence_count:
type: integer
minimum: 0
name:
type: string
nullable: true
manual:
type: boolean
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
classification:
type: string
account:
"$ref": "#/components/schemas/Account"
category:
"$ref": "#/components/schemas/Category"
nullable: true
merchant:
"$ref": "#/components/schemas/Merchant"
nullable: true
tags:
type: array
items:
"$ref": "#/components/schemas/Tag"
transfer:
"$ref": "#/components/schemas/Transfer"
nullable: true
created_at:
type: string
format: date-time
updated_at:
type: string
format: date-time
TransactionCollection:
type: object
required:
- transactions
- pagination
properties:
transactions:
type: array
items:
"$ref": "#/components/schemas/Transaction"
pagination:
"$ref": "#/components/schemas/Pagination"
Valuation:
type: object
required:
- id
- date
- amount
- currency
- kind
- account
- created_at
- updated_at
properties:
id:
type: string
format: uuid
date:
type: string
format: date
amount:
type: string
currency:
type: string
notes:
type: string
nullable: true
kind:
type: string
account:
"$ref": "#/components/schemas/Account"
created_at:
type: string
format: date-time
updated_at:
type: string
format: date-time
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
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
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
- CategoryImport
- RuleImport
- 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
- CategoryImport
- RuleImport
- 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"
ImportCollection:
type: object
required:
- data
- meta
properties:
data:
type: array
items:
"$ref": "#/components/schemas/ImportSummary"
meta:
type: object
required:
- current_page
- total_pages
- total_count
- per_page
properties:
current_page:
type: integer
minimum: 1
next_page:
type: integer
nullable: true
prev_page:
type: integer
nullable: true
total_pages:
type: integer
minimum: 0
total_count:
type: integer
minimum: 0
per_page:
type: integer
minimum: 1
ImportResponse:
type: object
required:
- data
properties:
data:
"$ref": "#/components/schemas/ImportDetail"
Trade:
type: object
required:
- id
- date
- amount
- currency
- name
- qty
- price
- account
- created_at
- updated_at
properties:
id:
type: string
format: uuid
date:
type: string
format: date
amount:
type: string
currency:
type: string
name:
type: string
notes:
type: string
nullable: true
qty:
type: string
price:
type: string
investment_activity_label:
type: string
nullable: true
account:
"$ref": "#/components/schemas/Account"
security:
type: object
nullable: true
properties:
id:
type: string
format: uuid
ticker:
type: string
name:
type: string
nullable: true
category:
type: object
nullable: true
properties:
id:
type: string
format: uuid
name:
type: string
created_at:
type: string
format: date-time
updated_at:
type: string
format: date-time
TradeCollection:
type: object
required:
- trades
- pagination
properties:
trades:
type: array
items:
"$ref": "#/components/schemas/Trade"
pagination:
"$ref": "#/components/schemas/Pagination"
Holding:
type: object
required:
- id
- date
- qty
- price
- amount
- currency
- account
- security
- created_at
- updated_at
properties:
id:
type: string
format: uuid
date:
type: string
format: date
qty:
type: string
description: Quantity of shares held
price:
type: string
description: Formatted price per share
amount:
type: string
currency:
type: string
cost_basis_source:
type: string
nullable: true
account:
"$ref": "#/components/schemas/Account"
security:
type: object
required:
- id
- ticker
- name
properties:
id:
type: string
format: uuid
ticker:
type: string
name:
type: string
nullable: true
avg_cost:
type: string
nullable: true
created_at:
type: string
format: date-time
updated_at:
type: string
format: date-time
HoldingCollection:
type: object
required:
- holdings
- pagination
properties:
holdings:
type: array
items:
"$ref": "#/components/schemas/Holding"
pagination:
"$ref": "#/components/schemas/Pagination"
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:
- accounts
- categories
- tags
- merchants
- plaid_items
- imports
- budgets
properties:
accounts:
type: integer
minimum: 0
categories:
type: integer
minimum: 0
tags:
type: integer
minimum: 0
merchants:
type: integer
minimum: 0
plaid_items:
type: integer
minimum: 0
imports:
type: integer
minimum: 0
budgets:
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
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"
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.
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: 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/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"
"/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/imports":
get:
summary: List imports
description: List all imports for the user's family with pagination and filtering.
tags:
- Imports
security:
- apiKeyAuth: []
parameters:
- name: page
in: query
required: false
description: 'Page number (default: 1)'
schema:
type: integer
- name: per_page
in: query
required: false
description: 'Items per page (default: 25, max: 100)'
schema:
type: integer
- name: status
in: query
required: false
description: Filter by status
schema:
type: string
enum:
- pending
- complete
- importing
- reverting
- revert_failed
- failed
- name: type
in: query
required: false
description: Filter by import type
schema:
type: string
enum:
- TransactionImport
- TradeImport
- AccountImport
- MintImport
- CategoryImport
- RuleImport
- 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.
tags:
- Imports
security:
- apiKeyAuth: []
parameters: []
responses:
'201':
description: import created
content:
application/json:
schema:
"$ref": "#/components/schemas/ImportResponse"
'422':
description: validation error - file too large
content:
application/json:
schema:
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. Required
for SureImport unless a multipart file is uploaded.
type:
type: string
enum:
- TransactionImport
- TradeImport
- AccountImport
- MintImport
- CategoryImport
- RuleImport
- 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. Required
for SureImport unless a multipart file is uploaded.
type:
type: string
enum:
- TransactionImport
- TradeImport
- AccountImport
- MintImport
- CategoryImport
- RuleImport
- 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 and row statistics.
tags:
- Imports
security:
- apiKeyAuth: []
responses:
'200':
description: import retrieved
content:
application/json:
schema:
"$ref": "#/components/schemas/ImportResponse"
'404':
description: import not found
content:
application/json:
schema:
"$ref": "#/components/schemas/ErrorResponse"
"/api/v1/merchants":
get:
summary: List merchants
tags:
- Merchants
security:
- apiKeyAuth: []
responses:
'200':
description: merchants listed
content:
application/json:
schema:
type: array
items:
"$ref": "#/components/schemas/MerchantDetail"
"/api/v1/merchants/{id}":
parameters:
- name: id
in: path
required: true
description: Merchant ID
schema:
type: string
get:
summary: Retrieve a merchant
tags:
- Merchants
security:
- apiKeyAuth: []
responses:
'200':
description: merchant retrieved
content:
application/json:
schema:
"$ref": "#/components/schemas/MerchantDetail"
'404':
description: merchant not found
content:
application/json:
schema:
"$ref": "#/components/schemas/ErrorResponse"
"/api/v1/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
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
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/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/tags":
get:
summary: List tags
tags:
- Tags
security:
- apiKeyAuth: []
responses:
'200':
description: tags listed
content:
application/json:
schema:
"$ref": "#/components/schemas/TagCollection"
post:
summary: Create tag
tags:
- Tags
security:
- apiKeyAuth: []
parameters: []
responses:
'201':
description: tag created with auto-assigned color
content:
application/json:
schema:
"$ref": "#/components/schemas/TagDetail"
'422':
description: validation error - missing name
content:
application/json:
schema:
"$ref": "#/components/schemas/ErrorResponse"
requestBody:
content:
application/json:
schema:
type: object
properties:
tag:
type: object
properties:
name:
type: string
description: Tag name (required)
color:
type: string
description: Hex color code (optional, auto-assigned if not
provided)
required:
- name
required:
- tag
required: true
"/api/v1/tags/{id}":
parameters:
- name: id
in: path
required: true
description: Tag ID
schema:
type: string
get:
summary: Retrieve a tag
tags:
- Tags
security:
- apiKeyAuth: []
responses:
'200':
description: tag retrieved
content:
application/json:
schema:
"$ref": "#/components/schemas/TagDetail"
'404':
description: tag not found
content:
application/json:
schema:
"$ref": "#/components/schemas/ErrorResponse"
patch:
summary: Update a tag
tags:
- Tags
security:
- apiKeyAuth: []
parameters: []
responses:
'200':
description: tag updated
content:
application/json:
schema:
"$ref": "#/components/schemas/TagDetail"
'404':
description: tag not found
content:
application/json:
schema:
"$ref": "#/components/schemas/ErrorResponse"
requestBody:
content:
application/json:
schema:
type: object
properties:
tag:
type: object
properties:
name:
type: string
color:
type: string
required: true
delete:
summary: Delete a tag
tags:
- Tags
security:
- apiKeyAuth: []
responses:
'204':
description: tag deleted
'404':
description: tag not found
"/api/v1/trades":
get:
summary: List trades
tags:
- Trades
security:
- apiKeyAuth: []
parameters:
- name: page
in: query
required: false
description: 'Page number (default: 1)'
schema:
type: integer
- name: per_page
in: query
required: false
description: 'Items per page (default: 25, max: 100)'
schema:
type: integer
- name: account_id
in: query
required: false
description: Filter by account ID
schema:
type: string
- name: account_ids
in: query
required: false
description: Filter by multiple account IDs
schema:
type: array
items:
type: string
- name: start_date
in: query
required: false
description: Filter trades from this date (inclusive)
schema:
type: string
format: date
- name: end_date
in: query
required: false
description: Filter trades until this date (inclusive)
schema:
type: string
format: date
responses:
'200':
description: trades paginated
content:
application/json:
schema:
"$ref": "#/components/schemas/TradeCollection"
'401':
description: unauthorized
content:
application/json:
schema:
"$ref": "#/components/schemas/ErrorResponse"
'422':
description: invalid date filter
content:
application/json:
schema:
"$ref": "#/components/schemas/ErrorResponse"
post:
summary: Create trade
tags:
- Trades
security:
- apiKeyAuth: []
parameters: []
responses:
'201':
description: trade created
content:
application/json:
schema:
"$ref": "#/components/schemas/Trade"
'422':
description: validation error - missing security identifier
content:
application/json:
schema:
"$ref": "#/components/schemas/ErrorResponse"
'404':
description: account not found
content:
application/json:
schema:
"$ref": "#/components/schemas/ErrorResponse"
requestBody:
content:
application/json:
schema:
type: object
properties:
trade:
type: object
properties:
account_id:
type: string
format: uuid
description: Account ID (required)
date:
type: string
format: date
description: Trade date (required)
qty:
type: number
description: Quantity (required)
price:
type: number
description: Price (required)
type:
type: string
enum:
- buy
- sell
description: Trade type (required)
security_id:
type: string
format: uuid
description: Security ID (one of security_id, ticker, manual_ticker
required)
ticker:
type: string
description: Ticker symbol
manual_ticker:
type: string
description: Manual ticker for offline securities
currency:
type: string
description: Currency (defaults to account currency)
investment_activity_label:
type: string
description: Activity label (e.g. Buy, Sell)
category_id:
type: string
format: uuid
description: Category ID
required:
- account_id
- date
- qty
- price
- type
required:
- trade
required: true
"/api/v1/trades/{id}":
parameters:
- name: id
in: path
required: true
description: Trade ID
schema:
type: string
get:
summary: Retrieve trade
tags:
- Trades
security:
- apiKeyAuth: []
responses:
'200':
description: trade retrieved
content:
application/json:
schema:
"$ref": "#/components/schemas/Trade"
'401':
description: unauthorized
content:
application/json:
schema:
"$ref": "#/components/schemas/ErrorResponse"
'404':
description: trade not found
content:
application/json:
schema:
"$ref": "#/components/schemas/ErrorResponse"
patch:
summary: Update trade
tags:
- Trades
security:
- apiKeyAuth: []
parameters: []
responses:
'200':
description: trade updated
content:
application/json:
schema:
"$ref": "#/components/schemas/Trade"
'404':
description: trade not found
content:
application/json:
schema:
"$ref": "#/components/schemas/ErrorResponse"
requestBody:
content:
application/json:
schema:
type: object
properties:
trade:
type: object
properties:
date:
type: string
format: date
qty:
type: number
price:
type: number
type:
type: string
enum:
- buy
- sell
nature:
type: string
enum:
- inflow
- outflow
name:
type: string
notes:
type: string
currency:
type: string
investment_activity_label:
type: string
category_id:
type: string
format: uuid
required: true
delete:
summary: Delete trade
tags:
- Trades
security:
- apiKeyAuth: []
responses:
'200':
description: trade deleted
content:
application/json:
schema:
"$ref": "#/components/schemas/DeleteResponse"
'404':
description: trade not found
content:
application/json:
schema:
"$ref": "#/components/schemas/ErrorResponse"
"/api/v1/transactions":
get:
summary: List transactions
tags:
- Transactions
security:
- apiKeyAuth: []
parameters:
- name: page
in: query
required: false
description: 'Page number (default: 1)'
schema:
type: integer
- name: per_page
in: query
required: false
description: 'Items per page (default: 25, max: 100)'
schema:
type: integer
- name: account_id
in: query
required: false
description: Filter by account ID
schema:
type: string
- name: category_id
in: query
required: false
description: Filter by category ID
schema:
type: string
- name: merchant_id
in: query
required: false
description: Filter by merchant ID
schema:
type: string
- name: start_date
in: query
required: false
description: Filter transactions from this date
schema:
type: string
format: date
- name: end_date
in: query
required: false
description: Filter transactions until this date
schema:
type: string
format: date
- name: min_amount
in: query
required: false
description: Filter by minimum amount
schema:
type: number
- name: max_amount
in: query
required: false
description: Filter by maximum amount
schema:
type: number
- name: type
in: query
required: false
description: Filter by transaction type
schema:
type: string
enum:
- income
- expense
- name: search
in: query
required: false
description: Search by name, notes, or merchant name
schema:
type: string
- name: account_ids
in: query
required: false
description: Filter by multiple account IDs
schema:
type: array
items:
type: string
- name: category_ids
in: query
required: false
description: Filter by multiple category IDs
schema:
type: array
items:
type: string
- name: merchant_ids
in: query
required: false
description: Filter by multiple merchant IDs
schema:
type: array
items:
type: string
- name: tag_ids
in: query
required: false
description: Filter by tag IDs
schema:
type: array
items:
type: string
responses:
'200':
description: transactions filtered by date range
content:
application/json:
schema:
"$ref": "#/components/schemas/TransactionCollection"
post:
summary: Create transaction
tags:
- Transactions
security:
- apiKeyAuth: []
parameters: []
responses:
'201':
description: transaction created
content:
application/json:
schema:
"$ref": "#/components/schemas/Transaction"
'422':
description: validation error - missing required fields
content:
application/json:
schema:
"$ref": "#/components/schemas/ErrorResponse"
requestBody:
content:
application/json:
schema:
type: object
properties:
transaction:
type: object
properties:
account_id:
type: string
format: uuid
description: Account ID (required)
date:
type: string
format: date
description: Transaction date
amount:
type: number
description: Transaction amount
name:
type: string
description: Transaction name/description
description:
type: string
description: Alternative to name field
notes:
type: string
description: Additional notes
currency:
type: string
description: Currency code (defaults to family currency)
category_id:
type: string
format: uuid
description: Category ID
merchant_id:
type: string
format: uuid
description: Merchant ID
nature:
type: string
enum:
- income
- expense
- inflow
- outflow
description: Transaction nature (determines sign)
tag_ids:
type: array
items:
type: string
format: uuid
description: Array of tag IDs
required:
- account_id
- date
- amount
- name
required:
- transaction
required: true
"/api/v1/transactions/{id}":
parameters:
- name: id
in: path
required: true
description: Transaction ID
schema:
type: string
get:
summary: Retrieve a transaction
tags:
- Transactions
security:
- apiKeyAuth: []
responses:
'200':
description: transaction retrieved
content:
application/json:
schema:
"$ref": "#/components/schemas/Transaction"
'404':
description: transaction not found
content:
application/json:
schema:
"$ref": "#/components/schemas/ErrorResponse"
patch:
summary: Update a transaction
tags:
- Transactions
security:
- apiKeyAuth: []
parameters: []
responses:
'200':
description: transaction updated
content:
application/json:
schema:
"$ref": "#/components/schemas/Transaction"
'404':
description: transaction not found
content:
application/json:
schema:
"$ref": "#/components/schemas/ErrorResponse"
requestBody:
content:
application/json:
schema:
type: object
properties:
transaction:
type: object
properties:
date:
type: string
format: date
amount:
type: number
name:
type: string
description:
type: string
description: Alternative to name field
notes:
type: string
currency:
type: string
description: Currency code
category_id:
type: string
format: uuid
merchant_id:
type: string
format: uuid
nature:
type: string
enum:
- income
- expense
- inflow
- outflow
tag_ids:
type: array
items:
type: string
format: uuid
description: Array of tag IDs to assign. Omit to preserve existing
tags; use [] to clear all tags.
required: true
delete:
summary: Delete a transaction
tags:
- Transactions
security:
- apiKeyAuth: []
responses:
'200':
description: transaction deleted
content:
application/json:
schema:
"$ref": "#/components/schemas/DeleteResponse"
'404':
description: transaction not found
content:
application/json:
schema:
"$ref": "#/components/schemas/ErrorResponse"
"/api/v1/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"
'422':
description: validation error - missing date
content:
application/json:
schema:
"$ref": "#/components/schemas/ErrorResponse"
'404':
description: account not found
content:
application/json:
schema:
"$ref": "#/components/schemas/ErrorResponse"
requestBody:
content:
application/json:
schema:
type: object
properties:
valuation:
type: object
properties:
account_id:
type: string
format: uuid
description: Account ID (required)
amount:
type: number
description: Valuation amount (required)
date:
type: string
format: date
description: Valuation date (required)
notes:
type: string
description: Additional notes
required:
- account_id
- amount
- date
required:
- valuation
required: true
"/api/v1/valuations/{id}":
parameters:
- name: 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