Introduction

Qlro answers one question end-to-end: given a quantum workload, which device should you run it on first, and how do you prove the choice was reasonable? The product is shipped as a Python SDK, a CLI, a REST API, and a web app at qlro.io. They share one canonical scoring pipeline (WCPP §5 of the methodology paper), one snapshot of vendor benchmark data, and one decision-record format you can cite from a paper or an RFP.

This page is the full reference. For the 5-minute installer walkthrough, see the quickstart. Methodology and validation live in the Zenodo paper; trust posture lives at /independence and /data-policy.

Quickstart (5 min)

1. Install

bash
pip install qlro

# Optional: vendor extras (Braket / IBM Runtime) pull in their SDKs
pip install "qlro[braket,ibm]"

2. Authenticate

Two ways. Either is fine; both end up storing the same key in a way the SDK will pick up automatically.

bash
# Option A — browser-mediated (recommended)
qlro auth login
# Opens https://qlro.io/cli-auth, you confirm in the browser, the key
# lands in ~/.qlro/credentials with 0600 permissions.

# Option B — env var (CI / Docker friendly)
export QLRO_API_KEY="qlro_..."   # copy from qlro.io/settings

3. Run a recommendation

python
import qlro

result = qlro.recommend_workload(
    "industry.pharma.drug_similarity_search",
    parameters={"library_size": 50_000},
)
print(result.primary)            # top device
print(result.rankings[0].fit)    # workload-conditioned fit score
print(result.snapshot_commit)    # provenance handle for citations

4. Submit an outcome (closes the loop)

python
qlro.log_outcome(
    recommendation=result,
    actually_ran_on="ibm_kingston",
    observed_fidelity=0.61,
    note="Ran on real hardware 2026-06-08",
)
# Appears at qlro.io/accuracy (public aggregate) and qlro.io/my-runs
# (your personal feed). Counts toward your 500-call monthly quota.

Core concepts

WCPP — Workload-Conditioned Physical Projection

Devices are scored against four axes — Γ (connectivity), Φ (coherence), F (fidelity), T (throughput) — and combined with a workload-specific weight vector via geometric mean. The weights are derived deterministically from the circuit shape and the workload category; there is no language model in the scoring path.

Snapshots

Every recommendation pins a snapshot_commit hash from the Metriq benchmark dataset. Re-running with the same inputs and the same commit yields the same ranking; switching commits is the only way a result can drift. Snapshots are versioned monthly.

Decision records

A decision record is a content-hashed bundle: the WCPP ranking, the snapshot commit, a bilingual PDF, a BibTeX entry, and a ZIP of the underlying inputs. It is the artefact a procurement officer or external reviewer can cite to defend the choice. Generated from the SDK (qlro.mint_decision_record()) or from any template page on qlro.io.

SDK reference (Python)

All functions are importable from the top-level qlro package. Local-only entry points (recommend, recommend_workload,list_workloads, status, lint) do not need an API key. Cloud-write entry points (log_outcome, mint_decision_record) do.

qlro.recommend(
    circuit: QuantumCircuit | None = None,
    category: str = "chemistry",
    priorities: dict[str, float] | None = None,
    shots: int = 1024,
    devices: list[str] | None = None,
    snapshot: MetriqSnapshot | None = None,
) -> Recommendation
The base recommendation entry point. Local-only: runs off the bundled wcpp_snapshot.json, needs no API key, never hits the network.
Parameters
circuitQuantumCircuit | NoneQiskit QuantumCircuit. When present, circuit metadata (qubits / depth / 2-qubit count) feeds workload-specific weight derivation. (default: None)
categorystrWorkload category. chemistry | optimization | simulation | ml. (default: "chemistry")
prioritiesdict[str, float] | None{ confidence, cost, speed } weight vector. Sum auto-normalised. In v1 only confidence affects ranking; cost/speed wiring lands later. (default: None)
shotsintShot count for the planned run. Informational in v1. (default: 1024)
deviceslist[str] | NoneRestrict the candidate set to a subset of canonical device keys. (default: None)
snapshotMetriqSnapshot | NonePre-loaded snapshot. Skip to use the package default. (default: None)
Returns Recommendation
.primarystrTop-pick device key.
.runner_upstr | NoneSecond-best device key.
.rankingslist[DeviceRanking]Full ranking with .fit, .sigma, .axes per device.
.snapshot_commitstrProvenance hash. Pin this when citing.
.scoring_versionstrScoring pipeline version that produced this result.
.show()methodPretty-prints the ranking table to stdout (Jupyter/CLI friendly).
Examples
import qlro

