API Reference
Version: 1.0.0
MangoGridStudio exposes two API services:
- FastAPI Backend (
http://localhost:58000) — authentication, analyst workflows, scenario management, AI chat, model builder, power flow validation, and user management. All routes listed here are under this base URL unless stated otherwise. - Julia Solver API (
http://localhost:8082) — high-performance solver execution for UC, ED, SCUC, SCED, sensitivity analysis, and case management. Accessed via the/api/modules/analyst/solve/*proxy routes below.
Request conventions
Authentication header
All endpoints require a Bearer token except where noted as No auth:
Authorization: Bearer <access_token>
Obtain a token via POST /api/auth/login. Use POST /api/auth/refresh to extend.
During local development with AUTH_BYPASS_ENABLED=True, any token value is accepted:
curl -H "Authorization: Bearer dev-bypass-token" http://localhost:58000/health
Content-Type
All POST/PUT request bodies use JSON:
Content-Type: application/json
Error codes (common to all endpoints)
| Code | Meaning |
|---|---|
| 400 | Bad request — invalid input. Check the detail field. |
| 401 | Unauthorized — token missing, expired, or invalid. |
| 403 | Forbidden — authenticated but insufficient permissions. |
| 404 | Resource not found. |
| 409 | Conflict — resource already exists or state prevents the operation. |
| 422 | Validation error — field-level details in detail[].loc and detail[].msg. |
| 429 | Rate limited. Retry after the interval in Retry-After header. |
| 502 | Bad gateway — Julia solver service unreachable or returned an error. |
| 503 | Service unavailable — Julia solver module not loaded. |
| 504 | Gateway timeout — solver exceeded the configured timeout_seconds. |
Authentication
GET /api/auth/csrf-token
Retrieve a CSRF token for form-based flows.
Auth: No auth
Response 200:
{ "csrf_token": "abc123..." }
POST /api/auth/login
Authenticate and receive access + refresh tokens.
Auth: No auth
Request body:
{
"username": "analyst@grid.example",
"password": "s3cr3t"
}
Response 200:
{
"access_token": "eyJhbGci...",
"refresh_token": "eyJhbGci...",
"token_type": "bearer",
"expires_in": 3600
}
Error codes: 400 (missing credentials), 401 (wrong password), 404 (unknown user)
Example:
curl -s -X POST http://localhost:58000/api/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"analyst@grid.example","password":"s3cr3t"}' | jq .access_token
POST /api/auth/refresh
Exchange a refresh token for a new access token.
Auth: No auth
Request body:
{ "refresh_token": "eyJhbGci..." }
Response 200: same shape as /api/auth/login
POST /api/auth/logout
Revoke the current access token.
Auth: Bearer token
Response 200:
{ "message": "Logged out successfully" }
POST /api/auth/register
Register a new user account.
Auth: No auth (or admin-only — controlled by ALLOW_PUBLIC_REGISTRATION env var)
Request body:
{ "username": "newuser@grid.example", "password": "SecurePass1!" }
Response 201:
{ "user_id": "usr_01HX...", "username": "newuser@grid.example" }
POST /api/auth/verify-2fa
Complete two-factor authentication.
Auth: No auth
Request body:
{
"temp_token": "eyJhbGci...",
"code": "123456"
}
Response 200: same shape as /api/auth/login (full access + refresh tokens)
Unit Commitment (UC) — Analyst Routes
The analyst module exposes solve routes at /api/modules/analyst/solve/. These routes proxy to the Julia solver service and return a job handle immediately (HTTP 202). Poll /status until completed or failed, then fetch /results.
GET /api/modules/analyst/solve/enhanced-uc/health
Check whether the Enhanced UC Julia module is loaded and operational.
Auth: Bearer token
Response 200:
{
"loaded": true,
"api_version": "2.0.0",
"capabilities": ["unit_commitment", "economic_dispatch", "benders_decomposition"],
"supported_modes": ["da_uc", "rt_uc", "stochastic_uc"],
"supported_solvers": ["highs", "cbc", "gurobi", "cplex"]
}
Error codes: 503 (Julia module not loaded or solver unreachable)
POST /api/modules/analyst/solve/uc
Submit a Unit Commitment solve job.
Auth: Bearer token
Rate limit: configurable via RATE_LIMIT_SOLVES_PER_MINUTE env var (default: 10/min)
Request body:
{
"scenario_id": "scn_pjm_summer_2024",
"payload": {
"generators": [
{
"name": "101_STEAM_3",
"bus": 101,
"p_max": 76.0,
"p_min": 30.0,
"marginal_cost": 22.5,
"no_load_cost": 210.0,
"startup_cost": 5284.8,
"ramp_up": 120.0,
"ramp_down": 120.0,
"min_uptime": 8,
"min_downtime": 4,
"initial_status": 1
}
],
"loads": [
{ "bus": 101, "values": [350.0, 360.0, 370.0, 380.0, 390.0, 400.0, 410.0, 420.0,
430.0, 440.0, 450.0, 460.0, 450.0, 440.0, 430.0, 420.0,
410.0, 400.0, 390.0, 380.0, 370.0, 360.0, 350.0, 340.0] }
],
"horizon": 24,
"solver": "highs"
},
"timeout": 300
}
Request fields:
scenario_id(string, required): identifier for tracing and result lookuppayload.generators(array, required): generator objects. See Generator object.payload.loads(array, required): per-bus load time series.valuesmust have exactlyhorizonentries.payload.horizon(integer, required): number of hours to optimize, typically 24payload.solver(string, optional):"highs"(default),"cbc","gurobi","cplex"timeout(integer, optional): maximum seconds before job is killed, default 300
Response 202:
{
"job_id": "job_01HX4KZABCDEF",
"status": "queued",
"status_url": "/api/modules/analyst/solve/job_01HX4KZABCDEF/status",
"results_url": "/api/modules/analyst/solve/job_01HX4KZABCDEF/results"
}
Error codes: 422 (input validation failure), 502 (Julia solver unreachable)
Full curl example:
curl -s -X POST http://localhost:58000/api/modules/analyst/solve/uc \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d @examples/uc_input_ieee118_24h.json | jq .job_id
POST /api/modules/analyst/solve/scuc
Submit a Security-Constrained Unit Commitment solve job. Same request shape as /solve/uc; include "transmission_lines" and "contingencies" in the payload for N-1 security constraints.
Auth: Bearer token
Response 202: same shape as /solve/uc
POST /api/modules/analyst/solve/ed
Submit an Economic Dispatch solve job. Accepts commitment decisions (binary) as input; optimizes dispatch quantities only.
Auth: Bearer token
Response 202: same shape as /solve/uc
GET /api/modules/analyst/solve/{job_id}/status
Poll job status.
Auth: Bearer token
Path parameter: job_id — ID returned by a submit endpoint
Response 200:
{
"job_id": "job_01HX4KZABCDEF",
"status": "running",
"job_type": "uc",
"submitted_at": "2026-03-15T14:22:00Z",
"started_at": "2026-03-15T14:22:01Z",
"finished_at": null,
"error": null,
"progress": 42
}
status values: queued, running, completed, failed, cancelled
Example:
curl -s http://localhost:58000/api/modules/analyst/solve/job_01HX4KZABCDEF/status \
-H "Authorization: Bearer $TOKEN" | jq .status
GET /api/modules/analyst/solve/{job_id}/results
Fetch completed job results. Returns 202 if still running.
Auth: Bearer token
Response 200 (completed):
{
"job_id": "job_01HX4KZABCDEF",
"status": "completed",
"solve_time_s": 3.8,
"objective_value": 142850.50,
"termination_status": "OPTIMAL",
"commitment": {
"101_STEAM_3": [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
},
"dispatch_mw": {
"101_STEAM_3": [76.0, 76.0, 76.0, "..."]
},
"lmp": {
"bus_101": [28.09, 28.09, 28.09, "..."]
},
"total_cost_usd": 142850.50
}
termination_status values: OPTIMAL, FEASIBLE, INFEASIBLE, TIMEOUT
Response 202 (still running): same shape as /status
Generator object
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | Unique generator identifier |
bus | integer | yes | Bus number |
p_max | float | yes | Maximum output (MW) |
p_min | float | yes | Minimum stable output (MW) |
marginal_cost | float | yes | Variable cost ($/MWh). PJM M28 §4: heat_rate × fuel_price / 1000 |
no_load_cost | float | yes | Fixed cost while online ($/h) |
startup_cost | float | yes | Cold-start cost ($) |
ramp_up | float | yes | Maximum ramp-up rate (MW/h) |
ramp_down | float | yes | Maximum ramp-down rate (MW/h) |
min_uptime | integer | yes | Minimum consecutive hours online |
min_downtime | integer | yes | Minimum consecutive hours offline |
initial_status | integer | yes | Hours online (>0) or offline (≤0) at hour 0 |
AI Analyst
The AI Analyst provides conversational analysis of grid optimization results and code generation assistance.
POST /api/ai/chat
Send a message to the AI Analyst.
Auth: Bearer token
Rate limit: tier-based token budget
Request body:
{
"message": "Why is bus 123 congested in hours 16–18?",
"context": {
"scenario_id": "scn_pjm_summer_2024",
"module_id": "power-flow"
},
"session_id": "sess_01HX...",
"agent_id": "grid-analyst"
}
Request fields:
message(string, required): user message, max 4,000 characterscontext(object, optional): scenario/module context for grounded answerssession_id(string, optional): resume an existing conversation; omit to start a new sessionagent_id(string, optional): delegate to a named agent (e.g.,"grid-analyst")
Response 200:
{
"session_id": "sess_01HX...",
"response": "Bus 123 is congested because the thermal limit on line 45–123...",
"tokens_used": 312,
"moderated": false,
"refused": false,
"safety_checked": true
}
Error codes: 400 (message missing or too long), 422 (safety filter violation), 429 (rate limited)
GET /api/ai/chat/history
Retrieve conversation history for the current session.
Auth: Bearer token
Response 200:
{
"messages": [
{ "role": "user", "content": "Why is bus 123 congested?" },
{ "role": "assistant", "content": "Bus 123 is congested because..." }
]
}
Returns at most the last 20 messages in the active session.
POST /api/ai/generate-code
Generate code from a natural language prompt. Convenience wrapper around /api/ai/chat optimized for code generation tasks.
Auth: Bearer token
Request body:
{ "prompt": "Write Python code to plot LMPs at buses 101–118 for the last 24 hours" }
Response 200: same shape as /api/ai/chat
WebSocket /ws/ai/chat
Real-time streaming AI chat.
Auth: Token in query parameter — ws://localhost:58000/ws/ai/chat?token=<access_token>
Client sends:
{ "message": "Show me the top 5 congested lines", "session_id": "sess_01HX..." }
Server sends (streamed):
{ "type": "token", "content": "The " }
{ "type": "token", "content": "top 5 " }
{ "type": "done", "session_id": "sess_01HX...", "full_response": "The top 5 congested lines are..." }
Error codes: 4401 (missing or invalid token), 4408 (connection limit exceeded per user)
Audit
GET /api/audit/logs
Retrieve platform audit logs. Admin role required.
Auth: Bearer token (admin role)
Query parameters:
type(string, optional): filter by event type (e.g.,login,solve_submitted,config_changed)
Response 200:
{
"logs": [
{
"id": "evt_01HX...",
"event_type": "solve_submitted",
"user_id": "usr_01HX...",
"timestamp": "2026-03-15T14:22:00Z",
"details": { "job_id": "job_01HX...", "solver": "highs" }
}
]
}
Users
GET /api/users/me
Retrieve the current user's profile.
Auth: Bearer token
Response 200:
{
"user_id": "usr_01HX...",
"username": "analyst@grid.example",
"created_at": "2026-01-01T00:00:00Z"
}
POST /api/users/me/password
Change the current user's password.
Auth: Bearer token
Request body:
{
"current_password": "OldPass1!",
"new_password": "NewPass2!"
}
Response 200:
{ "message": "Password updated successfully" }
Error codes: 401 (current password incorrect), 422 (new password fails complexity requirements)
POST /api/users/me/api-keys
Create a named API key for programmatic access.
Auth: Bearer token
Request body:
{ "name": "ci-pipeline-key" }
Response 201:
{
"key_id": "key_01HX...",
"name": "ci-pipeline-key",
"api_key": "mgsk_01HX...",
"created_at": "2026-03-15T14:22:00Z"
}
The api_key value is shown only once at creation time. Store it securely.
GET /api/users/me/subscriptions
List the current user's module subscriptions.
Auth: Bearer token
Response 200:
{
"user_id": "usr_01HX...",
"modules": [
{
"module_id": "power-flow",
"access_level": "full",
"features_enabled": ["solve", "validate", "stability"],
"subscribed_at": "2026-01-01T00:00:00Z"
}
]
}
Modules
GET /api/modules
List all available modules.
Auth: Bearer token
Response 200:
{
"modules": [
{
"id": "power-flow",
"name": "Power Flow",
"description": "DC power flow solve, N-1 validation, and stability analysis",
"version": "1.0.0"
}
]
}
GET /api/modules/{module_id}
Get module info and check access for the current user.
Auth: Bearer token
Path parameter: module_id — e.g., power-flow
Response 200:
{
"id": "power-flow",
"name": "Power Flow",
"has_access": true,
"access_level": "full"
}
GET /api/modules/{module_id}/access
Check whether the current user has an active subscription to a module.
Auth: Bearer token
Response 200:
{
"module_id": "power-flow",
"has_access": true,
"access_level": "full",
"features_enabled": ["solve", "validate", "stability"]
}
POST /api/modules/{module_id}/access
Record a module access event (updates last_accessed timestamp for the user's subscription).
Auth: Bearer token
Response 200:
{
"module_id": "power-flow",
"success": true,
"last_accessed": "2026-03-15T14:22:00Z"
}
Power Flow Validation
The Power Flow module provides a suite of validation endpoints for transmission network data quality checks.
POST /api/modules/power-flow/solve
Lightweight solver wrapper that handles zero-impedance lines and returns adjusted line parameters.
Auth: Bearer token
Request body:
{
"lines": [
{ "id": "line_45_123", "r_ohm": 0.01, "x_ohm": 0.05, "s_nom_mva": 200.0 }
]
}
Response 200:
{
"status": "ok",
"adjusted_lines": [...],
"warnings": [],
"processing_notes": []
}
POST /api/modules/power-flow/validate
Validate power flow data including voltage limits and phase angle constraints.
Auth: Bearer token
Request body:
{
"buses": [{ "id": 101, "v_nom_kv": 345.0, "v_pu": 1.02, "angle_deg": 0.0 }],
"lines": [...],
"generators": [...]
}
Response 200:
{
"valid": true,
"errors": [],
"warnings": ["Bus 105: voltage 1.07 pu near upper limit 1.10 pu"]
}
POST /api/modules/power-flow/validate-topology
Validate network connectivity — detects floating buses and electrical islands.
Auth: Bearer token
Request body:
{
"buses": [{ "id": 101 }, { "id": 102 }],
"lines": [{ "from_bus": 101, "to_bus": 102 }]
}
Response 200:
{
"valid": true,
"floating_buses": [],
"islands": [],
"critical_issues": [],
"warnings": []
}
POST /api/modules/power-flow/validate-generators
Validate generator data completeness — checks for required fields (p_max_mw, p_min_mw, q_max_mvar, q_min_mvar).
Auth: Bearer token
Request body:
{ "generators": [{ "id": "GEN_101", "p_max_mw": 400.0 }] }
Response 200:
{
"valid": false,
"incomplete_generators": ["GEN_101"],
"missing_fields": { "GEN_101": ["q_max_mvar", "q_min_mvar"] },
"warnings": [],
"details": []
}
POST /api/modules/power-flow/validate-lines
Validate line definitions — detects duplicates and parameter errors.
Auth: Bearer token
Request body:
{
"lines": [
{ "id": "L1", "from_bus": 101, "to_bus": 102, "r_ohm": 0.01, "x_ohm": 0.05 }
]
}
Response 200:
{
"valid": true,
"warnings": [],
"duplicate_line_pairs": [],
"details": []
}
POST /api/modules/power-flow/validate-line
Validate physical plausibility of individual line parameters.
Auth: Bearer token
Request body:
{ "r_ohm": 0.01, "x_ohm": 0.05, "id": "L1" }
Response 200:
{ "valid": true, "error": null, "details": {} }
POST /api/modules/power-flow/validate-case
Full data quality check — voltage base consistency, missing reactive limits, island detection.
Auth: Bearer token
Request body:
{
"buses": [...],
"lines": [...],
"generators": [...]
}
Response 200:
{
"valid": true,
"data_quality_issues": [],
"warnings": [],
"details": []
}
POST /api/modules/power-flow/validate-contingency
Validate N-1 contingency analysis requirements — checks ROCOF (Rate of Change of Frequency) against inertia.
Auth: Bearer token
Request body:
{
"contingency": "N-1",
"system_inertia_mw_s": 50000.0,
"projected_rate_of_change_freq": 0.5
}
Response 200:
{
"valid": true,
"violations": [],
"warnings": []
}
POST /api/modules/power-flow/validate-flow
Validate reactive flow consistency and flag extreme from/to reactive power mismatches.
Auth: Bearer token
Request body:
{
"line_id": "L1",
"from_flow_mvar": 10.2,
"to_flow_mvar": -9.8,
"line_impedance": { "r_pu": 0.001, "x_pu": 0.005 }
}
Response 200:
{
"valid": true,
"warnings": [],
"severity": "low",
"mismatch_mvar": 0.4,
"imbalance_percent": 3.9
}
POST /api/modules/power-flow/check-violations
Check line flows against thermal limits.
Auth: Bearer token
Request body:
{
"lines": [
{ "id": "L1", "flow_mva": 185.0, "thermal_limit_mva": 200.0 }
]
}
Response 200:
{
"violations": ["L1"],
"violation_details": [{ "line_id": "L1", "flow_mva": 185.0, "limit_mva": 200.0, "loading_pct": 92.5 }],
"warnings": []
}
GET /api/modules/power-flow/line-limits
Return seasonal thermal rating for a transmission line.
Auth: Bearer token
Query parameters:
line_id(string, required): transmission line identifierseason(string, required): one ofsummer,winter,spring_fall
Response 200:
{
"line_id": "L1",
"season": "summer",
"thermal_limit_mva": 195.0,
"rating_source": "seasonal_derating"
}
POST /api/modules/power-flow/analyze-stability
Analyze reactive power sufficiency for voltage stability.
Auth: Bearer token
Request body:
{
"bus_id": 101,
"load_mvar": 120.0,
"available_sources": [
{ "id": "GEN_101", "q_max_mvar": 80.0, "q_min_mvar": -20.0 }
]
}
Response 200:
{
"feasible": true,
"reason": "Sufficient reactive reserve",
"details": { "total_available_mvar": 80.0, "deficit_mvar": 0.0 }
}
POST /api/modules/power-flow/validate-units
Validate unit consistency — detects kV/MV mixing and MVA/kVA mixing.
Auth: Bearer token
Request body:
{ "buses": [{ "id": 101, "v_nom_kv": 345.0 }] }
Response 200:
{
"valid": true,
"warnings": [],
"suspicious_values": [],
"details": []
}
Model Builder
The Model Builder provides a GUI-backed API for configuring, running, and tracking PCM (Production Cost Model) executions.
GET /api/model-builder/models
List available PCM models.
Auth: Bearer token
Response 200:
{
"models": [
{
"id": "ieee118_24h",
"name": "IEEE 118-Bus 24h",
"description": "54-generator, 118-bus test system",
"supported_solvers": ["highs", "cbc"]
}
]
}
GET /api/model-builder/solvers
List available solvers and their configurable parameters.
Auth: Bearer token
Response 200:
{
"solvers": [
{
"id": "highs",
"name": "HiGHS",
"parameters": [
{ "name": "time_limit", "type": "integer", "default": 300, "description": "Wall-clock limit in seconds" },
{ "name": "mip_gap", "type": "float", "default": 0.001, "description": "Optimality gap tolerance" }
]
}
]
}
POST /api/model-builder/run
Execute a PCM model. Returns immediately with an execution ID; poll /executions/{execution_id} for status.
Auth: Bearer token
Request body:
{
"pcm_model_id": "ieee118_24h",
"network_id": "net_01HX...",
"solver": {
"id": "highs",
"time_limit": 600,
"mip_gap": 0.001
},
"options": {
"horizon": 24,
"include_reserves": true
}
}
Request fields:
pcm_model_id(string, required): model identifier from/model-builder/modelssolver(object, required): solver selection and parametersnetwork_id(string, optional): network data ID; uses model default if omittedoptions(object, optional): model-specific run options
Response 202:
{
"execution_id": "exec_01HX...",
"status": "queued"
}
GET /api/model-builder/executions
List execution history for the current user.
Auth: Bearer token
Query parameters:
limit(integer, optional): max results (default 20)offset(integer, optional): pagination offset
Response 200:
{
"executions": [
{
"execution_id": "exec_01HX...",
"status": "completed",
"pcm_model_id": "ieee118_24h",
"created_at": "2026-03-15T14:22:00Z"
}
]
}
GET /api/model-builder/executions/{execution_id}
Get execution status and results.
Auth: Bearer token
Response 200:
{
"execution_id": "exec_01HX...",
"status": "completed",
"pcm_model_id": "ieee118_24h",
"solver": { "id": "highs", "time_limit": 600 },
"network_id": "net_01HX...",
"options": {},
"created_at": "2026-03-15T14:22:00Z",
"results": {
"objective_value": 142850.50,
"termination_status": "OPTIMAL",
"solve_time_s": 4.2
}
}
status values: queued, running, completed, failed
GET /api/model-builder/configs
List saved model configurations.
Auth: Bearer token
Response 200:
{
"configs": [
{
"config_id": "cfg_01HX...",
"name": "Summer Peak — HiGHS 600s",
"pcm_model_id": "ieee118_24h",
"created_at": "2026-03-15T14:22:00Z"
}
]
}
POST /api/model-builder/configs
Save a model configuration for later reuse.
Auth: Bearer token
Request body:
{
"name": "Summer Peak — HiGHS 600s",
"description": "Standard summer peak run config",
"pcm_model_id": "ieee118_24h",
"solver": { "id": "highs", "time_limit": 600, "mip_gap": 0.001 }
}
Response 201:
{ "config_id": "cfg_01HX...", "name": "Summer Peak — HiGHS 600s" }
GET /api/model-builder/configs/{config_id}
Retrieve a saved configuration by ID.
Auth: Bearer token
Response 200: same shape as the config object in /model-builder/configs
POST /api/model-builder/ai/validate
Validate a model-builder AI prompt through the guardrail system.
Auth: Bearer token
Request body:
{
"prompt": "Run a 168-hour study with demand +20% on all buses",
"channel": "model-builder"
}
Response 200:
{
"allowed": true,
"warnings": [],
"rules": []
}
If allowed is false, the prompt should not be submitted to the model builder.
POST /api/model-tuner/ai/validate
Validate a model-tuner AI prompt through the guardrail system. Same request/response shape as /model-builder/ai/validate.
Health
GET /health
Root health check.
Auth: No auth
Response 200:
{ "status": "ok" }
GET /api/health
API-prefixed health check (same behavior).
Auth: No auth
Response 200:
{ "status": "ok" }
See also
- Developer Quickstart — end-to-end walkthrough: clone → first solve → experiment
- Unit Commitment Concepts — DA-UC formulation, constraint types, PJM M11 §3 worked example
- LMP Decomposition Concepts — LMP = energy + congestion + loss (PJM M11 §6.4)
backend/openapi.json— machine-readable OpenAPI 3.1 spec (regenerate withpython scripts/export_openapi.py)http://localhost:58000/docs— interactive Swagger UI (requires running backend)