API Reference
Agent onboarding and SQL API reference for programmatic access to your financial data.
Overview
Expense Budget Tracker exposes one public machine API at:
https://api.expense-budget-tracker.com/v1/
You can use that same surface in two ways:
- Agent-native onboarding starting from
GET /v1/ - Direct HTTP usage with an existing long-lived ApiKey
All requests use the same Postgres Row Level Security enforcement as the web app.
If your client speaks MCP, use the hosted MCP connector at https://mcp.expense-budget-tracker.com/mcp and authorize it through browser OAuth. This page covers the separate Agent API onboarding and direct HTTP contract that use a long-lived ApiKey.
Discovery, source, and runtime schema
Start here:
https://api.expense-budget-tracker.com/v1/
The discovery response tells agents how to bootstrap auth and what to call next. It also links to the README and implementation source. The API exposes:
GET /v1/schemaGET /v1/openapi.jsonandGET /v1/swagger.jsonas compatibility probes that report OpenAPI is unavailable and point clients back to discovery and source
Use schema when you need the exact list of allowed relations and columns exposed by /v1/sql/query, /v1/sql/execute, and compatibility /v1/sql.
Agent-native onboarding
If you want Claude Code, Codex, OpenClaw, or another agent to connect itself, start with the discovery endpoint and follow the actions returned by the server.
Auth flow
GET https://api.expense-budget-tracker.com/v1/- Read the returned
send_codeaction andbootstrapUrl POSTthe user email tohttps://auth.expense-budget-tracker.com/api/agent/send-code- Receive
otpSessionToken - Ask the user for the 8-digit code from email
POSTcode,otpSessionToken, andlabeltohttps://auth.expense-budget-tracker.com/api/agent/verify-code- Receive a long-lived
ApiKey - Save that key outside chat memory
GET https://api.expense-budget-tracker.com/v1/meGET https://api.expense-budget-tracker.com/v1/workspaces- Optionally
POST https://api.expense-budget-tracker.com/v1/workspacesto create a workspace POST https://api.expense-budget-tracker.com/v1/workspaces/{workspaceId}/selectGET https://api.expense-budget-tracker.com/v1/schema- Read with
POST https://api.expense-budget-tracker.com/v1/sql/queryand send approved writes withPOST https://api.expense-budget-tracker.com/v1/sql/execute
Auth header
Authorization: ApiKey <key>
Workspace handling
POST /v1/workspaces/{workspaceId}/selectsaves the default workspace for that API key- after a workspace is saved,
/v1/sql/query,/v1/sql/execute, and compatibility/v1/sqlcan omitX-Workspace-Id X-Workspace-Id: <workspaceId>is still supported when you want to override the saved workspace for one request- if the user has exactly one workspace and the key has no saved selection yet, the API auto-saves and uses that workspace
For a step-by-step human guide, see AI Agent Setup.
Direct HTTP usage with an existing key
Scripts, cron jobs, dashboards, and custom apps can call the same API directly once they already have a long-lived ApiKey.
Authentication
Pass the key as an ApiKey auth header:
curl -X POST https://api.expense-budget-tracker.com/v1/sql/query \
-H "Authorization: ApiKey ebta_your_key_here" \
-H "X-Workspace-Id: workspace-id" \
-H "Content-Type: application/json" \
-d '{"sql": "SELECT * FROM ledger_entries ORDER BY ts DESC LIMIT 10"}'
X-Workspace-Id is required only if the key does not already have a saved default workspace or if you want to override the saved workspace for that request.
Authorization: ApiKey ebta_your_key_hereX-Workspace-Id: <workspaceId>when needed
Endpoint summary
GET /v1/— public discovery documentGET /v1/openapi.jsonandGET /v1/swagger.json— source-discovery compatibility probes, not API specsGET /v1/me— authenticated account contextGET /v1/workspaces— list workspaces available to the key ownerPOST /v1/workspaces— create a workspacePOST /v1/workspaces/{workspaceId}/select— save the default workspace for this keyGET /v1/schema— inspect allowed relations and columns for SQLPOST /v1/sql/query— run one read-onlySELECTorWITH ... SELECTstatementPOST /v1/sql/execute— run one approvedINSERT,UPDATE, orDELETEstatementPOST /v1/sql— compatibility endpoint for atomic multi-statement scripts
SQL policy
The primary endpoints deliberately separate reads from writes:
POST /v1/sql/queryaccepts exactly one read-onlySELECTorWITH ... SELECTstatementPOST /v1/sql/executeaccepts exactly oneINSERT,UPDATE, orDELETEmutation, including supportedWITHforms- compatibility
POST /v1/sqlaccepts restricted multi-statement scripts and applies them atomically; use it only when that atomic script behavior is required
Blocked or rejected patterns:
- multiple statements on the primary
/v1/sql/queryand/v1/sql/executeendpoints - DDL such as
CREATE,DROP, andALTER - transaction wrappers such as
BEGIN,COMMIT, andROLLBACK set_config()- SQL comments
- quoted identifiers
- dollar-quoted strings
The server also restricts which relations can be queried. Use /v1/schema to inspect the exposed relations and columns before generating SQL.
Currently exposed relations:
ledger_entriesbudget_linesworkspace_settingsaccount_metadataaccounts(read-only)fx_rates_raw(read-only)fx_rates_daily(read-only)
Limits
- 100 returned rows per request
- 100 affected rows per mutation statement and request
- 25-second total SQL request deadline
- 10 requests/second, 10,000 requests/day per key
Security
- API keys are stored as SHA-256 hashes (plaintext never persisted)
- RLS enforces workspace isolation at the database level
- Keys can be revoked from the product at any time
- Removing a workspace member auto-revokes all their keys