Docs / Introduction

RoRo Quantum docs

Build a quantum circuit, run it on real Qiskit simulation, and read an honest result — from the console, the SDK, or the REST API.

#Introduction

RoRo Quantum is a quantum cloud. You design circuits — visually in the console or in code with standard Qiskit — submit them as runs, and get back normalized measurement results. Every run executes for real: simulator targets run on real simulators — Qiskit Aer on our instant local tier, partner-grade simulators in the cloud — and QPU targets queue and execute on real superconducting quantum hardware. The counts you read are the real thing.

New to quantum? In plain words. A normal bit is a coin lying flat — heads (0) or tails (1). A qubit is a coin spinning in the air: a blend of heads and tails at once (this is superposition). A circuit is the set of nudges you give the spinning coins. When you measure, every coin lands on heads or tails; run it many times (shots) and the pattern of landings tells you what your circuit really does. Two coins can also be entangled — linked so they always land in agreement, even when far apart.
Want the real depth? Jump to Core concepts and The math — every section below layers from simple to rigorous.

There are three ways in:

#Architecture

Every entry point speaks to the same core API. The core owns identity, credits, and run records; it hands the actual circuit off to a stateless quantum service, which either simulates it (Qiskit Aer for local simulator targets) or brokers it to a cloud simulator or real quantum hardware, then stores the normalized result.

Console
visual builder
SDK
Python · Kotlin
REST
any language
Core API
auth · credits · runs
Quantum
simulators + real QPUs
Clients → Core API → Quantum service (simulators + real QPUs)
The quantum service is stateless and never touches the database directly — the core is the single source of truth for your runs and credits.

#Quickstart

Create an API key in the console (API keys → Create), then submit your first run.

# 1. install
pip install roro-quantum

# 2. run a Bell state
from roro import RoRoClient
from qiskit import QuantumCircuit

qc = QuantumCircuit(2, 2)
qc.h(0)
qc.cx(0, 1)
qc.measure([0, 1], [0, 1])

roro = RoRoClient(api_key="qcs_live_…")
job = roro.submit_run("roro.sim.sv", shots=5000, circuit=qc, wait=True)
print(job["status"], job["counts"])
# completed {'00': 2481, '11': 2519}
New here? Verifying your email in the console creates your account, a free workspace, and 100 free credits — enough for thousands of shots.
The next Python SDK. roro-quantum is the REST client on PyPI today, and it is deprecated in favour of the platform SDK (roro-quantum-platform 1.0, import roro_platform). The platform SDK already runs in the Lab; its PyPI release is pending. Until then the client above keeps working unchanged.

#Core concepts

TermWhat it means
RunOne submission of a circuit to a target, with a number of shots. Runs are async jobs with a status.
TargetThe machine a run executes on, identified by a targetId such as roro.sim.sv.
ShotsHow many times the circuit is sampled. More shots → smoother statistics, higher cost.
CreditsYour balance. Each run costs a small amount per shot, debited when the run is accepted.
WorkspaceA container for runs and budgets — one per project, class, or team.
OrganizationYour account's top level: members, roles (owner/admin/educator/member), and the credit pool.

#Authentication

The REST API and SDK authenticate with an API key. Create one in the console; it's shown once, so store it safely. Send it as a Bearer token:

Authorization: Bearer qcs_live_…
Keys look like qcs_live_… and are tied to your organization. Treat them like passwords — never commit them to source control. Revoke and rotate keys anytime in the console.

#Building circuits

A circuit is a set of gates on qubit wires, ending in measurements. You can build one visually in the console, or describe it in code and export QASM 2.0 — the format RoRo runs.

q0
H
q1
XM
A Bell state: H on q0, CNOT to q1, then measure
# the QASM 2.0 RoRo runs
OPENQASM 2.0;
include "qelib1.inc";
qreg q[2];
creg c[2];
h q[0];
cx q[0],q[1];
measure q -> c;

#Running circuits

A run needs a target, a shot count, and a circuit as QASM 2.0. The SDK can derive QASM from a Qiskit QuantumCircuit, or you can pass it directly.

roro = RoRoClient(api_key="qcs_live_…")

qasm = """OPENQASM 2.0;
include "qelib1.inc";
qreg q[2]; creg c[2];
h q[0]; cx q[0],q[1];
measure q -> c;"""

job = roro.submit_run("roro.sim.sv", shots=2000, qasm=qasm, wait=True)

#Run lifecycle

When you submit a run, RoRo validates it, reserves and debits credits, executes it on the quantum service, and stores the normalized result. If your balance is too low, the run is rejected before it ever executes.

Submit
target, shots, QASM
Validate
check & price
Reserve
debit credits
Execute
simulator or real QPU
Result
normalized counts
The path of a single run, end to end

