Headless Control API
The Control API is the same control plane used by Desktop. It is also available to servers, scripts, and lab automation. Prefer scientific-agent run for a single configuration; use the API when you need creation, pause/resume, queries, uploads, or live events.
Start the service
In the first terminal:
scientific-agent \
--data-dir ./research-data \
serve --host 127.0.0.1 --port 8000
Check it from a second terminal:
curl -sS http://127.0.0.1:8000/api/v1/health | jq
Interactive API documentation is at http://127.0.0.1:8000/api/v1/docs. serve is an unauthenticated local development mode. Keep it bound to 127.0.0.1; do not expose it directly to a LAN or the internet. Desktop starts its own token-protected service, which should not be mixed with this one.
One complete API run
Save the following as run.json:
{
"title": "API toy run",
"goal": "Reach a score of at least 0.9",
"acceptance": {
"metric": "score",
"operator": ">=",
"value": 0.9
},
"budget": {
"max_actions": 5,
"max_experiments": 3,
"max_failures": 2
},
"capabilities": ["toy.evaluate"],
"research_questions": ["Can the deterministic candidate pass?"],
"reasoner": {"type": "deterministic"}
}
Preview the contract without creating a run:
curl -sS -X POST \
-H 'Content-Type: application/json' \
--data @run.json \
http://127.0.0.1:8000/api/v1/runs/preview | jq
Create it and retain the run_id:
curl -sS -X POST \
-H 'Content-Type: application/json' \
--data @run.json \
http://127.0.0.1:8000/api/v1/runs | tee created.json | jq
RUN_ID=$(jq -r '.summary.run_id' created.json)
curl -sS -X POST "http://127.0.0.1:8000/api/v1/runs/$RUN_ID/start" | jq
Read state and timeline:
curl -sS "http://127.0.0.1:8000/api/v1/runs/$RUN_ID/state" | jq
curl -sS "http://127.0.0.1:8000/api/v1/runs/$RUN_ID/events?cursor=0&limit=100" | jq
Live updates
The Server-Sent Events stream works well for progress panels and supervisors:
curl -N "http://127.0.0.1:8000/api/v1/runs/$RUN_ID/stream?after=0"
Every update has an increasing cursor. On reconnect, put the last cursor in after, or send it as Last-Event-ID, to avoid replaying from the beginning.
Lifecycle operations
curl -sS -X POST "http://127.0.0.1:8000/api/v1/runs/$RUN_ID/pause" | jq
curl -sS -X POST "http://127.0.0.1:8000/api/v1/runs/$RUN_ID/resume" | jq
curl -sS -X POST "http://127.0.0.1:8000/api/v1/runs/$RUN_ID/recover" | jq
curl -sS -X POST "http://127.0.0.1:8000/api/v1/runs/$RUN_ID/cancel" | jq
recover applies only to an interrupted run with a recovery point. For cancel and recover, add a unique X-Idempotency-Key so a retried script does not apply a command twice.
Ask and intervene
Ask answers from the recorded study and returns references to events, metrics, jobs, or artifacts:
curl -sS -X POST \
-H 'Content-Type: application/json' \
--data '{"message":"Why is the run not complete?"}' \
"http://127.0.0.1:8000/api/v1/runs/$RUN_ID/ask" | jq
Add a research note:
curl -sS -X POST \
-H 'Content-Type: application/json' \
--data '{
"directive_type":"ADD_RESEARCH_NOTE",
"reason":"Record an operator observation",
"note":"Repeat the evaluation if score variance exceeds 0.02"
}' \
"http://127.0.0.1:8000/api/v1/runs/$RUN_ID/intervene" | jq
Other directive_type values are PAUSE_RUN, RESUME_RUN, and UPDATE_BUDGET. Include only fields to change in the budget object; max_experiments: null removes the experiment limit. To auto-resume from BUDGET_EXHAUSTED, at least one existing limit must actually increase or be removed. Every intervention is audited.
Query research results
Useful read endpoints include:
| Content | Path suffix |
|---|---|
| Overview | /overview |
| Analyses | /analyses |
| Evidence, claims, hypotheses | /evidence, /claims, /hypotheses |
| Verifications and reports | /verifications, /reports |
| Metrics, budget, resources | /metrics, /budget, /resources |
| Jobs and model calls | /jobs, /model-calls |
| Artifacts and lineage | /artifacts, /lineage, /research-flow |
| Ask/intervention history | /interactions |
Append the suffix to /api/v1/runs/$RUN_ID. Paginated lists accept cursor and limit, with at most 250 items per page.
To download an artifact, obtain its artifact_id from /artifacts first:
curl -sS \
"http://127.0.0.1:8000/api/v1/runs/$RUN_ID/artifacts/$ARTIFACT_ID/content" \
-o result.bin
Upload an input artifact as Base64:
{
"name": "measurements.csv",
"artifact_type": "OTHER",
"content_base64": "YSxiCjEsMgo="
}
POST it to /api/v1/runs/$RUN_ID/artifacts. Upload run inputs before starting the run.
Scientific commands
Automation can explicitly request analyses, reports, hypotheses, and verification rather than disguising commands as chat:
The report example below assumes a non-frozen run that can still accept scientific commands, a contract that authorized scientific.report.generate, and an existing visible anchor artifact. The minimal toy contract above did not authorize reporting, so reusing it would correctly return a missing-capability error.
curl -sS -X POST \
-H 'Content-Type: application/json' \
-H 'X-Idempotency-Key: report-request-001' \
--data '{
"command_type":"RequestReport",
"report_title":"Final assessment",
"include_figures":true,
"reason":"Prepare the review package"
}' \
"http://127.0.0.1:8000/api/v1/runs/$REPORT_RUN_ID/scientific-commands" | jq
Command types are RequestAnalysis, RequestReport, SubmitHypothesis, CreateVerificationPlan, ApproveVerificationPlan, RequestVerification, and UpdateHypothesisStatus. Each requires different references and fields; use the ScientificCommandRequest schema in /api/v1/docs as the source of truth.
Capabilities, plugins, storage, and exports
curl -sS http://127.0.0.1:8000/api/v1/capabilities | jq
curl -sS http://127.0.0.1:8000/api/v1/plugins | jq
curl -sS http://127.0.0.1:8000/api/v1/storage/info | jq
curl -sS -X POST http://127.0.0.1:8000/api/v1/storage/backups | jq
Manage plugins with POST /plugins/{plugin_id}/install, /enable, and /disable. A custom ZIP goes to POST /plugins/custom/install with filename and content_base64. Installation validates and stores the package; enabling it is what imports code.
Create a portable scientific export from a successful run:
curl -sS -X POST \
-H 'Content-Type: application/json' \
--data "{\"run_id\":\"$RUN_ID\",\"name\":\"review-package\"}" \
http://127.0.0.1:8000/api/v1/exports | jq
Automation checklist
- Call
/healthbefore creating a run. - Preview the contract and check
validandwarningsfirst. - Persist the
run_id, command idempotency keys, and SSE cursor. - Parse JSON state; do not infer success from terminal prose.
- Never edit event files or artifact directories behind the API.
- A multi-user or remote deployment needs a separate reverse proxy, authentication, and TLS; built-in
serveis not a public service.