Moneybook API
Work with accounts, transactions, imports and workspaces through the API. The OpenAPI contract defines request and response schemas, permissions and error codes.
Servers
Local development
http://localhost:8080Production
https://spective-backend-1053388195102.asia-south1.run.app
Authentication and access
API contract for Moneybook personal finance platform. Import filenames must be basenames without separators, traversal segments or control characters; invalid names return 400 IMPORT_INVALID_FILENAME. Resource limits: multipart files are limited to 10 MiB each and requests to 20 MiB (SOR documents retain their stricter 5 MiB domain limit). Oversized multipart requests return 413 with UPLOAD_TOO_LARGE (SOR routes use SOR_DOCUMENT_TOO_LARGE). Authenticated /v1 requests have an instance-local fixed-window limit, default 120/minute per verified identity; excess requests return 429 with RATE_LIMITED and Retry-After seconds. Health probes and the separately authenticated scheduler route are excluded. Unexpected server failures return INTERNAL_ERROR with a generic message; internal exception details, database queries, paths and credentials are never returned. Authentication: every operation except health needs either a Supabase Auth access token (`Authorization: Bearer <jwt>`, verified against the project's JWKS, issuer `<SUPABASE_URL>/auth/v1`, audience `authenticated`) or a Moneybook API key (`X-Spective-Api-Key`). Client-supplied identity headers are ignored. Workspaces: data is owned by a workspace. Workspace-scoped operations act on the workspace named by the `X-Spective-Workspace-Id` header (or the `workspaceId` path parameter) and default to the caller's personal workspace. The caller must be an active member; a workspace the caller cannot see answers 404 `WORKSPACE_NOT_FOUND`, a role without the needed permission answers 403 `WORKSPACE_ACCESS_DENIED`. Roles: OWNER (everything, manages members), EDITOR (read, write), AUDITOR (read, export, audit trail), VIEWER (read). GET operations need READ, other methods need WRITE unless stated. API keys are pinned to the workspace they were created in.
Operations
POST /internal/jobs/dispatch
Disabled by default. Requires Google-signed OIDC token from the exact configured scheduler service account, exact audience, verified email and Google issuer. Supabase tokens and API keys are rejected. Claims at most one durable job with the same outcomes as administrator dispatch.
POST /v1/admin/jobs/dispatch
ADMIN bearer-only bounded dispatch of at most one durable job. Disabled unless durable mode and dispatch are enabled. Claims are atomic; repeated requests cannot execute an already claimed job. Expired claims become UNCERTAIN and require operator reconciliation, never automatic replay of financial or notification effects.
POST /v1/sor/statements
Persist immutable statement extraction; verify source ownership, arithmetic and running balances. No ledger writes yet.
GET /v1/sor/statements/{id}
Feature-flagged owner-scoped SOR operation. Requires sor:read or sor:write as appropriate.
POST /v1/sor/statements/{id}/commit
Atomic ledger/provenance/audit commit. Reject stale ledger, overlapping periods and opening mismatch. No guessed opening adjustment or external jobs.
POST /v1/sor/statements/{id}/discard
Feature-flagged owner-scoped SOR operation. Requires sor:read or sor:write as appropriate.
POST /v1/sor/positions
Create immutable owned asset/liability evidence; no guessed inventory or ownership. Same key/payload replays.
GET /v1/sor/positions
Bounded owner-scoped immutable inventory; cursor is the last returned ID. Requires sor:read or sor:write.
GET /v1/sor/positions/{id}
Feature-flagged owner-scoped SOR operation. Requires sor:read or sor:write as appropriate.
POST /v1/sor/valuations
Append dated value evidence. Server records observation time; no provider fetch or FX assumption.
GET /v1/sor/net-worth
Feature-flagged owner-scoped SOR operation. Requires sor:read or sor:write as appropriate.
POST /v1/sor/allocations
Link part of an existing payment/refund to committed bill evidence. Bounds both payment and bill capacity; never posts spend twice.
POST /v1/sor/allocations/{id}/reverse
Feature-flagged owner-scoped SOR operation. Requires sor:read or sor:write as appropriate.
GET /v1/sor/bills/{id}/settlement
Feature-flagged owner-scoped SOR operation. Requires sor:read or sor:write as appropriate.
POST /v1/sor/documents
Experimental feature flag required. Stores up to 5 MiB locally in PostgreSQL; no external extraction.
POST /v1/sor/bills
Validate and persist an immutable draft. Same owner/key/content replays; changed content conflicts. Does not create ledger spend.
GET /v1/sor/bills/{id}
getSorBill
POST /v1/sor/bills/{id}/commit
Confirm the immutable bill evidence and append an audit event atomically. Replays safely; never posts duplicate spending. Payment allocation is a future operation.
POST /v1/sor/classifications
Persist a semantic category proposal with original category and evidence. Does not apply it.
POST /v1/sor/classifications/{id}/{action}
Atomically accept or revert a semantic category assignment, preserving source and audit evidence. Conflicts if the current category changed. Explicitly accepted assignments take precedence over automatic enrichment; new proposals alone never replace them. Reassignment uses a new proposal with the current expectedCategory and explicit acceptance.
GET /v1/wealth/positions
listWealthPosition
POST /v1/wealth/positions
createWealthPosition
GET /v1/wealth/positions/{id}
getWealthPosition
PUT /v1/wealth/positions/{id}
updateWealthPosition
DELETE /v1/wealth/positions/{id}
archiveWealthPosition
GET /v1/wealth/compensation
listWealthCompensation
POST /v1/wealth/compensation
createWealthCompensation
GET /v1/wealth/compensation/{id}
getWealthCompensation
PUT /v1/wealth/compensation/{id}
updateWealthCompensation
DELETE /v1/wealth/compensation/{id}
archiveWealthCompensation
GET /v1/wealth/events
listWealthEvent
POST /v1/wealth/events
createWealthEvent
GET /v1/wealth/events/{id}
getWealthEvent
PUT /v1/wealth/events/{id}
updateWealthEvent
DELETE /v1/wealth/events/{id}
archiveWealthEvent
GET /v1/wealth/scenarios
listWealthScenario
POST /v1/wealth/scenarios
createWealthScenario
GET /v1/wealth/scenarios/{id}
getWealthScenario
PUT /v1/wealth/scenarios/{id}
updateWealthScenario
DELETE /v1/wealth/scenarios/{id}
archiveWealthScenario
GET /v1/wealth/allocations
listWealthAllocation
POST /v1/wealth/allocations
createWealthAllocation
GET /v1/wealth/allocations/{id}
getWealthAllocation
PUT /v1/wealth/allocations/{id}
updateWealthAllocation
DELETE /v1/wealth/allocations/{id}
archiveWealthAllocation
GET /v1/wealth/positions/{id}/valuations
listWealthValuations
POST /v1/wealth/positions/{id}/valuations
appendWealthValuation
GET /v1/wealth/dashboard
getWealthDashboard
POST /v1/wealth/projections
calculateWealthProjection
GET /v1/wealth/projections/{id}
getWealthProjection
GET /v1/health
getHealth
GET /v1/auth/me
Resolves the Supabase identity to the Moneybook user (linking or creating it on first sign-in) and returns it with the personal workspace.
GET /v1/workspaces
Workspaces the caller is an active member of, personal first.
GET /v1/workspaces/{workspaceId}
getWorkspace
GET /v1/workspaces/{workspaceId}/export
OWNER or AUDITOR only. Download a ZIP containing a versioned financial JSON snapshot and original source files. Credentials and statement passwords are excluded. Export fails without returning a partial archive if a source file cannot be read. Maximum archive payload 100 MiB.
POST /v1/workspaces/{workspaceId}/offboarding
Active OWNER bearer session only. Confirmation must equal the workspace id. Deletes all workspace financial rows, memberships, invitations and API keys atomically, retaining only a minimal deletion receipt. Original files are queued for idempotent deletion by the authenticated job dispatcher. The personal workspace is replaced empty on next sign-in; shared workspaces disappear. This does not delete the Supabase identity or other workspaces. Requires exporting first if a copy is wanted; deletion cannot be undone through the app.
GET /v1/workspaces/{workspaceId}/members
Any member may list members.
PATCH /v1/workspaces/{workspaceId}/members/{userId}
OWNER only. Changes role and optional access expiry. Membership changes are serialized per workspace so concurrent requests cannot remove the last active owner. The personal workspace's creator always stays owner. API keys cannot manage membership.
DELETE /v1/workspaces/{workspaceId}/members/{userId}
OWNER user session only; API keys cannot manage membership. The last active owner and the personal workspace's creator cannot be removed.
POST /v1/workspaces/{workspaceId}/leave
Leave a workspace you were invited to. You cannot leave your own personal workspace or leave it without an owner. API keys cannot leave workspaces.
GET /v1/workspaces/{workspaceId}/invites
OWNER only.
POST /v1/workspaces/{workspaceId}/invites
OWNER user session only; API keys cannot manage membership. Creates a pending invite for an email address (valid 14 days). An expired invitation or access grant may be reissued. No email is sent by the API; the invitee sees it under /v1/invites after signing in with that verified email.
DELETE /v1/workspaces/{workspaceId}/invites/{inviteId}
OWNER only. Revokes a pending invite.
GET /v1/workspaces/{workspaceId}/audit-events
Membership and access audit trail. Needs AUDIT (OWNER, AUDITOR).
GET /v1/invites
Pending, unexpired invites addressed to the caller's verified email.
POST /v1/invites/{inviteId}/accept
Accept an invite addressed to the caller's verified email. Both the invitation and any granted access expiry must still be valid. API keys cannot accept invites.
GET /v1/accounts
listAccounts
POST /v1/accounts
createAccount
GET /v1/accounts/{accountId}
getAccount
PATCH /v1/accounts/{accountId}
updateAccount
POST /v1/accounts/{accountId}
POST alias for clients or local environments where PATCH transport is unavailable.
DELETE /v1/accounts/{accountId}
Delete an account and remove all linked transactions, import sessions, import records, and import batches for the authenticated user.
GET /v1/currencies
listEnabledCurrencies
GET /v1/categories
listCategories
POST /v1/categories
createCategory
PATCH /v1/categories/{categoryId}
updateCategory
DELETE /v1/categories/{categoryId}
deleteCategory
GET /v1/imports
listImportSessions
POST /v1/imports
startImport
POST /v1/imports/preview
previewImport
POST /v1/imports/bulk-preview
Validate multiple uploaded statement files with one shared account, format, parser preset, and optional PDF password. Each file creates its own staged import session when accepted.
GET /v1/import-batches
listImportBatches
POST /v1/import-batches/preview
Validate multiple same-format uploaded statement files as one persistent import batch. Each accepted file becomes a child import session retained for source preview and final confirmation.
GET /v1/import-batches/{batchId}
getImportBatch
DELETE /v1/import-batches/{batchId}
Delete an uncommitted import batch and its staged child sessions. Batches with committed transactions cannot be deleted.
GET /v1/import-batches/{batchId}/detail
getImportBatchDetail
POST /v1/import-batches/{batchId}/files
Add more same-format files to an existing staged import batch. Files use the batch account, format, and parser preset. Completed batches are immutable and reject additional files.
POST /v1/import-batches/{batchId}/confirm
confirmImportBatch
POST /v1/import-batches/{batchId}/review
Complete a review-only batch without creating ledger transactions. Intended for batches where every child session has no importable rows.
DELETE /v1/import-batches/{batchId}/sessions/{sessionId}
Remove a staged, uncommitted child session from an import batch.
POST /v1/imports/table-preview
Extract candidate table columns and rows from an upload so users can build parser mappings without writing regex.
GET /v1/imports/{sessionId}
getImportSession
POST /v1/imports/{sessionId}/confirm
confirmImport
POST /v1/imports/{sessionId}/review
Mark a review-only import as completed without creating ledger transactions. Intended for files that were retained for audit/preview but produced no importable rows.
GET /v1/imports/{sessionId}/asset
Return the stored source asset for an import session so users can preview the original file while reviewing parser results.
POST /v1/imports/{sessionId}/asset-preview
Return a browser-previewable source asset. For password-protected PDFs, the supplied one-time password or the account saved password may be used to return an unlocked PDF preview.
GET /v1/imports/{sessionId}/detail
getImportSessionDetail
POST /v1/imports/{sessionId}/corrections
Apply review-time row edits to an uncommitted import session. Completed sessions are immutable and reject corrections.
GET /v1/ops/logs/stream
ADMIN-only development heartbeat stream. Disabled in production; local connections close after 60 seconds.
GET /v1/transactions
listTransactions
POST /v1/transactions
createTransaction
GET /v1/transactions/export
CSV export of the active workspace's ledger. Needs EXPORT (OWNER, AUDITOR); EDITOR and VIEWER get 403.
POST /v1/transactions/bulk
bulkCreateTransactions
GET /v1/transactions/inter-account/link-candidates
Scan for proposed inter-account transfer pairs
GET /v1/transactions/{transactionId}
getTransaction
PATCH /v1/transactions/{transactionId}
updateTransaction
POST /v1/transactions/{transactionId}
POST alias for clients or local environments where PATCH transport is unavailable.
DELETE /v1/transactions/{transactionId}
deleteTransaction
GET /v1/transactions/{transactionId}/inter-account-suggestions
Suggest counterpart transactions for an inter-account transfer link
POST /v1/transactions/{transactionId}/inter-account-link
Link two transactions as an inter-account transfer
DELETE /v1/transactions/{transactionId}/inter-account-link
Remove inter-account transfer link for this transaction
GET /v1/goals
listGoals
POST /v1/goals
createGoal
GET /v1/goals/{goalId}
getGoal
PATCH /v1/goals/{goalId}
updateGoal
DELETE /v1/goals/{goalId}
deleteGoal
GET /v1/insights
listInsights
POST /v1/insights
triggerInsightGeneration
GET /v1/insights/report
getInsightReport
GET /v1/jobs
Latest 200 jobs owned by the active authorized workspace. Unowned historical and global campaign records are excluded. Messages are controlled summaries, never raw exception text.
GET /v1/jobs/{jobId}
Active-workspace job only; foreign or unowned identifiers return 404.
GET /v1/notifications/preferences
getNotificationPreferences
PUT /v1/notifications/preferences
upsertNotificationPreferences
GET /v1/notifications/history
listNotificationHistory
GET /v1/settings/preferences
getUserPreferences
PATCH /v1/settings/preferences
updateUserPreferences
POST /v1/settings/preferences
POST alias for clients or local environments where PATCH transport is unavailable.
POST /v1/settings/reset-data
OWNER only. Deletes all data in the active workspace plus the caller's own notification and preference state.
GET /v1/settings/api-keys
listApiKeys
POST /v1/settings/api-keys
createApiKey
GET /v1/settings/api-keys/{apiKeyId}
getApiKey
PATCH /v1/settings/api-keys/{apiKeyId}
updateApiKey
DELETE /v1/settings/api-keys/{apiKeyId}
revokeApiKey
POST /v1/settings/api-keys/{apiKeyId}/rotate
rotateApiKey
GET /v1/admin/users
Platform admins only (Supabase `app_metadata.role = admin`). Users are created by signing in through Supabase Auth.
PATCH /v1/admin/users/{userId}
Activate or deactivate a user. Roles and credentials are managed in Supabase Auth.
GET /v1/admin/notification-templates
listNotificationTemplates
POST /v1/admin/notification-templates
createNotificationTemplate
GET /v1/admin/notification-templates/{templateId}
getNotificationTemplate
PATCH /v1/admin/notification-templates/{templateId}
updateNotificationTemplate
DELETE /v1/admin/notification-templates/{templateId}
deleteNotificationTemplate
GET /v1/admin/notification-rules
listNotificationRules
POST /v1/admin/notification-rules
createNotificationRule
GET /v1/admin/notification-rules/{ruleId}
getNotificationRule
PATCH /v1/admin/notification-rules/{ruleId}
updateNotificationRule
DELETE /v1/admin/notification-rules/{ruleId}
deleteNotificationRule
POST /v1/admin/notification-campaigns/{campaignId}/trigger
triggerNotificationCampaign
GET /v1/admin/db/parser-presets
listParserPresets
POST /v1/admin/db/parser-presets
createParserPreset
GET /v1/admin/db/parser-presets/{presetId}
getParserPreset
PATCH /v1/admin/db/parser-presets/{presetId}
updateParserPreset
DELETE /v1/admin/db/parser-presets/{presetId}
deleteParserPreset
GET /v1/admin/currencies
listCurrencies
PATCH /v1/admin/currencies/{currencyCode}
updateCurrency
GET /v1/admin/categories
listMasterCategories
POST /v1/admin/categories
createMasterCategory
PATCH /v1/admin/categories/{categoryId}
updateMasterCategory
DELETE /v1/admin/categories/{categoryId}
deleteMasterCategory
GET /v1/admin/db/tables
listDatabaseTables
GET /v1/admin/db/tables/{table}/rows
Active verified ADMIN only. Credential/password/token fields, provider configuration, historical job messages and binary cells are redacted. Page must be nonnegative; size is 1..200.
POST /v1/admin/db/tables/{table}/rows
Insert a row (active verified ADMIN; schema validated and audited)
PATCH /v1/admin/db/tables/{table}/rows
Patch a row (ADMIN; single-row match enforced)
POST /v1/admin/db/tables/{table}/delete
Delete a row (ADMIN; single-row match enforced)
GET /v1/fx-rates
listFxRates