Credits, failures & refunds

Billing is settled at the front of the lifecycle, and every resolution is recorded on the run itself:

Watching a live run

Every run record carries four fields beyond the basics that make the lifecycle observable. The two live ones are populated for hardware queues and stay null for simulator runs, which complete instantly:

FieldMeaning
queuePositionJobs ahead of yours on the hardware backend while the run is queued. null for instant simulator runs.
providerStatusThe raw, live status string from the hardware backend while the run is in flight. null for instant simulator runs.
refundabletrue when a failed, charged run has a machine-side error — you may request a refund for it.
refundStatusnull · pending · approved · rejected — the audit trail of a refund, including automatic ones.

Poll GET /v1/runs/{id}, or subscribe to GET /v1/runs/{id}/stream (Server-Sent Events) to get every change — including queuePosition and providerStatus — pushed until the run is terminal.

#Reading results

A completed run returns normalized counts (a histogram of measured bitstrings) and probabilities. The console also derives the most-likely outcome and the Shannon entropy.

00
01
10
11
Bell-state counts — only 00 and 11 appear
{
  "id": "run_…",
  "status": "completed",
  "shots": 2000,
  "counts": { "00": 1001, "11": 999 },
  "probabilities": { "00": 0.5005, "11": 0.4995 }
}

#The math, briefly

You don't need the math to use RoRo — but here's what's happening underneath. A qubit is a vector, a gate is a matrix, and a measurement is a probability.

Superposition. A Hadamard gate creates an equal blend of 0 and 1 — a fair quantum coin:

$$H\,|0\rangle = \tfrac{1}{\sqrt{2}}\big(|0\rangle + |1\rangle\big), \qquad H = \tfrac{1}{\sqrt{2}}\begin{pmatrix}1 & 1\\[2pt] 1 & -1\end{pmatrix}$$

Entanglement. A Hadamard followed by a CNOT produces a Bell state — two qubits whose outcomes are perfectly correlated:

$$|\Phi^{+}\rangle = \tfrac{1}{\sqrt{2}}\big(|00\rangle + |11\rangle\big)$$

Measurement (Born rule). The probability of each outcome is the squared amplitude — exactly what your shot counts estimate:

$$P(x) = \big|\langle x\,|\,\psi\rangle\big|^{2}$$

Entropy. The console reports the Shannon entropy of the outcome distribution — how spread-out the result is. A fair coin gives 1 bit; a certain outcome gives 0:

$$S = -\sum_{x} p_x \log_2 p_x$$

#Credits & pricing

Credits work like tokens. Our own simulators (roro.sim.*, free: true in the machine list) are free — a run there charges nothing. On every other machine a run costs a small amount per shot — plus, on a machine priced by device time (priceModel: "per-second"), an amount per second of the device time its quote projects — debited from your balance when the run is accepted (failed machine-side runs are refunded — see the run lifecycle). You start with 100 free credits; top up anytime, and allocate budgets to members and workspaces. Package prices are public — see the pricing page.

# cost of a run on a machine priced per shot (0 on a machine whose `free` is true)
cost = ceil(shots × target.costPerShot)

# on a machine priced by device time the time terms join in; the seconds are the quote's estimatedSeconds,
# so read the price itself from POST /v1/runs/quote
cost = max(target.minJobCredits, target.costPerJob + shots × target.costPerShot + ceil(seconds) × target.costPerSecond)

#Resilience (error mitigation)

Resilience is an optional run option: you ask the platform to reduce the effect of noise on your result, and it runs a small set of extra circuits in the same run to do it. It is mitigation, not error correction — it post-processes what was measured; it does not protect the qubits while your circuit runs. Your counts and probabilities always stay exactly as the server received them, and the derived distribution arrives separately, in mitigated.

levelWhat runsCircuits
0 (default)Nothing extra — a plain run.1
1Readout mitigation. Two calibration circuits — every measured qubit prepared in 0, then in 1 — run in the same set on the same qubits, and the measured flip rates are inverted bit by bit (a tensored model). This is not TREX: each bit is modelled on its own, so correlated readout errors are not captured. Nothing is cached; the calibration is measured with your run.3
2Readout mitigation + zero-noise extrapolation (ZNE). Your circuit also runs gate-folded at scales 3 and 5 (a scale-k circuit is the same circuit made k× longer), readout mitigation is applied to each scale, and the result is extrapolated linearly to zero noise. Without an observable each outcome's probability is extrapolated from the scales — read that as a heuristic.5

