Files
sure/docs/api/openapi.yaml
ghost da42423475 feat(api): accept Sure NDJSON imports (#1601)
* feat(api): accept Sure NDJSON imports

* fix(api): preserve uploaded Sure imports on publish errors

* fix(api): reset preserved Sure imports after enqueue failure

* fix(api): tighten Sure import upload handling

* test(api): align import API key fixtures

* docs(api): document import publish failure IDs
2026-05-01 22:56:18 +02:00

3934 lines
106 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
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"
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
properties:
rows_count:
type: integer
minimum: 0
valid_rows_count:
type: integer
minimum: 0
nullable: true
ImportSummary:
type: object
required:
- id
- type
- status
- created_at
- updated_at
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
ImportDetail:
type: object
required:
- id
- type
- status
- created_at
- updated_at
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
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
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/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. Requires admin role.
security:
- apiKeyAuth: []
responses:
'200':
description: account reset initiated
content:
application/json:
schema:
"$ref": "#/components/schemas/SuccessMessage"
'401':
description: unauthorized
'403':
description: forbidden - requires read_write scope and admin role
"/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