HTTP API
Dyno exposes inference, persistent research jobs, resident activation capture and an execution inspector. Choose the correct service before sending a request. The native app does not need these calls for normal use; see the app handbook. Python users can use the SDK.
Services and access
| Service | Typical origin / prefix | Start it with | Network access |
|---|---|---|---|
| Model inference | http://127.0.0.1:8971/v1 |
Models → Start, or dyno serve |
Depends on server binding |
| Routed inference | http://127.0.0.1:8970/v1 |
Router → Start the router | LAN when explicitly enabled |
| Isolated research | http://127.0.0.1:8980/lab/v1 |
Lab → Start lab, or dyno lab |
Loopback only |
| Resident activations | http://127.0.0.1:8971/lab |
Current Dyno inference server | Loopback only |
| Execution inspector | Inference server /executions |
Current Dyno inference server | Loopback only |
Ports are examples; use the values configured on your Mac. Research and execution endpoints reject LAN and browser-origin requests even if inference is shared. They are intended for native clients, curl, Python and the local MCP bridge. LAN inference has no built-in authentication; use a trusted network.
The downloadable OpenAPI 3 document describes the research job and resident-capture contracts. Its resident paths override the default server URL. It is not a full specification of inference or the execution inspector.
Inference and LAN clients
Start a model, then list the served identifiers:
curl http://127.0.0.1:8971/v1/models
Use the returned model identifier in model. For the router use model auto. This Bash example prompts for the URL and model so it works with your local configuration:
read -r -p 'Base URL (including /v1): ' DYNO_BASE_URL
read -r -p 'Model identifier (auto for router): ' DYNO_MODEL
export DYNO_MODEL
python3 - <<'PY' > request.json
import json, os
print(json.dumps({
"model": os.environ["DYNO_MODEL"],
"messages": [{"role": "user", "content": "Explain why leaves are green."}],
"max_tokens": 120,
"temperature": 0,
"stream": False,
"chat_template_kwargs": {"enable_thinking": False}
}))
PY
curl "$DYNO_BASE_URL/chat/completions" \
-H 'Content-Type: application/json' --data-binary @request.json
For Windows/WSL or another computer, enable Router → Share on local network and enter the copied client URL. 127.0.0.1 on Windows points to Windows, not the Mac. Test reachability before debugging payloads. Do not use a screenshot's private IP address.
Read the answer from choices[0].message.content. Compatible models may return reasoning_content separately. chat_template_kwargs.enable_thinking overrides the launch default only when the model's template supports it. Raw-text research requests do not use this setting.
For streamed responses set stream: true and use curl -N. Read SSE data: events until [DONE]; accumulate text deltas rather than expecting one final JSON document. To request token probabilities, set logprobs: true and top_logprobs: 5 on a supporting direct model endpoint. Log probabilities are in choices[0].logprobs.content; convert with exp(logprob).
Isolated research: first request
Start Lab → Experiments → Start lab. A source installation can use dyno lab --port 8980. Check health, then submit:
curl http://127.0.0.1:8980/lab/v1/health
curl http://127.0.0.1:8980/lab/v1/jobs \
-H 'Content-Type: application/json' \
-d '{"operation":"inspect","model":"mlx-community/Qwen1.5-0.5B-Chat-4bit","prompt":"The capital of France is","layers":[4,8],"max_input_tokens":128,"seed":0}'
Successful submission returns 202 with job metadata including id, status, operation, model and config. Submission is not completion. Copy the returned ID, then poll:
read -r -p 'Job ID: ' DYNO_JOB_ID
curl "http://127.0.0.1:8980/lab/v1/jobs/$DYNO_JOB_ID"
When status is completed, inspect result. Inspect results include token strings/IDs, layer norms, final next_tokens, available intermediate predictions, provenance and artifact names. If status is failed, read error; invalid architecture or operation-specific inputs can fail in the worker after a successful 202 submission.
Each job loads its own model copy. Only one isolated job runs at a time. SDK/HTTP clients do not receive the native app's memory/GPU admission checks. Check headroom and competing serving activity before submission. A model ID may download weights; a local model directory uses an existing download.
Job endpoints
All paths below use http://127.0.0.1:8980 unless you configured a different Lab port.
| Method | Path | Success | Response |
|---|---|---|---|
| GET | /lab/v1/health |
200 | Service capabilities |
| GET | /lab/v1/openapi.json |
200 | Research OpenAPI document |
| POST | /lab/v1/jobs |
202 | Submitted job metadata |
| GET | /lab/v1/jobs |
200 | { "jobs": [...] }, newest 100 summaries |
| GET | /lab/v1/jobs/{id} |
200 | Full job with configuration and result/error |
| POST | /lab/v1/jobs/{id}/cancel |
200 | Job metadata; send JSON {} |
| DELETE | /lab/v1/jobs/{id} |
200 | { "deleted": true }; terminal jobs only |
| GET | /lab/v1/jobs/{id}/artifacts/{name} |
200 | Binary file listed by the job |
Jobs progress from queued to running to completed, failed or cancelled. If a service restarts with an unfinished job on disk, that job is marked interrupted. Saved jobs persist under ~/.mlx-dyno/lab/<id>. Native token-analysis history and direct resident captures are separate and do not appear here.
Download, cancel or delete deliberately:
curl --fail "http://127.0.0.1:8980/lab/v1/jobs/$DYNO_JOB_ID/artifacts/activations.npz" -o activations.npz
# Stop the isolated worker, if it is still running:
curl "http://127.0.0.1:8980/lab/v1/jobs/$DYNO_JOB_ID/cancel" \
-H 'Content-Type: application/json' -d '{}'
# Delete the terminal job and its artifacts:
curl -X DELETE "http://127.0.0.1:8980/lab/v1/jobs/$DYNO_JOB_ID"
Cancellation does not stop an inference server. Deleting a running job returns 409; cancel it first. Downloaded files are not deleted by deleting their server-side job.
Experiment parameters
Common fields for POST /lab/v1/jobs:
| Field | Default / allowed values | Meaning |
|---|---|---|
operation |
Required: inspect, compare, probe, sae |
Experiment type |
model |
Required nonempty string | Hugging Face ID or local model directory |
revision |
Optional revision string | Pin a model revision |
layers |
[0]; 1–8 indices, each 0–255 |
Zero-indexed block outputs; indices must exist in the model |
prompt |
Required in practice for inspect/compare | Raw text, without chat template |
max_input_tokens |
256; range 1–1024 | Input token limit |
max_tokens |
32; range 1–128 | Greedy continuation budget for comparisons |
seed |
0; range 0–2147483647 | Experiment seed |
examples |
At most 512 entries | Explicit training/test dataset for probe or SAE |
The request body is limited to 500,000 bytes. Workers have a 30-minute deadline. Compatibility requires supported MLX block layouts; downloading an MLX model does not guarantee that every research operation supports it.
Interventions (compare)
Use one selected layer: the worker intervenes at the first selected layer. The intervention changes the block's last-token output on every forward pass.
| Field | Values / behavior |
|---|---|
intervention |
scale (default), ablate, patch, steer |
strengths |
1–9 finite numbers between −5 and 5; default [0,0.5,1,1.5] |
target_token |
Optional text encoding to exactly one token; otherwise baseline's top token |
prompts |
Optional 1–32 prompts for repeated comparisons; still provide prompt |
donor_prompt |
Donor text for patching |
positive, negative |
Two texts whose final-token activation difference supplies a unit steering direction |
Scale strength 1 leaves the state unchanged. Ablation, patch and steering strength 0 leave it unchanged. Target-token probabilities are compared at the identical input prefix. Greedy continuations can diverge and are not aligned causal traces of later tokens.
{
"operation": "compare",
"model": "mlx-community/Qwen1.5-0.5B-Chat-4bit",
"prompt": "The capital of France is",
"layers": [8],
"intervention": "scale",
"strengths": [0, 1, 1.5],
"max_tokens": 12,
"seed": 0
}
Probes (probe)
Each example needs text, binary label (0 or 1) and split (train or test). Both classes must occur in each split; exact cross-split duplicate texts are rejected. The SDK dataset example supplies a complete minimal demonstration.
Features are final-token block activations, standardized using training statistics. Regularized logistic probes report held-out accuracy, AUROC, Brier score, majority baseline and shuffled-label control. Artifact weights are evaluated as sigmoid(((x - mean) / std) @ weight + bias). Good probe accuracy shows decodable information, not necessarily a representation the model causally uses.
SAE sandbox (sae)
Examples need text and explicit split; labels are not required. Supply training and held-out examples. features defaults to 64 (range 8–512); steps defaults to 100 (range 1–500). It trains a small ReLU encoder/linear decoder with L1 regularization on final-token activations.
Results include reports with held-out reconstruction MSE, active/dead feature statistics, training losses and top activating examples. Artifacts include weights and normalization. Feature indices are not validated concepts. Importing pretrained SAEs and steering with their decoder features are not implemented.
Resident activation endpoints
These run on the inference port, not the Lab job port. First discover support:
curl http://127.0.0.1:8971/lab/capabilities
Read serving_activations and copy the exact returned model. An updated Dyno endpoint enables this automatically; an old process must be restarted explicitly. Then submit JSON with:
{
"model": "COPY_EXACT_CAPABILITIES_MODEL_VALUE",
"prompt": "The capital of France is",
"layers": [4, 8],
"max_input_tokens": 128
}
Save that payload as capture.json, then run:
curl --max-time 75 http://127.0.0.1:8971/lab/activations \
-H 'Content-Type: application/json' --data-binary @capture.json
| Method | Path | Behavior |
|---|---|---|
| GET | /lab/capabilities |
Model identity, support and limits |
| POST | /lab/activations |
Direct measurement result (200), not a job ID |
Allowed fields are model, prompt, layers and max_input_tokens; unknown fields are rejected. Use 1–4 distinct layers and at most 256 input tokens (default 128). A model mismatch fails rather than loading new weights. The result supplies tokens, layer norms, final next-token candidates and provenance; no raw tensor artifact or intermediate logit lens.
Only one capture can be pending/running. It runs on the generation thread between scheduler iterations using fresh forward-pass state. It shares GPU/workspace and may briefly delay requests. A queued capture expires after 60 seconds; a forward pass already started completes before serving resumes. Client timeout is not a cancellation API.
Execution and performance endpoints
On a current inference server, GET /stats exposes instrumented serving metrics. The native Performance view combines these with Mac hardware telemetry. An arbitrary OpenAI-compatible server may not support this extension.
The execution inspector uses:
| Method | Path | Purpose |
|---|---|---|
| GET | /executions |
Recent captured request summaries and capture counters |
| GET | /executions/{id} |
Request, lifecycle events and model output for a trace |
| DELETE | /executions |
Clear finished traces; active requests keep running |
Execution is bounded, in-memory history, not the persistent research job store. It captures at most 64 requests per endpoint with content/step limits. Model-emitted reasoning is returned text, not complete access to internal computations. A skipped-history count concerns capture capacity; it does not mean inference failed. These endpoints reject LAN and browser-origin access.
Errors and troubleshooting
Research JSON errors have an error message. Inspect the HTTP status and, for accepted jobs, the eventual job status.
| Status / condition | Meaning | Next step |
|---|---|---|
| 400 | Invalid JSON, body size or parameters | Check field types, limits and required model/prompt |
| 403 | Research/Execution request blocked by local-access rules | Use a native loopback client on the serving Mac |
| 404 | Job, artifact or route absent | Verify service port, prefix, ID and listed artifact name |
| 408 (resident) | Queued capture expired | Wait for serving capacity and retry |
| 409 | Job/capture busy, model conflict, or deletion of active job | Read the message; wait, correct identity, or cancel deliberately |
| Connection refused | Service not started or wrong port | Start the intended service and use its configured port |
Job failed after 202 |
Worker rejected data or model execution failed | Read error; check architecture, layer indices and dataset |
Results include provenance such as configuration hash, runtime versions, requested revision and resolved model information. A weight-file size/time manifest is not a cryptographic checksum of weights. Keep the configuration, artifact files and exact model revision when sharing an experiment.