Instead of a level you can set the methods yourself: "readout": true and "zne": {"scales": [1, 3, 5], "extrapolator": "linear", "observable": "ZZ"}. Scales are 2 to 4 odd integers in ascending order, starting at 1, each at most 7; richardson is offered only with an observable, which may contain only Z and I (its rightmost character is classical bit 0 — the rightmost character of a counts key). Explicit fields override the level. An unknown key inside resilience is a 400, because a typo would otherwise re-price your run.

What resilience costs

Resilience is billed in shot-equivalents at the machine's per-shot price: readout adds two calibration circuits, and ZNE bills each scale k× because the folded circuit is k× longer. The quote shows the total before you spend. ZNE circuits that ran are charged even when extrapolation is skipped — you receive their counts. Circuits of a set that did not run are refunded automatically when the run completes.

# what a resilience run bills (the server computes it — read it from the quote)
effectiveShots = shots × (sum of ZNE scales, or 1) + 2 × calibrationShots   # + 2 × … only with readout
cost           = effectiveShots × target.costPerShot   # per-shot machines; a device-time machine adds its time terms

# e.g. level 2 at 1024 shots, calibration 1024 shots: 1024 × (1+3+5) + 2 × 1024 = 11264 shot-equivalents

The platform chooses calibrationShots (never more than your shots); the quote's effectiveShots, circuits, baseCostMilli and resilience block show exactly what will run and what it costs. A machine also publishes resilienceMaxEffectiveShots — the most shot-equivalents one resilience run may bill there.

Where it is offered

Resilience is offered on the noise-model simulators when enabled, and readout on IBM hardware when the operator switches it on. Every machine in GET /v1/machines (and its /properties) says what it offers right now, and why when it offers nothing:

FieldMeaning
resilienceThe methods you can request here right now, e.g. ["readout", "zne"]; [] when none.
resilienceStateoffered · switched-off-by-operator (the machine could offer it, but it is switched off or not available right now) · not-offered (this machine does not offer it — for example the exact simulator, which has no noise to mitigate).
resilienceNoteThe reason, in one sentence, whenever something is not offered.
resilienceMaxEffectiveShotsThe shot-equivalent ceiling of one resilience run; null when nothing is offered.
On a simulator, the calibration measures the simulator's noise model — a representative model of a hardware family, not any specific device. On IBM hardware your layout hint is advisory.

A request a machine cannot honour is refused at the quote (valid: false with validationError) and, with the same text, at submit (400) — nothing is charged. If the platform cannot plan the set right now, the quote is invalid and submit answers 503 resilience quote unavailable — try again; it never guesses a price.

Reading a mitigated result

FieldMeaning
countsProvenanceWhere counts came from: measured — the raw measured counts; provider-corrected — results from provider-corrected hardware are labelled as received (the provider applied its own readout correction, so a second one is not offered); null — as received (older runs, and machines that do not report it).
effectiveShotsThe shot-equivalents billed (equal to shots on a plain run).
resilienceThe resolved plan and how it settled: status is applied, zne-measured-not-extrapolated (the folds ran and are charged; you get their counts), readout-measured-not-applied, partially-applied (some circuits did not run and were refunded — refundedMilli) or not-applied-refunded; plus notes.
mitigatedThe derived result: probabilities, method (readout · zne · readout+zne), negativityClipped, calibration (source: "measured-this-run", per-bit p01/p10) and zne (scales, values, extrapolation), and warnings.
resilienceDetailThe raw histograms behind it — the two calibration histograms and the per-scale counts. Only on GET /v1/runs/{id} and the stream's final done event.
curl -X POST https://api.roroquantum.com/v1/runs/quote \
  -H "Authorization: Bearer qcs_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "machineTargetId": "roro.sim.noisy", "shots": 1024, "qasm": "OPENQASM 2.0; …", "resilience": { "level": 2 } }'
# → {"valid": true, "effectiveShots": 11264, "circuits": 5, "costCredits": …, "resilience": {"calibrationShots": 1024, "zneScales": [1, 3, 5], …}, …}
#   or {"valid": false, "validationError": "…why this machine can't run it…"}

Submitting is the same body on POST /v1/runs. Read the result by its labels: counts are what countsProvenance says they are (measured reads “measured (raw)”, anything else “as received”), and mitigated is null whenever nothing was applied — check resilience.status before you use it:

curl -X POST https://api.roroquantum.com/v1/runs \
  -H "Authorization: Bearer qcs_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "machineTargetId": "roro.sim.noisy", "shots": 1024, "qasm": "OPENQASM 2.0; …", "resilience": { "level": 1 } }'