result = qlro.recommend(category="chemistry")
print(result.primary, result.rankings[0].fit)

Minimal call — category-only ranking from the snapshot.

from qiskit import QuantumCircuit
import qlro

qc = QuantumCircuit(4)
qc.h(0); qc.cx(0, 1); qc.cx(1, 2); qc.cx(2, 3); qc.measure_all()

rec = qlro.recommend(circuit=qc, category="ml", shots=4096)
rec.show()

With a real circuit, workload weights adapt to the actual shape.

qlro.recommend_workload(
    template_id: str,
    parameters: dict[str, Any] | None = None,
    priorities: dict[str, float] | None = None,
    shots: int = 1024,
    devices: list[str] | None = None,
    snapshot: MetriqSnapshot | None = None,
    category: str | None = None,
) -> Recommendation
Procurement-facing variant. Picks a named template from the catalogue, builds the canonical circuit server-side, runs the same ranking pipeline. No quantum-circuit knowledge required.
Parameters
template_id*strCatalogue identifier. Use qlro.list_workloads() to discover.
parametersdict[str, Any] | NoneTemplate-specific parameters. Missing values fall back to the template defaults. (default: None)
prioritiesdict[str, float] | NoneSame shape as qlro.recommend(). (default: None)
shotsintPlanned shot count. (default: 1024)
deviceslist[str] | NoneRestrict the candidate set. (default: None)
snapshotMetriqSnapshot | NonePre-loaded snapshot. (default: None)
categorystr | NoneOverride the template's declared category. (default: None)
Returns Recommendation (same shape as qlro.recommend)
Examples
import qlro

rec = qlro.recommend_workload(
    "industry.pharma.drug_similarity_search",
    parameters={"library_size": 50_000},
)
print(rec.primary)
# Pin a specific provider channel via the devices filter
rec = qlro.recommend_workload(
    "industry.finance.barrier_option_pricing",
    parameters={"expiry_days": 180},
    devices=["ibm_kingston", "ibm_brisbane", "ibm_fez"],
)
Raises
ValueErrortemplate_id not found, or parameters fail the template schema.
qlro.list_workloads(industry: str | None = None) -> list[dict[str, Any]]
qlro.list_industries() -> list[str]
Catalogue browsing. Local; no API key. The dicts mirror the GET /workloads REST payload.
Parameters
industrystr | NoneOptional restrict: "pharma" | "finance" | "manufacturing". (default: None)
Returns list[dict]
Examples
import qlro

for tpl in qlro.list_workloads(industry="pharma")[:3]:
    print(tpl["template_id"], "-", tpl["display_name"])
qlro.status(
    device: str,
    workload: str | None = None,
    return_path: str = "json",
) -> Status
Channel-neutral go/no-go check (0.16.0+). Asks"is device X ready for workload Y right now?" Powers the Jupyter magic, the Slack alert, the GitHub Action gate, and the qlro status CLI.
Parameters
device*strCanonical Metriq device key.
workloadstr | NoneWorkload category the status is evaluated against. None falls back to chemistry. (default: None)
return_pathstr"json" returns a Status object; "raise" raises on red severity. Used by guard-clauses. (default: "json")
Returns Status
.severitystrgreen | amber | red.
.reasonstrOne-line human-readable explanation.
.suggestionstrAction to take (e.g. 'switch to ibm_fez').
.snapshot_commitstrProvenance hash.
Examples
import qlro

s = qlro.status("ibm_brisbane", workload="chemistry")
if s.severity != "green":
    raise RuntimeError(s.reason)
device.run(circuit, shots=1024)

Guard a hardware submit on the live status verdict.

qlro.lint(
    circuit: QuantumCircuit | str,
    device: str,
    shots: int | None = None,
    region: str | None = None,
    access: list[str] | None = None,
) -> LintReport
Pre-submit linter (0.15.0+). Statically analyses the circuit against the target device and reports every rule that would cause a REJECT or silent rewrite at submission time.
Parameters
circuit*QuantumCircuit | strQiskit circuit or OpenQASM 2.0 source string.
device*strCanonical device key.
shotsint | NonePlanned shot count. Enables SHOTS_OVER_QUOTA rule. (default: None)
regionstr | NoneIntended AWS region. Enables REGION_MISMATCH rule. (default: None)
accesslist[str] | NoneProvider channels you hold credentials for. Enables NO_ACCESS rejection. (default: None)
Returns LintReport
.findingslist[Finding]Each carries .rule_id, .severity (WARN | REJECT), .message, .quick_fix, .doc_url, .source_line.
.has_rejectboolTrue if any finding has severity REJECT.
Examples
import qlro

