Reference

HTTP API

Everything the frontend calls, all same-origin under /api/. Getting those requests to the Python service is the deployment's job: a reverse proxy, or a platform's routing rules; in development next.config.ts proxies them to BACKEND_ORIGIN.

Costs a model call marks the endpoints that reach the model, which is the difference that matters if you are paying per token. Nothing is rate limited: the caps this project used to carry existed for a public demo that no longer does.

Core

MethodPathWhat it does
GET/api/healthResolved provider and model, and where workspaces are kept. Start here when something is wrong.
POST/api/uploadUpload a CSV or Excel file.
POST/api/queryAsk a question about the data. The main one.· costs a model call
POST/api/initialize-dataLoad rows into a workspace without a file upload.

Model-backed

MethodPathWhat it does
POST/api/classify-commandClassify a spreadsheet command. Returns only a validated classification object.· costs a model call
POST/api/orchestrateDecompose a compound request into steps.· costs a model call
POST/api/formulaWrite a spreadsheet formula from a description. Returns the formula; nothing is applied to the sheet.· costs a model call

The model picker

Writable only where control_allowed() says this is not a public deployment, which is what EDI_ALLOW_MODEL_SWITCHING=0 turns off.

MethodPathWhat it does
GET/api/modelsWhat this machine can reach: pulled Ollama models, a signed-in Claude CLI, any provider whose key is already set. Pass refresh=true to probe again rather than reuse the last answer.
POST/api/models/selectChoose the provider and model. Saved next to the workspaces, and it outranks EDI_LLM_PROVIDER.
POST/api/models/keyStore a provider key. It reaches the backend once, going in, and is never returned.
DELETE/api/models/key/{provider}Forget a stored key.
POST/api/models/resetDrop the saved choice and fall back to the environment.

Workspaces and chats

MethodPathWhat it does
POST/api/workspaceCreate a workspace.
POST/api/workspacesSummarise the workspaces whose ids you send. A POST for a read, because the browser supplies the list.
GET/api/workspace/{id}Everything the sheet needs to restore itself.
PUT/api/workspace/{id}Save data, sheet state, chat messages, or the name. Only the fields sent are written.
DELETE/api/workspace/{id}Delete a workspace. Its chats go with it, by cascade.
GET/api/workspace/{id}/chatsChat threads, newest first.
POST/api/workspace/{id}/chatsStart a thread.
GET/api/chats/{id}One thread.
PUT/api/chats/{id}Save messages or rename.

Sheet operations

MethodPathWhat it does
POST/api/workspace/{id}/analyze-insightsProfile the data. Mostly computed; asks the model only for the written summary.· costs a model call
POST/api/workspace/{id}/smart-formatDetect column types and derive formats. Pure Python, no model call.
POST/api/workspace/{id}/quick-data-entryGenerate rows to fill a sheet.· costs a model call
POST/api/workspace/{id}/pivotCross-tabulate the sheet. Returns the whole table as a grid, header row first. Pure pandas, no model call.
POST/api/cancel-operationCancel an in-flight operation.
POST/api/reset-stateClear server-side state.

Health

GET /api/health is the one to reach for when something is not working. It reports what was actually resolved rather than what you meant to configure:

{
  "status": "healthy",
  "llm": "available",
  "llm_config": {
    "provider": "ollama",
    "model": "qwen2.5-coder:7b",
    "configured": true,
    "detail": null
  },
  "store": { "backend": "sqlite", "location": ".edi-data" }
}

llm_config.detail carries the reason when a model could not be built: a missing key, an unnamed model, an uninstalled provider package.

Errors

Refusals come back as JSON with a detail string written for the person who will read it. 413 means the parsed sheet is more data than the grid can hold, which EDI_MAX_DATA_MB sets and Self-hosting explains -- the upload itself is not capped; 503 means no model is configured; 502 means the model was reached but returned something unusable; 403 means the model picker was switched off with EDI_ALLOW_MODEL_SWITCHING=0.

FastAPI's own schema is not routed. It generates one at /openapi.json, with interactive docs at /docs, and both work when you run the backend directly. Behind a proxy that forwards only /api/* they reach the frontend instead, where /docs now redirects to these pages, which is a more confusing answer than a 404. Forward them too if you want them public, and consider whether you do, since they describe every endpoint to anyone who asks.

How it works covers what happens behind /api/query.