# then GET /v1/runs/{id} until "status": "completed":
# "countsProvenance": "measured"        → counts are the raw measured histogram
# "resilience": {"status": "applied", …} → "mitigated": {"probabilities": {…}, "method": "readout", …}
# any status where nothing was applied   → "mitigated": null
Not in the SDKs yet. No released SDK carries a resilience argument yet — the published Python client is roro-quantum 0.4.1, and the JavaScript and Kotlin clients are not on a public registry at all. The platform SDK (rq.run(…, resilience={"level": 1})) carries it and runs in the Lab today; its PyPI release is pending. The JavaScript client ignores an option it does not know, so a run submitted through it with resilience is a plain run. Until an SDK release carries the argument, send the REST field above from any HTTP client.

#Targets

Every run executes on a target (a machine), identified by its targetId — such as roro.sim.sv. The catalog changes as machines come and go, so list what's available to you with GET /v1/machines rather than hard-coding ids; when you submit a run, pass the id in the body field machineTargetId (targetId is accepted as an alias).

Machine types

Each machine has a type that tells you what it really is — the honest difference between an exact simulator, a hardware test device, and a real quantum processor. Pick by what you're doing:

typeWhat it isUse it when…
aerAn instant, local, exact simulator. No queue, no device noise.Learning, prototyping, and verifying a circuit is correct before you spend on hardware.
qsimulatorA cloud simulator. Still exact/statistical, but scales to more qubits than a local one.Bigger circuits than the local tier can handle, still with no hardware queue.
qtesterA hardware-adjacent test device — mirrors a processor's gate set, priced like a simulator.Rehearsing a hardware run (gates, topology) without paying hardware prices.
qpuA real quantum processor. Results carry genuine device noise and may queue.You want real hardware results — after the circuit already works on a simulator.
A good workflow: develop on aer, scale on a qsimulator, then move the same circuit to a qpu. You never rewrite anything — the platform transpiles your circuit to whatever gate set the target runs.

Reading a machine

Beyond type, each machine in GET /v1/machines tells you what it can do and whether it's ready right now. The capability fields are vendor-neutral — they describe the physics, not a brand:

FieldMeaning
qubitsHow many qubits you can address.
modalityThe technology: simulator, superconducting, trapped-ion, or neutral-atom.
connectivityHow qubits couple: all-to-all, nearest-neighbour, programmable, or limited.
nativeGatesThe basis the hardware physically runs. Informational — you can build with any gate; the platform transpiles.
availabilityA short human note on how the machine is reached and when it may be busy.

Live status & queue

Hardware isn't always up, and popular processors form a queue. Three fields on each machine tell you what to expect before you submit:

FieldMeaning
onlinetrue when the machine can accept runs right now. Simulators are effectively always online.
statusonline (ready), busy (in high demand — visible but not runnable at the moment), or offline.
queueDepthJobs already queued ahead on that backend, when the machine reports it. Higher = longer wait. null when unknown (e.g. simulators).

Once a run is in flight you can watch its own place in the queue live via queuePosition and providerStatus.

What a run costs

Every machine sets a costPerShot in credits and a free flag. On a machine priced per shot (priceModel: "per-shot") a run's price is ceil(shots × costPerShot); a machine priced by device time (priceModel: "per-second") also lists costPerSecond and minJobCredits, and its price adds the seconds its quote projects. Either way the price is debited when the run is accepted — so more shots (smoother statistics) cost proportionally more, and a bigger, faster, or busier processor costs more per shot than a simulator. Our own simulators are free (free: true, costPerShot: 0); partner simulators are cheap; real processors carry the real cost of the hardware. Preview the exact charge for any (machine, shots) without spending using POST /v1/runs/quote, and see the general model in Credits & pricing.

Example targets

Two always-on simulators to start with — but again, read the live list from GET /v1/machines rather than hard-coding these. For the full fleet and how to call each one, see Machines & how to call them.

Target IDTypeDescription
roro.sim.svSimulatorStatevector simulator — fast, exact sampling, free. The default for learning and prototyping.
roro.sim.noisySimulatorSimulator with a basic noise model, free, for more realistic statistics.

#Machines & how to call them

Every machine has a codename — a short, stable id like iqm.emerald or roro.sim.sv. The codename is the machine's targetId: it's the one thing you pass to run() / submit_run() in the SDK (or machineTargetId over the REST API) to run there. Copy a codename from the Machines page in the console, or read them live from GET /v1/machines.

Two kinds of name. RoRo's own tiers (the free simulators and the flagship RoRo Quantum processor) carry roro.* codenames. The famous third-party QPUs and simulators reached through Amazon Braket keep their real vendor names — Rigetti, IQM, Amazon — with matching codenames like iqm.emerald.

The live machines

A snapshot of the current fleet. Prices and availability change — always read the authoritative costPerShot, online and status from GET /v1/machines before you rely on them.