report = qlro.lint(open("vqe.qasm").read(), device="H2-2", shots=2000)
for f in report.findings:
    print(f"[{f.severity}] {f.rule_id}: {f.message}")
if report.has_reject:
    raise SystemExit(2)
qlro.log_outcome(
    recommendation: Recommendation,
    actual_result: Any = None,
    actually_ran_on: str | None = None,
    observed_fidelity: float | None = None,
    shots: int | None = None,
    calibration_level: int | None = None,
    note: str | None = None,
    submit: bool = True,
    endpoint: str | None = None,
    timeout: float = 10.0,
) -> dict[str, Any]
Real-hardware outcome submission. Posts the (predicted, observed) pair to the feedback API that powers /accuracy and /my-runs. Best effort — network errors are caught and returned in the dict; a notebook never crashes.
Parameters
recommendation*RecommendationThe object returned by qlro.recommend() / qlro.recommend_workload().
actual_resultAnyQiskit Result (anything with .get_counts()). When provided and observed_fidelity is None, the dominant-outcome fraction is used. (default: None)
actually_ran_onstr | NoneDevice the user actually ran on. Defaults to recommendation.primary. (default: None)
observed_fidelityfloat | NoneExplicit observed fidelity in [0, 1] when already computed. (default: None)
shotsint | NoneInferred from actual_result when absent. (default: None)
calibration_levelint | None0 / 1 / 2 if the prediction was calibrated; left blank for raw predictions. (default: None)
notestr | NoneFree text (≤500 chars). Scrubbed for PII server-side. (default: None)
submitboolSet False to skip the network call and only return the local summary. (default: True)
endpointstr | NoneOverride the feedback endpoint. (default: None)
timeoutfloatHTTP timeout in seconds. (default: 10.0)
Returns dict
submittedboolWhether the network call succeeded.
idint?Server-assigned row id (only on success).
errorstr?Error string on submission failure.
summarydictLocal-side view of what was logged.
Examples
import qlro

rec = qlro.recommend_workload("industry.pharma.drug_similarity_search")
result = qlro.log_outcome(
    recommendation=rec,
    actually_ran_on="ibm_kingston",
    observed_fidelity=0.61,
    note="Real hardware 2026-06-08",
)
print(result["submitted"], result.get("id"))

CLI reference

Installed as the qlro entry point. Run qlro --help for the full subcommand list, or qlro <subcommand> --help for per-command options.

$ qlro auth login
Browser-mediated CLI authorisation. Mints a fresh API key bound to your qlro.io account and writes it to ~/.qlro/credentials with mode 0600. The SDK and CLI pick it up automatically on subsequent calls.
Synopsis
qlro auth login
Examples
$ qlro auth login

Standard flow. Opens the browser, prints the verification code, polls until you confirm.

$ QLRO_API_BASE=https://staging.qlro.io qlro auth login

Point at a non-default backend (staging / self-hosted).

Exit codes
0Authorised; key written to ~/.qlro/credentials.
2Failed to start the session (network / backend down).
3Session was consumed by another poller. Run again.
4Session expired before you confirmed in the browser.
5Timed out waiting for browser confirmation (10 min).
See also:qlro.io/settings for the manual env-var path; Authentication for resolution order.
$ qlro recommend
The base recommendation entry point. Takes an OpenQASM 2.0 file (or - for stdin) and ranks every device in the snapshot under the chosen workload category.
Synopsis
qlro recommend <circuit> [--category <cat>] [--all] [--json]
Flags
<circuit> <path>*Path to an OpenQASM 2.0 file. Use - to read from stdin.
--category <name>Workload category: chemistry | optimization | simulation | ml. (default: chemistry)
--allPrint the full ranking table with per-axis breakdown.
--jsonEmit a machine-readable JSON payload after the human summary.

* required.

Examples
$ qlro recommend vqe.qasm --category chemistry
$ qlro recommend vqe.qasm --category optimization --all

Full ranking table with axis breakdown for QAOA-style workloads.

