marinAI — API Reference
All business endpoints live under /v1 and require Keycloak authentication. The only public endpoint is the health check. Request and response shapes are defined by the backend data model.
API Reference (v1)
All business endpoints require authentication. The only public endpoint is the health check.
Health
GET /v1/Response:
{
"message": "Parametric Propeller API - running",
"supported_versions": ["/v1"]
}Default curves
GET /v1/curves/initReturns an object with six entries:
{
"chord": {
"control": [{"x": 0.25, "y": 0.03}],
"curve": [{"x": 0.25, "y": 0.03}]
}
}The frontend uses this both on initial load and for reset.
The response model also permits optional num_blades and diameter metadata in the frontend type, but the current backend response does not provide them. The frontend therefore retains its existing blade-count and diameter values after reset.
Curve upload and fitting
POST /v1/curves/upload
Content-Type: multipart/form-dataFields:
csv_file: required project-table CSVcp_file: optional control-point CSV
Behavior:
- Rejects a missing or non-
.csvproject file with 400. - Rejects a non-
.csvcontrol-point file with 400. - Uses uploaded control points directly where supplied.
- Fits cubic Bézier curves elsewhere.
- Converts processing failures into HTTP 500.
- Returns radii, raw data, fitted control points, interpolated curves, and RMSE values.
Curve recomputation
POST /v1/curves/{dataset}
Content-Type: application/jsonRequest:
{
"controlPoints": [
{"x": 0.25, "y": 0.03}
]
}Response:
{
"curvePoints": [
{"x": 0.25, "y": 0.03}
]
}The {dataset} path variable is validated against PLOT_KEYS: chord, pitch, rake, skew, camber, thickness. An unknown name returns HTTP 404 with the list of valid parameters, so /v1/curves/anything no longer behaves like /v1/curves/chord. The interpolation itself is the same regardless of dataset (the math only depends on the control points).
CSV-to-IGES propeller generation
POST /v1/propeller/iges
Content-Type: multipart/form-dataFields:
csv_file: required global-properties CSVstart_r: default0.25end_r: default1.0filename: defaultblade.iges
Validation:
- CSV filename is required and must end in
.csv. - Radial bounds must satisfy
0.0 <= start_r < end_r. - Output names without
.igsor.igesreceive.iges.
InvalidGeometryError is allowed to propagate to the 422 handler. Other failures become HTTP 500.
Parametric STL generation
POST /v1/propeller/stl
Content-Type: application/jsonThe request is a full ParametricInput object. The endpoint always uses the backend’s fixed 12-station RADII, not the client’s n_sections.
Successful response:
{
"stl_url": "/v1/exports/<uuid>.stl"
}Parametric IGES generation
POST /v1/propeller/parametric/iges
Content-Type: application/jsonThe request is again a full ParametricInput object. Successful response:
{
"iges_url": "/v1/exports/<uuid>.iges"
}The backend README endpoint table does not yet list this newer endpoint.
Export download
GET /v1/exports/{filename}The endpoint serves STL, IGES/IGS, or generic files from EXPORT_DIR (default /tmp/exports). Missing files return 404.
Section table
POST /v1/sections
Content-Type: application/jsonThe request is a ParametricInput object. The response depends on include_control_points:
false: CSV namedtabella_progetto.csvtrue: ZIP namedtabella_progetto.zip, containing:tabella_progetto.csvcontrol_points.csv
The section table uses a dynamically generated np.linspace(start_r, end_r, n_sections) range, unlike the fixed STL radii.
Control-point CSV columns are:
parameter,cp_index,cp_x,cp_yThree-dimensional section points
POST /v1/sections/points3d
Content-Type: application/jsonThe endpoint:
- Builds the parametric blade.
- Calls
apply_transformations. - Writes upper and lower points for each section.
- Uses the corrected true section radius in the first column.
CSV columns are:
radius,side,index,x,y,zside is either upper or lower. Coordinate rows are emitted tip-first because BladeX stores them tip-to-hub.
Error behavior
| Situation | Current HTTP status |
|---|---|
| Missing bearer credentials | 403 from FastAPI’s HTTPBearer behavior |
| Invalid/expired bearer token | 401 |
Token without sub | 401 |
| Pydantic request validation failure | 422 |
| Negative camber/thickness geometry | 422 |
| Missing export file | 404 |
Unknown {dataset} on curve update | 404 |
| Invalid upload extension or radial bounds | 400 |
| Unhandled CSV or geometry-processing failure | 500 |
The distinction between a missing token and an invalid token matters. The frontend’s retry logic is designed around receiving 401 for supplied-token problems.
Authentication (Keycloak)
Configuration
The backend reads these variables from the process environment or local .env:
| Variable | Purpose |
|---|---|
KEYCLOAK_URL | Keycloak server base URL |
KEYCLOAK_REALM | Realm issuing access tokens |
KEYCLOAK_CLIENT_ID | Client ID used only when audience verification is enabled |
KEYCLOAK_VERIFY_AUDIENCE | true or false; default false |
A local .env file exists but is ignored by Git. Only .env.example is tracked. The example supplies no credential values.
Import-time behavior is strict:
- Missing
KEYCLOAK_URLorKEYCLOAK_REALMraisesRuntimeError. KEYCLOAK_VERIFY_AUDIENCE=truewithoutKEYCLOAK_CLIENT_IDraisesRuntimeError.
This means the backend intentionally refuses to start without usable Keycloak configuration.
Verification model
core/auth/deps.py:
- Loads dotenv configuration.
- Builds the issuer as
{KEYCLOAK_URL}/realms/{KEYCLOAK_REALM}. - Builds the JWKS URL as
{issuer}/protocol/openid-connect/certs. - Uses FastAPI
HTTPBearer. - Uses PyJWT
PyJWKClientwith cached keys and a one-hour lifespan. - Decodes bearer tokens using RS256.
- Verifies issuer always.
- Verifies audience only when explicitly enabled.
- Requires a
subclaim. - Returns a typed
UserPayload. - Converts other token problems to HTTP 401.
UserPayload permits unknown extra claims through Pydantic extra="allow", while requiring sub and exp and optionally carrying preferred_username, email, and aud.
Router-level dependencies protect:
routers/v1/curves.pyrouters/v1/exports.pyrouters/v1/sections.py
Only routers/v1/health.py remains public.
The backend never needs KEYCLOAK_CLIENT_SECRET. It validates presented access tokens using public realm keys; it does not exchange authorization codes or refresh tokens.
CSV Formats
Project-table CSV
Expected headers:
Radii (adim),Chord lengths,Skew angles,Pitch,Rake,Camber,ThicknessSample project tables include Fincantieri-style and GBI-style datasets. The parser expects exact header strings and converts every field to floating-point numbers.
Control-point CSV
Expected headers:
parameter,cp_index,cp_x,cp_yUnknown parameters are ignored. Indexes are sorted per parameter. If a property has override points, its RMSE is reported as zero.
Exported section CSV
Generated headers are:
Radii (adim),Chord lengths,Skew angles,Pitch,Rake,Camber,ThicknessValues are formatted with %.15g.
Profile-coordinate CSV
sections_properties.csv contains literal Python-style coordinate arrays in quoted fields:
Radii (adim),X upper coordinates,Y upper coordinates,X lower coordinates,Y lower coordinatesThe legacy reader (read_sections_properties) parses those fields with ast.literal_eval, scales them by chord, constructs custom profiles, and normalizes chord length. This format is offline-only; the live API does not accept it.
Global-properties sample caveat
The checked-in data/csv/global_properties.csv currently contains only:
Radii (adim),Chord lengths,Pitch,Rake,Skew anglesIt does not contain the Camber and Thickness columns required by read_global_properties / read_project_csv. Consequently, that specific sample cannot be used directly with the CSV-to-IGES global-properties reader or the project-curve fitter in its present form — use tabella_progetto_FC.csv or tabella_progetto_GBI.csv (canonical 7-column headers) instead.
Control-point JSON format
Offline control-point JSON (save/load_control_points) uses keys such as:
chord_x
chord_y
chord_mapping
pitch_x
pitch_y
...Mappings identify composite Bézier segments. C1 continuity across segments is not automatic and must be enforced manually.
load_control_points currently expects all six standard properties to be present. A JSON file missing one of those keys raises a key error. The live POST /v1/curves/{dataset} API uses CurveRequest{controlPoints} with no mapping field — composite mappings are offline-only.