NameTypeQubitsPer-shot priceSDK codename
RoRo Quantum A1QPU · superconducting600.25 crroro.qpu.q1
RoRo SimulatorSimulator (aer)22freeroro.sim.sv
RoRo Noisy SimulatorSimulator (aer)22freeroro.sim.noisy
RoRo Superconducting ProfileSimulator (aer)22freeroro.sim.noisy.sc
RoRo Trapped-Ion ProfileSimulator (aer)22freeroro.sim.noisy.ion
RoRo Neutral-Atom ProfileSimulator (aer)22freeroro.sim.noisy.atom
IQM EmeraldQPU · superconducting548.00 criqm.emerald
Rigetti Cepheus-1QPU · superconducting1082.125 crrigetti.cepheus
Amazon SV1 (state-vector simulator)Cloud simulator (qsimulator)340.01 cramazon.sv1
Amazon DM1 (density-matrix simulator)Cloud simulator (qsimulator)170.01 cramazon.dm1
IBM Kingston (Heron r2)QPU · superconducting15620.00 cribm.qpu.kingston
IBM Fez (Heron r2)QPU · superconducting15620.00 cribm.qpu.fez
IBM Marrakesh (Heron r2)QPU · superconducting15620.00 cribm.qpu.marrakesh

The Amazon SV1/DM1 simulators and the Rigetti / IQM processors are reached through Amazon Braket; roro.* machines are RoRo's own tiers. Every one is called the same way — by its codename.

Call one by codename

Point submit_run at any codename. Develop on a simulator, then move the same circuit to a QPU by swapping only the codename — the platform transpiles it to whatever gate set the target runs.

# pip install roro-quantum
from roro import RoRoClient
from qiskit import QuantumCircuit

roro = RoRoClient()                 # reads your RORO_API_KEY

qc = QuantumCircuit(2, 2)
qc.h(0); qc.cx(0, 1); qc.measure([0, 1], [0, 1])

# Run on a machine by its codename — here Amazon Braket's SV1 cloud simulator:
job = roro.submit_run("amazon.sv1", shots=1000, circuit=qc, wait=True)
print(job["status"], job["counts"])

# Same circuit, real hardware — just change the codename, e.g. "iqm.emerald":
job = roro.submit_run("iqm.emerald", shots=1000, circuit=qc, wait=True)

To discover codenames at runtime instead of hard-coding them, list the live fleet — each entry's targetId is its codename:

for m in roro.machines():
    rate = "free" if m["free"] else f"{m['costPerShot']} cr/shot"
    print(m["targetId"], "·", m["displayName"], "·", m["qubits"], "qubits ·", rate)

Preview the exact charge for a (codename, shots) pair without spending via POST /v1/runs/quote, and see Targets for the full field-by-field breakdown of what each machine reports.

#REST API

Base URL https://api.roroquantum.com. All endpoints require the Authorization: Bearer header and return JSON.

Prefer an interactive reference? Try the OpenAPI / Swagger explorer — authorize with your key and call endpoints from the browser. Spec: openapi.yaml.

List targets

GET /v1/machines
curl https://api.roroquantum.com/v1/machines \
  -H "Authorization: Bearer qcs_live_…"

Submit a run

POST /v1/runs

Body fields: machineTargetId (string — the target's targetId from the targets list; the field name targetId is accepted as an alias), shots (int), qasm (string, QASM 2.0), and optionally resilience (object — see Resilience).

curl -X POST https://api.roroquantum.com/v1/runs \
  -H "Authorization: Bearer qcs_live_…" \
  -H "Content-Type: application/json" \
  -d '{
    "machineTargetId": "roro.sim.sv",
    "shots": 5000,
    "qasm": "OPENQASM 2.0; include \"qelib1.inc\"; qreg q[2]; creg c[2]; h q[0]; cx q[0],q[1]; measure q -> c;"
  }'

List & fetch runs

GET /v1/runs
GET /v1/runs/{id}
curl https://api.roroquantum.com/v1/runs/run_123 \
  -H "Authorization: Bearer qcs_live_…"

Cancel & stream a run

POST /v1/runs/{id}/cancel
GET /v1/runs/{id}/stream

Cancel stops a queued/running run (hardware jobs are stopped at the provider and the charge is refunded). Stream delivers live run updates over Server-Sent Events until the run is terminal — see Watching a live run.

Price a run without submitting it

POST /v1/runs/quote

Same body as POST /v1/runs, same validation and pricing engine, nothing charged. Returns costCredits, valid (+ validationError), balanceCredits and sufficient. (POST /v1/quote is the same handler under the path the SDKs called first — both work.)

curl -X POST https://api.roroquantum.com/v1/runs/quote \
  -H "Authorization: Bearer qcs_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "machineTargetId": "roro.sim.sv", "shots": 5000, "qasm": "OPENQASM 2.0; …" }'
# → {"costCredits": 25.0, "valid": true, "sufficient": true, "balanceCredits": 100.0, …}