$ cat vqe.qasm | qlro recommend - --json | jq '.rankings[0]'

Pipe stdin + JSON for shell scripting.

Exit codes
0Ranking printed; top pick written to stdout.
1Circuit could not be parsed.
2Snapshot unavailable (network).
$ qlro workload
Procurement-facing entry point. Picks a named template from the catalogue, builds the canonical circuit server-side, and returns the same ranking as qlro recommend — no QASM file required.
Synopsis
qlro workload <template_id> [--list] [--param key=value]...
Flags
<template_id> <id>Catalogue identifier, e.g. industry.pharma.drug_similarity_search.
--listPrint every template id with a one-line description and exit.
--param <key=val>Set one template-specific parameter. Repeatable.
--jsonJSON output instead of the rendered summary.

* required.

Examples
$ qlro workload --list
$ qlro workload industry.pharma.drug_similarity_search --param library_size=50000

Run the drug-similarity template with a 50k library.

$ qlro workload industry.finance.barrier_option_pricing --param expiry=180d --json

Barrier option pricing, JSON output.

Exit codes
0Ranking printed.
1Template id not found.
$ qlro status
Channel-neutral go/no-go check (0.15.0+). Returns a single severity verdict for "is device X ready for workload Y right now?" CI-friendly exit codes power the GitHub Action gate.
Synopsis
qlro status <device> [--workload <cat>]
Flags
<device> <name>*Canonical Metriq device key (ibm_brisbane, iqm_garnet, H2-2, ...).
--workload <cat>Workload category the status is evaluated against. (default: chemistry)

* required.

Examples
$ qlro status ibm_brisbane --workload chemistry
$ qlro status H2-2 --workload optimization || echo "skip submit"

Skip a submit step in shell when the device is amber/red.

Exit codes
0Green — submit is safe.
1Amber — proceed with caution (drift / slow queue).
2Red — do not submit (snapshot stale / device down).
$ qlro lint
Pre-submit circuit linter (0.15.0+). Statically analyses an OpenQASM 2.0 circuit against a target device and reports every rule that would cause a REJECT or silent rewrite at submission time. 12 rules; VSCode-style diagnostic format with severity, source caret, quick-fix hint, and vendor doc URL.
Synopsis
qlro lint <circuit> --device <name> [--shots N] [--region R] [--access P,P] [--json] [--color WHEN]
Flags
<circuit> <path>*Path to an OpenQASM 2.0 file. Use - for stdin.
--device <name>*Canonical device key the lint runs against.
--shots <N>Planned shot count. Enables the SHOTS_OVER_QUOTA rule.
--region <aws-region>Intended AWS region. Enables REGION_MISMATCH rule.
--access <list>Comma-separated provider channels you hold credentials for (ibm, aws, quantinuum, ionq, ...). Enables NO_ACCESS rejection.
--jsonMachine-readable JSON output instead of rendered diagnostics.
--color <when>auto | always | never. Auto colours only when stdout is a TTY. (default: auto)

* required.

Examples
$ qlro lint vqe.qasm --device ibm_brisbane
$ qlro lint vqe.qasm --device H2-2 --shots 2000
$ qlro lint vqe.qasm --device ibm_brisbane --access ibm,aws
$ qlro lint vqe.qasm --device H2-2 --json | jq '.findings[] | select(.severity=="REJECT")'

Pipe JSON into jq to fail the build on any REJECT-severity finding.

Exit codes
0Clean — no findings.
1Warnings only — submit will succeed but results may be unreliable.
2At least one REJECT — submit will be refused by the vendor.
$ qlro doctor
Per-device health check. Surfaces snapshot freshness, recent calibration drift, queue depth (when available), and a single suggested next action. Read-only.
Synopsis
qlro doctor <device>
Flags
<device> <name>*Canonical Metriq device key.

* required.

Examples
$ qlro doctor iqm_garnet
$ qlro doctor ionq_forte
$ qlro calibrate
One-shot calibration of (ε_2q, d_c, ε_ro) against observed fidelity from cheap calibration circuits. Reduces cross-vendor RMSE by 82–94% versus the bare snapshot.
Synopsis
qlro calibrate <device> [--ghz-fidelity F] [--deep-ladder-fidelity F] [--ghz-counts JSON]
Flags
<device> <name>*Canonical Metriq device key.
--ghz-fidelity <F>Observed fidelity of the GHZ-4 calibration circuit (success probability).
--deep-ladder-fidelity <F>Observed fidelity of the Deep Ladder calibration circuit.
--ghz-counts <JSON>GHZ measurement counts as JSON. Enables Level-2 readout calibration.

