The FastAPI controller exposes a JSON API on the same port as the
dashboard (api.port, default 8085). All endpoints return JSON
unless noted. Errors come back as HTTP 4xx/5xx with a JSON body
{"detail": "...", ...}.
For interactive exploration, the controller also serves the OpenAPI
schema at GET /docs and GET /openapi.json.
Every endpoint — including the HTML dashboard and /docs — requires
the password set via [security].dashboard_password in the controller's
TOML config (default "password").
- Browser — visiting any protected URL redirects to
/login(HTML form). On success the server sets an HMAC-signedautodft_authcookie.GET /logoutclears it. - Scripts — send the password via the
X-AutoDFT-Passwordheader on every request. No cookie needed.
# Header path
curl -s http://localhost:8085/api/overview \
-H "X-AutoDFT-Password: password"
# Browser path
curl -i -c cookies.txt -X POST http://localhost:8085/login \
-d 'password=password&next=/' # 303 + Set-Cookie
curl -s -b cookies.txt http://localhost:8085/api/overviewUnauthenticated requests to /api/* return:
HTTP 401
{ "detail": "Authentication required. Send the password via the "
"X-AutoDFT-Password header or sign in at /login first." }Unauthenticated browser requests get an HTTP 303 redirect to
/login?next=<original-path>.
Run RDKit on a SMILES string and return a structured verdict. Used by the dashboard for live form validation; equally useful from a script.
Body:
{ "smiles": "c1ccc(O)cc1" }Response:
{
"valid": true,
"canonical": "Oc1ccccc1",
"atoms": 13,
"heavy_atoms": 7,
"charge": 0,
"multiplicity": 1,
"error": null
}valid=false populates error with the RDKit reason. Rejected cases
include empty strings, anything RDKit refuses to parse, and single-atom
species (GOAT needs ≥2 atoms).
Queue one calculation entrypoint. The server validates the SMILES first
(same validator as above) and returns 400 if invalid. Resolves any
header_*_id against the computation_headers table; if neither
header_*_id nor header_* (raw text) is provided, falls back to the
package defaults in autodft/qm/orca/defaults.py.
Body — every option:
| Field | Type | Default | Notes |
|---|---|---|---|
smiles |
str | required | Validated server-side. |
project |
str | "default" |
Used to group molecules and to scope exports / archives. |
priority |
int | 10 |
Higher = served first. Ties broken by submission order. |
request_t1 |
bool | false |
Build a T1 state and run the full chain on it. |
request_ox |
bool | false |
Build a +1 (oxidised) state. |
request_red |
bool | false |
Build a −1 (reduced) state. |
skip_confsearch |
bool | false |
Skip GOAT, send the RDKit-generated geometry straight to optimization. |
request_optimization |
bool | true |
If false, the pipeline stops after confsearch. |
request_singlepoint |
bool | true |
If false, no singlepoint is created (and no vertical excitations either). |
request_singlepoint_vertical_excitations |
bool | true |
Adds vert-ox / vert-red / spin-flip singlepoints on the optimised geometry of each state. |
max_conformers_S0 |
int | 1 |
Conformer cap for the S0 state. |
max_conformers_T1 |
int | 1 |
Conformer cap for the T1 state. |
max_conformers_ox |
int | 1 |
Conformer cap for the ox state. |
max_conformers_red |
int | 1 |
Conformer cap for the red state. |
max_conformers |
int? | null |
Legacy override — when set, applies to every state, regardless of the per-state fields. |
header_confsearch |
str? | null |
Raw ORCA header block (multi-line). Used if no header id is set. |
header_optimization |
str? | null |
Raw ORCA header block. Used if no header id is set. |
header_singlepoint |
str? | null |
Raw ORCA header block. Must not contain Opt or Freq. |
header_confsearch_id |
int? | null |
ID of a stored ComputationHeader. Wins over the raw text version. |
header_optimization_id |
int? | null |
Same. |
header_singlepoint_id |
int? | null |
Same. |
request_S1 is not exposed: the S1 state is not yet supported.
Responses:
200 OK→{ "id": 42, "smiles": "CCO", "status": "queued", "time_created": "2026-06-01T07:14:42.669100" }400 Bad Request(invalid SMILES) →{ "detail": "RDKit could not parse 'xxx'.", "validation": { "valid": false, "error": "...", ... } }
Minimal example — defaults everywhere:
curl -X POST http://localhost:8085/api/submit \
-H 'Content-Type: application/json' \
-d '{"smiles":"CCO","project":"alcohols"}'Full-coverage example — T1/ox/red, vert-ex off, custom per-state conformer counts, header by id:
curl -X POST http://localhost:8085/api/submit \
-H 'Content-Type: application/json' \
-d '{
"smiles": "c1ccc(O)cc1",
"project": "phenols",
"priority": 20,
"request_t1": true,
"request_ox": true,
"request_red": true,
"request_singlepoint_vertical_excitations": false,
"max_conformers_S0": 5,
"max_conformers_T1": 3,
"max_conformers_ox": 2,
"max_conformers_red": 2,
"header_confsearch_id": 2,
"header_optimization_id": 4,
"header_singlepoint_id": 6
}'{
"molecules": 4,
"tasks": {"created": 0, "pending": 16, "successful": 0, "failed": 0},
"jobs": {"RUNNING": 14, "PENDING": 2, "COMPLETED": 0},
"queue_length": 0
}Entrypoints that haven't been processed yet (time_started IS NULL).
Entrypoints that raised a processing_error (e.g. SMILES that slipped
past validation, RDKit/OpenBabel both unavailable). Surface these to the
user — the controller never silently retries them.
[
{ "id": 7, "smiles": "weird ?? input",
"priority": 10,
"time_created": "...",
"time_started": "...",
"processing_error": "RuntimeError: Cannot generate 3-D geometry for ..." }
]Paginated molecule list.
Full molecule tree: states → tasks → jobs.
status is one of created | pending | successful | failed; type is
one of confsearch | optimization | singlepoint | singlepoint_vert_ox | singlepoint_vert_red | singlepoint_vert_spin_change.
status matches SLURM (RUNNING | PENDING | COMPLETED | FAILED | TIMEOUT | CANCELLED | UNKNOWN).
[
{ "name": "phenols", "molecules": 12,
"tasks_total": 192, "tasks_failed": 3, "tasks_successful": 145 }
]Per-project view: progress, success rate, and one row per molecule:
{
"name": "phenols",
"submission_progress": { "total": 12, "started": 12 },
"success_rate": { "total_molecules": 12, "successful_molecules": 11 },
"molecules": [
{ "id": 3, "smiles": "Oc1ccccc1", "states": 4, "tasks": 16,
"successful": 16, "failed": 0,
"created_at": "2026-06-01T..." }
]
}Non-destructive export. Writes into <export_data>/<name>/:
csv→<name>.csv(summary table of energies)json→<name>.jsonfiles→files/tree with the canonical curated ORCA files
{ "format": "csv", "path": "/.../export_data/phenols/phenols.csv" }Destructive. Writes the CSV, copies every file matching the
extensions you list (preserving the directory layout), wipes
<comp_data>/mol_*/ for the project, and deletes the project's
database rows. The dashboard's "Export all files" button is the
intended UI for this.
Body:
{ "extensions": [".inp", ".xyz", ".out"], "all_conformers": false }Add .cube, .spindens, .eldens, .gbw, .densities, .hess, …
to keep more.
Response:
{ "project": "phenols", "archived": true,
"molecules": 12, "files_copied": 96, "files_dropped": 184,
"csv_path": "/.../export_data/phenols/phenols.csv",
"files_root": "/.../export_data/phenols/raw",
"extensions": [".inp", ".out", ".xyz"] }Refused with 409 if any task in created or pending status still
references the project (wait for them to finish or cancel them first).
Stored ORCA header templates that populate the dashboard's submission dropdowns. Six are seeded on first init; the rest are user-created.
{
"defaults": [ { "id": "default_confsearch", "label": "...",
"description": "...", "kind": "confsearch",
"text": "!GOAT XTB2\n..." } ],
"custom": [ { "id": 2, "label": "GOAT g-xTB conformer ensemble",
"description": "...", "kind": "confsearch",
"validated": true, "deleted": false,
"text": "!GOAT XTB\n%xtb\n XTBInputString \"--gxtb\"\nend\n..." } ]
}Strict kind filter: untagged custom headers are intentionally hidden
from slot-specific responses; they're still listed when no kind is
passed. Soft-deleted headers are excluded unless include_deleted=true.
{ "header_text": "!wB97X-D3 def2-TZVP TIGHTSCF\n%maxcore 4000\n%pal nprocs 16 end\n",
"description": "wB97X-D3/def2-TZVP TightSCF singlepoint",
"kind": "singlepoint",
"validated": false }kind must be one of confsearch | optimization | singlepoint | null.
Partial update — pass only the fields you want changed.
Soft-delete. Sets deleted=true; the row stays in the table so finished
tasks keep their FK pointers. Refused with 409 only when an
in-flight task (created or pending) still references the header.
The pipeline is built to fail loudly:
POST /api/submitreturns 400 on invalid SMILES before anything is persisted.- SMILES that pass the syntactic check but later break geometry
generation set
processing_erroron the entrypoint and appear onGET /api/entrypoints/failed. The controller does not retry them. - A job that runs but fails ORCA's checks sets
success=falseand afail_reasononComputationJob. The owning task moves tofailedonly afterpipeline.max_attemptsunsuccessful jobs (default 3).
To recover: fix the SMILES / header / config and resubmit. For the recovery CLI commands see the README.