Batch, properties, refund

POST /v1/runs/batch
GET /v1/machines/{id}/properties
POST /v1/runs/{id}/refund

Batch takes {"runs": [ …1–20 run bodies… ]}; each item is validated, priced and charged on its own and reported in its results slot (ok, run or error + errorStatus) — a failed item never rolls the others back (partial: true). Properties returns what to compile against: nativeGates, acceptedGates, minShots/maxShots, connectivity. Refund opens a request for a run that failed on the machine's side and was charged (refundable: true); it returns 201 with the ticket (status: pending) and an operator resolves it — see Credits, failures & refunds.

RoRo Copilot

POST /v1/ai/chat

One metered chat turn, billed to your credits by token usage. {"messages": [{"role": "user", "content": "…"}]} in the Anthropic Messages shape (system, tools, max_tokens, thinking optional; the model is chosen server-side). The reply carries content blocks and a usage object with credits_milli (charged) and balance_milli (after). Add "stream": true for Server-Sent Events: the model's events verbatim, then one trailing roro_usage frame.

#Run statuses

StatusMeaning
queuedAccepted and waiting to execute.
runningExecuting on the quantum service.
completedFinished — counts and probabilities are available.
failedCould not complete. Machine-side failures are refunded automatically; failures caused by the circuit keep the charge — see Credits, failures & refunds.
cancelledStopped by you before it finished; the charge is refunded.

#Errors

Errors use standard HTTP status codes with a JSON body: { "error": "message" }.

CodeMeaning
400Bad request — invalid QASM, missing field, or bad shots value.
401Missing or invalid API key.
402Insufficient credits for the requested run.
404Run or resource not found.
429Too many requests — slow down and retry.
503A machine or a resilience plan is unavailable right now (resilience quote unavailable — try again) — nothing was charged; retry.

#Platform SDK (roro-quantum-platform 1.0)

The Python SDK going forward: one Platform object for circuits, free local simulators, real machines, server-side prices, error mitigation, run history and RoRo Copilot (with an API key — the browser kernels carry the plugin but do not run it, because a copilot turn spends credits and they have no step that asks first) — the same package the Lab runs in your browser. It imports as roro_platform; its command line is roro.

Getting it. The 1.0 release is built and tested but not on PyPI yet — the upload is pending. It is preinstalled in the Lab. Once published it installs as pip install roro-quantum-platform.
from roro_platform import Platform

rq = Platform(api_key="qcs_live_…")       # or RORO_API_KEY; without a key the local simulators still run
BELL = 'OPENQASM 2.0; include "qelib1.inc"; qreg q[2]; creg c[2]; h q[0]; cx q[0],q[1]; measure q -> c;'

print(rq.run(BELL, shots=1000).counts)             # free, local
est = rq.estimate(BELL, shots=1000, target="iqm.emerald")[0]   # the server's quote; nothing is charged
result = rq.run(BELL, shots=1000, target="iqm.emerald")       # real hardware: queued, then streamed to the end
for r in rq.runs(limit=5):                                   # your run history, every surface
    print(r.id, r.status, r.cost_credits)

Example data that says it is example data: roro_platform.synthetic generates business tables (a shift roster, supplier costs, orders, routes) and noisy quantum counts from a stated noise model, each printed under a SYNTHETIC banner and refused by the functions that expect measured data. roro_platform.solve ranks the plans of a yes/no business decision — every plan up to 20 options, a heuristic search above that, and it says which — then optionally samples the decision with QAOA and re-scores every sampled plan exactly. Against an exact ranking the sampling can confirm it or fall short of it, never beat it; against a heuristic one it can find a better plan, and the comparison says the heuristic missed it.

#Python REST client (roro-quantum, deprecated)

The original SDK wraps the REST API with a small, Qiskit-friendly client. It's open source and published on PyPI. It is deprecated in favour of the platform SDK: it keeps working and gets no new features. Its last release, 0.6.0, also says so with a DeprecationWarning; the 0.4.1 on PyPI today predates that warning, and 0.6.0 is published after the platform SDK.

PyPI · roro-quantum roro-quantum 0.4.1 Support

# REST client only
pip install roro-quantum
# + Qiskit BackendV2 provider
pip install "roro-quantum[qiskit]"
from roro import RoRoClient
from qiskit import QuantumCircuit

roro = RoRoClient(api_key="qcs_live_…")

# discover targets
for m in roro.machines():
    print(m["targetId"], m["qubits"])

# build + submit a GHZ state, block until it finishes
qc = QuantumCircuit(3, 3)
qc.h(0); qc.cx(0, 1); qc.cx(1, 2)
qc.measure([0, 1, 2], [0, 1, 2])
job = roro.submit_run("roro.sim.sv", shots=4000, circuit=qc, wait=True)
print(job["counts"])