* required.

Examples
$ qlro calibrate iqm_garnet --ghz-fidelity 0.84

Level-1 calibration from a single GHZ-4 number.

$ qlro calibrate iqm_garnet --ghz-counts '{"0000": 412, "1111": 388, ...}'

Level-2 calibration with full measurement counts.

$ qlro braket-retro
Retrospective analysis of recent AWS Braket tasks. For every task in the window, reconstructs the Qlro recommendation and reports the cost / fidelity delta versus the device the user actually ran on. Read-only — surfaces savings, never submits.
Synopsis
qlro braket-retro [--days N] [--since ISO] [--until ISO] [--region R]
Flags
--days <N>Look back this many days. (default: 30)
--since <ISO>ISO 8601 lower bound. Overrides --days.
--until <ISO>ISO 8601 upper bound. Defaults to now.
--region <aws-region>AWS region (defaults to AWS_DEFAULT_REGION or us-east-1).

* required.

Examples
$ qlro braket-retro --days 30 --region us-east-1
$ qlro braket-retro --since 2026-05-01 --until 2026-06-01

Quarter slice for a procurement review.

$ qlro verify
Verify the Ed25519 signature on a Qlro decision record. Fetches the public key from qlro.io and confirms the bytes have not been altered since issuance. The proof an external auditor needs to trust the report.
Synopsis
qlro verify <decision-record.pdf>
Flags
<decision-record.pdf> <path>*Path to the bilingual PDF Qlro issued.

* required.

Examples
$ qlro verify qlro-decision-rec_xyz.pdf
Exit codes
0Signature valid.
1Signature mismatch — the file has been altered.
2Public key fetch failed.

Global environment variables

QLRO_API_KEYBearer token used for cloud writes. Wins over the credentials file. Set in CI and Docker.
QLRO_API_BASEOverride the backend URL. Defaults to https://qlro-production.up.railway.app.
QLRO_PUBLIC_SITE_URLOverride the public web URL used by qlro auth login. Defaults to https://qlro.io.

REST API reference

Base URL: https://qlro-production.up.railway.app. Authenticated endpoints expect Authorization: Bearer <api_key>. All responses are JSON. Errors return a detail field with a short reason string and an HTTP status code. See Error reference below.

GET/api/v1/workloadsno auth
Returns the full template catalogue. Use this to discover template_id values for recommend_workload calls.
Query parameters
industrystring?Restrict to one of pharma | finance | manufacturing.
Response body (200)
industriesstring[]Industry keys present in the catalogue.
templatesTemplate[]Each carries template_id, display_name, category, industry, description, and a parameter schema.
Example request
curl https://qlro-production.up.railway.app/api/v1/workloads?industry=pharma
Example response
{
  "industries": ["pharma", "finance", "manufacturing"],
  "templates": [
    {
      "template_id": "industry.pharma.drug_similarity_search",
      "display_name": "Drug similarity / virtual screening",
      "category": "ml",
      "industry": "pharma",
      "description": "Virtual screen N library compounds...",
      "parameters": [
        {"name": "library_size", "type": "int", "default": 10000}
      ]
    }
  ]
}
Errors
502Catalogue source unavailable. Transient.
POST/api/v1/recommend/intakeAPI key
The procurement-facing recommendation engine. Translates business vocabulary into circuit-level parameters server-side, runs the WCPP ranker, returns the result with reasons in the requested language. Counts toward the caller's monthly quota.
Request body
template_id*stringCatalogue identifier from GET /workloads.
accuracy_tier*enumapproximate | standard | high_precision.
response_time*enuminteractive | analysis | overnight.
data_scale*enumsmall | typical | large.
total_budget_usdnumber|nullTotal budget cap. null disables cost filtering.
audit_levelenuminternal | procurement | regulated. regulated auto-mints a decision record.
domain_labelstring?Free text (≤200 chars) carried into the decision record for greppability.
Response body (200)
primarystringTop-pick device key.
secondarystring?Runner-up device key.
rankingsRanking[]Per-device fit + axis breakdown + provider channel.
circuit_metaobjectnum_qubits, depth, two_qubit_gates, total_gates derived for this run.
reasonsstring[]Human-readable rationale lines in the requested language.
snapshot_commitstringProvenance hash. Cite this to reproduce later.
Example request
curl -X POST https://qlro-production.up.railway.app/api/v1/recommend/intake \
  -H 'Authorization: Bearer $QLRO_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "template_id": "industry.pharma.drug_similarity_search",
    "accuracy_tier": "standard",
    "response_time": "analysis",
    "data_scale": "typical",
    "total_budget_usd": 1000,
    "audit_level": "internal"
  }'
