VEEMAN's frontend talks to a FastAPI backend over a REST API. This page documents that surface for transparency.
Internal API
This API exists to serve the VEEMAN web app. It is authentication-gated, scoped to your user, and not a public, versioned contract — endpoints may change without notice. There is no public API key programme.
Every request carries a Supabase JWT as Authorization: Bearer <token> (or, in local development, a
backend-signed cookie). The backend verifies it and resolves your user_id; unauthenticated requests are
rejected. See Security.
/api/auth/login/api/auth/logout/api/auth/me/api/analysis/upload/api/analysis/sessions/api/analysis/session/{sid}/api/analysis/session/{sid}/api/analysis/session/{sid}/strategiesorder field lists the strategy names from first row to last./api/analysis/session/{sid}/strategies/{name}/rename/api/analysis/session/{sid}/strategies/{name}/api/analysis/session/{sid}/strategies/deletenames lists them. Deleting a selection is one gesture: one call, one Timeline entry (so a single undo brings them all back), one recomputation. Unknown or repeated names are ignored; an empty list is a no-op. Groups left without members are pruned, as with the single delete./api/analysis/session/{sid}/groups/orderorder lists the group ids from first to last. It moves whole sleeves and never touches the strategies' own order. Tolerant: unknown ids are ignored, groups you leave out keep their current relative position at the end./api/analysis/session/{sid}/groups/{gid}/mergename defaults to the group's name; consolidate: false keeps the group and adds the new leg muted (active it would count the same P/L twice), consolidate: true removes the merged members and activates the new leg. Only visible members are merged — muted ones are never deleted. One Timeline entry for the whole gesture. 422 if the group is unknown or has nothing active./api/analysis/session/{sid}Every session is mirrored to a draft continuously; GET /session/{sid} rebuilds a session from its
draft automatically when it is no longer in memory, so a client that keeps its session id needs none
of these. They exist for the case where that id is gone (another browser, cleared storage). See
Core concepts.
/api/analysis/session/{sid}/draftsaved_at); also returned as draft_saved_at on the session payload./api/analysis/drafts/api/analysis/drafts/{sid}/resumesession_id)./api/analysis/drafts/{sid}404 and 503 mean different things on a session route. 404 is a verdict: no such
session, and no draft to rebuild it from — a client holding that id should drop it. 503
(with Retry-After) means the opposite: the draft exists, but storage could not be reached
just now. Keep the session id and retry — discarding it on a 503 would throw away the
only pointer to work that is still there. A single failed read is retried internally, so a
503 only surfaces when storage is unavailable for longer than one attempt.
Restoring a draft never counts against the per-user session limit: reopening your own work is not the same as starting something new.
/api/analysis/session/{sid}/equity/api/analysis/session/{sid}/metricswindow_from/window_to (inclusive ISO days) + rebase scope them to the framed period./api/analysis/session/{sid}/rolling/api/analysis/session/{sid}/allocation/api/analysis/session/{sid}/monthlywindow_from/window_to/rebase as the metrics, so the grid and the ledger describe the same period./api/analysis/session/{sid}/strategies/metricswindow_from/window_to/rebase: each leg is clipped and re-anchored with its own factor, returned alongside as window_factors./api/analysis/session/{sid}/weekday/{name}/statswindow_from/window_to clip them to the framed period (no rebase: one-contract dollars)./api/analysis/session/{sid}/weekday/{name}/series/api/analysis/session/{sid}/montecarloblock_size weekly/monthly/quarterly, block bootstrap only)./api/analysis/session/{sid}/equity-controljob_id./api/analysis/session/{sid}/equity-control/{job_id}running / done / error)./api/analysis/session/{sid}/walkforwardjob_id. Legacy module: retired from the UI, still served for direct callers./api/analysis/session/{sid}/walkforward/{job_id}running / done / error). Legacy./api/analysis/session/{sid}/correlation/api/analysis/session/{sid}/timeline/api/analysis/session/{sid}/timeline/window{node_id: metrics}), each recomputed from its own recipe with the engine that computes the live session. Takes the same window_from/window_to/rebase as the metrics; {} without a window, where the frozen numbers already are the view./api/analysis/session/{sid}/timeline/undo/redo) — rewrites the live session and returns it with the new history state./api/analysis/session/{sid}/timeline/recallseq or node_id, abandoned branches included.History is in-memory and volatile by design — see History.
/api/analysis/portfolio/saved/api/analysis/strategies/saved/api/analysis/variants/api/analysis/variants/{vid}/scratch/session/{sid}/… routes (also DELETE to close it)./api/analysis/variants/{vid}/commit/api/analysis/variants/{vid}/window/api/analysis/proporzionatore/runjob_id./api/analysis/proporzionatore/montecarlojob_id./api/analysis/proporzionatore/jobs/{job_id}running / done / error).Naming
In the codebase and API these endpoints are still called proporzionatore. They power the
standalone Risk Sizing engine, retained as legacy — the same risk/margin engine now runs inline in the
workspace as per-strategy sizing.
Every route above is scope-aware: send the header X-Veeman-Space: {space_id} and
it reads and writes inside that space instead of your personal library. Without the header nothing
changes. Only shared kinds follow the header — drafts, settings and jobs stay personal in every case.
/api/spaces/api/spaces/{id}/api/spaces/{id}/invites/{token} to revoke, POST /{token}/accept to join)./api/spaces/{id}/members/{uid}/api/spaces/{id}/transfer/api/spaces/{id}/copy-incopy-out for the reverse)./api/spaces/{id}/activity/api/spaces/{id}/comments/{kind}/{name}/comment-counts returns them all at once./api/spaces/{id}/lease/{kind}/{name}/api/versions/{kind}/{name}/api/versions/{kind}/{name}/{rev}/api/versions/{kind}/{name}/{rev}/restore/api/versionsIf-Match, or the last writer wins
Inside a space, send If-Match: {rev} with the revision you started from on any
update. Without it a late save silently overwrites whoever saved in between. With it the server answers
409 carrying the current revision and its author, so the client can offer Reload or Save as a copy.
An optional X-Veeman-Note header becomes that version's message.
Simulations run as background jobs
Both Risk Sizing simulations (the run and its Monte Carlo) execute on the server, decoupled
from the HTTP request: the POST returns a job_id immediately and the client polls
the jobs endpoint. The run keeps going while your phone screen is off or the tab is in the
background, and the recipe is persisted so a mid-run backend restart re-runs it transparently.
The session Monte Carlo simulation works the same way.