# or submit without blocking, then poll yourself
job = roro.submit_run("roro.sim.sv", shots=4000, circuit=qc)
job = roro.wait_for_run(job["id"])  # or roro.run(job["id"])

Real hardware is asynchronous — price first, then watch the run live, or stop it:

# what would this cost? (nothing is charged)
q = roro.quote("iqm.emerald", shots=1000, circuit=qc)
print(q["costCredits"], q["sufficient"])

job = roro.submit_run("iqm.emerald", shots=1000, circuit=qc)   # comes back "queued"

# stream every change over SSE — one connection, no poll loop
for r in roro.stream_run(job["id"]):
    print(r["status"], r.get("queuePosition"), r.get("providerStatus"))

# or block with a progress line, or cancel (the provider job stops, the charge is refunded)
done = roro.wait_for_run(job["id"], timeout=600, progress=True)
roro.cancel_run(job["id"])

# also: submit_batch([...]), machine_properties(target), request_refund(id), ai_chat(messages)

Already on Qiskit? Use RoRo as a drop-in BackendV2 provider:

from qiskit import QuantumCircuit
from roro.provider import RoRoProvider

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

backend = RoRoProvider(api_key="qcs_live_…").get_backend("roro.sim.sv")
result = backend.run(qc, shots=5000).result()
print(result.get_counts())
Prefer another language? Every SDK call maps to a plain REST endpoint above — use curl or any HTTP client.

#Kotlin SDK

A typed, dependency-light Kotlin/JVM client for the same REST API — one class, net.mertnode.roroqs.sdk.RoRoClient, built on the JDK's java.net.http plus kotlinx-serialization (Java 21+). It authenticates with the same qcs_live_… API key; circuits are submitted as OpenQASM 2.0 strings.

Getting the SDK. The Kotlin SDK isn't published to a public Maven repository yet — it ships on request from the platform team (as a jar, or as access to the private registry) via support. The API below is the real, current surface and maps to the REST endpoints one-to-one.
import net.mertnode.roroqs.sdk.RoRoClient

val roro = RoRoClient(apiKey = "qcs_live_…")

// discover targets (typed)
roro.machines().forEach { println("${it.targetId}  ${it.qubits}q  ${if (it.free) "free" else "${it.costPerShot} cr/shot"}") }

// submit a Bell state (OpenQASM 2.0) and block until it finishes
val bell = """
    OPENQASM 2.0; include "qelib1.inc";
    qreg q[2]; creg c[2];
    h q[0]; cx q[0],q[1];
    measure q -> c;
""".trimIndent()

val run = roro.submitRun("roro.sim.sv", shots = 5000, qasm = bell, wait = true)
println(run.status)   // completed
println(run.counts)   // {00=2497, 11=2503}

Runs are plain data — poll, stream, cancel and paginate with the same client:

// submit without blocking…
val job = roro.submitRun("roro.sim.sv", shots = 4000, qasm = bell)

// …then poll until terminal (completed / failed / cancelled)
val done = roro.waitForRun(job.id)

// or stream every change over SSE (incl. queuePosition / providerStatus)
roro.streamRun(job.id) { r -> println("${r.status} ${r.queuePosition ?: ""}") }

// cancel a queued/running run (stops the provider job, refunds the charge)
roro.cancelRun(job.id)

// history, newest first
roro.runs(limit = 20).forEach { println("${it.id}  ${it.status}  ${it.cost}") }

Also on the same client: quote(target, shots, qasm) (price without submitting), submitBatch(listOf(RunSpec(…))) (1–20 independent runs), machineProperties(target), requestRefund(id), and aiChat(listOf(ChatMessage("user", "…"))) / streamAiChat(…) for a metered RoRo Copilot turn. A circuit is mandatory: submitRun throws before touching the network when qasm is null or blank.

Types: Machine(targetId, displayName, provider, kind, qubits, costPerShot, online, status, queueDepth, type, modality, connectivity, nativeGates, availability, analog) and Run(id, machineTargetId, machineName, shots, status, cost, counts, probabilities, error, createdAt, qasm, providerJobId, providerStatus, queuePosition, refundable, refundStatus), plus Quote, MachineProperties, BatchRunResponse, RefundRequest, ChatUsage. Any non-2xx response throws RoRoException(status, message), and roro.raw("/v1/…") is the escape hatch for any other GET endpoint.

#JavaScript / TypeScript SDK

An isomorphic Node and browser client for the same REST API — one class, RoRoClient, on the runtime's own fetch, typed end to end, with optional React hooks under @roroquantum/sdk/react. It authenticates with the same qcs_live_… API key; circuits are submitted as OpenQASM 2.0 strings.