Errors
401Missing or invalid API key.
404template_id not in the catalogue.
422Validation error on a body field.
429Monthly quota exhausted.
POST/api/v1/predict/recordAPI key
Mint a citable decision record. The response carries every artefact you need to defend the recommendation: citation URL, content hash, BibTeX, methodology DOI. PDFs and ZIP bundle are pulled separately from /predict/record/{id}/pdf and /predict/record/{id}/bundle.zip.
Request body
circuit*object{ n_qubits, depth, n_1q, n_2q, n_measured }.
category*enumchemistry | optimization | simulation | ml.
sourceobject{ kind: 'intake' | 'circuit', template_id?, intake? } — provenance.
Response body (200)
record_idstringULID-shaped record identifier.
citation_urlstringPermanent qlro.io/decision/{id} URL.
recommendationstringTop-pick device the record attests to.
snapshot_doistring?Methodology DOI used at mint time.
content_hashstringSHA-256 of the canonical bundle. Sign with Ed25519.
bibtexstringDrop-in BibTeX entry pinned to the snapshot.
Example request
curl -X POST https://qlro-production.up.railway.app/api/v1/predict/record \
  -H 'Authorization: Bearer $QLRO_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "circuit": {"n_qubits": 4, "depth": 8, "n_1q": 6, "n_2q": 3, "n_measured": 4},
    "category": "chemistry",
    "source": {"kind": "circuit"}
  }'
Errors
401Missing or invalid API key.
422Validation error on a body field.
429Monthly quota exhausted.
POST/api/v1/feedbackAPI key
Submit a (predicted, observed) fidelity outcome. Rate-limited per IP on top of the per-key monthly quota. The source channel (web | cli | sdk | autolog | unknown) is inferred from the User-Agent at submit time and stored on the row.
Request body
device*stringCanonical Metriq device key.
n_qubits*intCircuit width.
depth*intCircuit depth.
n_1q*intSingle-qubit gate count.
n_2q*intTwo-qubit gate count.
n_measured*intNumber of measurement operations.
predicted_fidelity*float [0,1]What Qlro predicted before the run.
observed_fidelity*float [0,1]Ground truth from the actual hardware run.
shotsint?Shot count used.
task_arnstring?Braket task ARN. When set, the server cross-checks observed_fidelity against the AWS-side counts and hides drift > tolerance.
workload_tagenum?chemistry | optimization | simulation | ml. Enables per-workload r-aggregation.
notestring?Free text (≤500 chars). Scrubbed for PII.
Response body (200)
idintServer-assigned row id.
submitted_atstringISO-8601 timestamp.
thanksstringHuman-readable acknowledgement. ARN-verified rows say so explicitly.
Example request
curl -X POST https://qlro-production.up.railway.app/api/v1/feedback \
  -H 'Authorization: Bearer $QLRO_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "device": "ibm_kingston",
    "n_qubits": 4, "depth": 8, "n_1q": 6, "n_2q": 3, "n_measured": 4,
    "predicted_fidelity": 0.62, "observed_fidelity": 0.61,
    "shots": 1024, "workload_tag": "chemistry"
  }'
Errors
401Missing or invalid API key.
422Body failed sanity checks (out-of-range fidelity, malformed counts).
429Per-IP rate limit or monthly quota hit.
GET/api/v1/me/quotaAPI key
Current month usage, tier limit, and reset date for the caller.
Response body (200)
tierstringfree | team | enterprise.
limitint|nullMonthly cap. null on enterprise (unmetered).
usedintCalls billed so far this month.
resets_atstringISO-8601 timestamp of the next reset.
Example request
curl -H 'Authorization: Bearer $QLRO_API_KEY' \
  https://qlro-production.up.railway.app/api/v1/me/quota
Example response
{"tier":"free","limit":500,"used":17,"resets_at":"2026-07-01T00:00:00Z"}
Errors
401Missing or invalid API key.
GET/api/v1/me/accuracyAPI key
Personal predicted-vs-observed history with Pearson r, RMSE, and MAE plus a per-workload breakdown. Powers /my-accuracy. Scope rules mirror /me/runs.
Response body (200)
nintTotal non-hidden rows in scope.
pearson_rfloat|nullLinear correlation predicted-vs-observed. null if n < 3.
rmsefloat|nullRoot mean square error. null if n < 3.
mean_abs_errorfloat|nullMean absolute error. null if n < 3.
recent_pointsPoint[]Latest 500 rows: predicted, observed, submitted_at, device, vendor, workload_tag.
workloadsWorkloadBreakdown[]Per-workload n + pearson_r + rmse + mean_abs_error. Buckets with n < 3 are skipped.
Example request
curl -H 'Authorization: Bearer $QLRO_API_KEY' \
  https://qlro-production.up.railway.app/api/v1/me/accuracy
Errors
401Missing or invalid API key.
GET/api/v1/me/runsAPI key
Every outcome row attributed to the caller, ordered by submitted_at DESC. Powers the /my-runs page. Latest 500 retained. Scope: customer_org_id when the key is partner-bound, otherwise key_hash.
Response body (200)
nintTotal rows returned.
rowsRow[]Each row carries id, submitted_at, device, vendor, n_qubits, depth, n_2q, shots, predicted_fidelity, observed_fidelity, note, workload_tag, source.
source_countsobjectMap of source → count. Drives the per-channel filter pills.
Example request
curl -H 'Authorization: Bearer $QLRO_API_KEY' \
  https://qlro-production.up.railway.app/api/v1/me/runs
Errors
401Missing or invalid API key.
POST/api/v1/oauth/syncserver token
Server-to-server only. Called by the Next.js /api/oauth/sync route after a user signs in via NextAuth. Idempotent on (provider, subject): the raw key is emitted exactly once, on first sync; subsequent calls return only the masked prefix.
Request body
provider*stringOAuth provider id (we use the synthetic 'oauth' for the v5 minimal config).
subject*stringStable provider-side identifier.
email*stringVerified email from the OAuth session.
namestring?Display name from the OAuth profile.
Response body (200)
api_keystringRaw key on first mint, empty string on subsequent sync.
key_prefixstringMasked prefix (12 chars). Always returned.
tierstringfree | team | enterprise.
new_key_mintedbooleanTrue only on the first-sync mint.
Errors
401Missing bearer token.
403Invalid OAuth-sync token.
503QLRO_OAUTH_SYNC_TOKEN not configured server-side.
POST/api/v1/oauth/reissueserver token
Counterpart to /oauth/sync. Revokes every active key for the identity and mints a fresh one. Carries the user's existing tier forward — paid status is not silently downgraded on re-issue.
Request body
provider*stringOAuth provider id.
subject*stringStable provider-side identifier.
email*stringVerified email.
namestring?Display name.
Response body (200)
api_keystringFreshly minted raw key.
key_prefixstringMasked prefix.
tierstringCarried forward from the most-recent revoked row.
revoked_countintHow many active rows were revoked in this call.
Errors
401Missing bearer token.
403Invalid OAuth-sync token.
POST/api/v1/cli-sessionsno auth
Public. Initiated by the qlro auth login CLI command. Returns a session id, an 8-char verification code, and the auth URL to open in the browser. Session TTL is 10 minutes.
Request body
device_namestring?Free text label (≤120 chars) for the calling host. Surfaces on /settings and on the consent screen.
Response body (200)
session_idstring32-byte random token (URL-safe base64).
verification_codestring8-char alphanumeric. Compare it in the browser.
expires_atstringISO-8601 expiry.
auth_urlstringOpen this URL in the user's browser.
POST/api/v1/cli-sessions/{id}/grantserver token
Server-to-server only. Called by the Next.js /api/cli-sessions/{id}/grant route after a logged-in user clicks Authorise CLI. Mints a separate free-tier key bound to the OAuth identity — web and CLI keys coexist.
Request body
verification_code*stringMust match the code attached to the session.
provider*stringOAuth provider id.
subject*stringStable provider-side id.
email*stringVerified email.
Response body (200)
okbooleanAlways true on success.
key_prefixstringMasked prefix of the new key.
device_namestring?Echoed back from session create.
Errors
403Verification code mismatch.
404Unknown session id.
409Session already granted; CLI poll has not fetched yet.
410Session expired or already consumed.
GET/api/v1/cli-sessions/{id}/keyno auth
Public. Polled by the CLI every 2 seconds for up to 10 minutes. The raw key is returned exactly once: on the first successful fetch the row is atomically marked consumed and the stored key wiped.
Response body (200)
statusenumpending | ready | consumed | expired.
api_keystring?Raw key. Only on status=ready.
user_emailstring?Owner email. Only on status=ready.
key_prefixstring?Masked prefix. Only on status=ready.
device_namestring?Label set at session create. Only on status=ready.
expires_atstring?Echoed when status=pending.
Errors
404Unknown session id.