Getting the SDK. The JavaScript SDK isn't on npm yet — it ships from the repository: build the tarball in sdks/js (npm install && npm pack) and install that file, or request a copy via support. The API below is the real, current surface and maps to the REST endpoints one-to-one — and it's a couple of fetch calls to the REST API if you'd rather not wait.
import { RoRoClient } from "@roroquantum/sdk";

const roro = new RoRoClient({ apiKey: "qcs_live_…" });

// submit a Bell state (OpenQASM 2.0)
const qasm = `OPENQASM 2.0;
include "qelib1.inc";
qreg q[2]; creg c[2];
h q[0]; cx q[0],q[1];
measure q -> c;`;
const job = await roro.submitAndWait("roro.sim.sv", { shots: 5000, qasm });
console.log(job.status, job.counts);
// completed { '00': 2519, '11': 2481 }

Also on the same client: machines() and machineProperties(target), quote(target, { shots, qasm }) (price without submitting), submitRun / submitBatch (1–20 independent runs), waitForRun(id) and streamRun(id, { onUpdate }) (SSE), cancelRun(id), requestRefund(id), runs({ limit, offset }), and aiChat / streamAiChat for a metered RoRo Copilot turn. Every waiting call takes an AbortSignal. A circuit is mandatory: submitRun throws before touching the network when qasm is missing or blank. Non-2xx responses throw RoRoError (with .status); idempotent GETs retry a network blip or a 5xx, POSTs never do, so a run is never double-submitted.

In React, @roroquantum/sdk/react adds useRoRo, useMachines and useRun over the same client.

#AI assistants (MCP)

RoRo Quantum runs a public Model Context Protocol server, so ChatGPT, Claude, Cursor, VS Code and any other MCP client can read RoRo Academy lessons and the machine catalogue while you chat. It is read-only: no account, no API key, and it never runs a circuit or charges anything.

https://api.roroquantum.com/mcp

Transport: Streamable HTTP. Authentication: none. Tools:

ToolWhat it does
search_lessonsFinds Academy lessons by topic — superposition, Bell states, Grover, error mitigation — in English, Turkish or Arabic.
get_lessonReads one lesson as text: explanations, formulas and example circuits, with a link to the interactive lesson. Quiz answers are never returned.
list_learning_tracksThe Academy's tracks and modules with lesson counts.
list_quantum_computersThe simulators and quantum hardware on RoRo Quantum: qubits, technology, connectivity and live status.
about_roro_quantumWhat RoRo Quantum offers, with links.

Claude

On claude.ai or Claude Desktop: Settings → Connectors → Add custom connector, name it RoRo Quantum and paste the URL above. In Claude Code:

claude mcp add --transport http roro-quantum https://api.roroquantum.com/mcp

ChatGPT

Until the RoRo Quantum plugin appears in ChatGPT's plugin directory, turn on developer mode in ChatGPT's app settings and create a connector with the URL above and no authentication.

Cursor and VS Code

Cursor — ~/.cursor/mcp.json:

{
  "mcpServers": {
    "roro-quantum": { "url": "https://api.roroquantum.com/mcp" }
  }
}

VS Code — .vscode/mcp.json:

{
  "servers": {
    "roro-quantum": { "type": "http", "url": "https://api.roroquantum.com/mcp" }
  }
}

Then ask, for example: “Teach me Grover's algorithm with a RoRo Quantum lesson” or “Which quantum computers can I use on RoRo Quantum?”. What the server receives is described in the privacy policy; for anything else, contact us.

#Glossary

TermDefinition
QubitA quantum bit — the basic unit of quantum information; can be in superposition of 0 and 1.
SuperpositionA qubit being a blend of 0 and 1 until measured. A Hadamard (H) gate creates an equal one.
EntanglementA correlation between qubits so the result of one constrains another — e.g. a Bell state.
GateAn operation on one or more qubits — H, X, CX (CNOT), measurement, and more.
ResilienceOptional error mitigation on a run (readout mitigation, zero-noise extrapolation) — billed as shot-equivalents, quoted first. It is not error correction. See Resilience.
TargetThe machine a run executes on, identified by its targetId (e.g. roro.sim.sv). The run-submit body names this field machineTargetId; targetId is accepted as an alias.
QASMOpenQASM 2.0, the text format describing a circuit, which RoRo executes.
AerQiskit's high-performance simulator — it powers RoRo's instant local simulator targets (machine type aer).
Machine typeEvery machine reports a type: aer = instant local simulator · qsimulator = partner-grade simulator · qtester = hardware test device · qpu = real quantum processor.
QPUA real quantum processor. Runs on qpu targets queue and execute on real superconducting hardware — results include genuine device noise.