Authentication

How the SDK / CLI resolves the key

Resolution order, evaluated at first use:

text
1. QLRO_API_KEY environment variable
2. ~/.qlro/credentials  (first non-empty line)
3. None — calls run in degraded / anonymous mode where supported

qlro auth login writes the second file with 0600 permissions, so the env var keeps winning whenever it is set (CI and Docker stay deterministic).

Lost your key?

The backend stores only the hash of the original secret;/oauth/sync never re-emits the original key. If your cache is gone (cleared localStorage, new browser, new machine), use the Re-issue API key button on /settings or any gated surface. The old key stops working immediately; tier and quota carry over.

Integrations

Qiskit (native)

python
from qiskit import QuantumCircuit
import qlro

qc = QuantumCircuit(4)
qc.h(0); qc.cx(0, 1); qc.cx(1, 2); qc.cx(2, 3); qc.measure_all()

rec = qlro.recommend(circuit=qc, category="ml")
print(rec.primary)

AWS Braket (autolog wrapper)

python
from braket.aws import AwsDevice
import qlro

# Wrap once at import time
qlro.autolog.braket.enable()

device = AwsDevice("arn:aws:braket:...:device/qpu/iqm/Garnet")
task = device.run(circuit, shots=1024)
result = task.result()
# qlro automatically submits the (predicted, observed) pair

Jupyter magic

python
%load_ext qlro
%%qlro_status ibm_brisbane --workload chemistry
# Renders the live status verdict in the cell, no print() noise.

CI / CD gating

The qlro status and qlro lint subcommands return CI-friendly exit codes. Minimum workflow:

yaml
name: Pre-submit gate
on: [pull_request]
jobs:
  qlro-gate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: pip install qlro
      - env:
          QLRO_API_KEY: ${{ secrets.QLRO_API_KEY }}
        run: |
          qlro lint vqe.qasm --device ibm_brisbane
          qlro status ibm_brisbane --workload chemistry

Decision records

A record bundles the WCPP ranking, the snapshot commit, the input circuit metadata, a bilingual PDF (en + ko), a BibTeX entry, and a ZIP of the inputs. Every record is content-hashed (SHA-256) and Ed25519-signed by qlro.io. Mint from Python:

python
record = qlro.mint_decision_record(
    recommendation=rec,
    audit_level="regulated",     # internal | procurement | regulated
    domain_label="VQE_4q_chem",
)
print(record["citation_url"])   # https://qlro.io/decision/<id>
print(record["bibtex"])          # paste into your bib file

See /audit-anatomy for a clickable walkthrough of an actual record.

Quota & tiers

Free Community: 500 calls / month per key, shared across the website and the CLI. Calls that hit the snapshot-only SDK code path (qlro.recommend(), qlro.recommend_workload() without outcome submission) do not count.

Tier breakdown lives at /pricing; usage is at /settings. At 80% the settings page surfaces a warning; at 100% writes return 429.

Error reference

text
HTTP 401  — Missing or invalid API key.
HTTP 402  — Reserved for paid features the current tier cannot use.
HTTP 403  — Authentication ok, but the operation is not allowed for the role.
HTTP 404  — Decision record / template / snapshot id is unknown.
HTTP 409  — Conflict (e.g. CLI session already granted, awaiting CLI fetch).
HTTP 410  — Resource gone (CLI session consumed / expired; old API key revoked).
HTTP 429  — Monthly quota or per-IP rate limit reached.
HTTP 502  — Backend unreachable through the Next.js proxy. Transient.
HTTP 503  — Server misconfigured (missing shared secret). Report this.

Support & contact

Pilot / enterprise enquiries: official@stockfolio.ai. Bug reports and feature requests: GitHub Issues at github.com/linsletoh/qlro. Operational status is reflected on /dashboard and the public accuracy aggregate at /accuracy.

Documentation tracks SDK version 0.16.0. Older versions: see the GitHub tag history.