n8n-nodes-ibm-quantum
v0.2.3
Published
n8n community node to build, submit and retrieve quantum circuits on the IBM Quantum Platform, with triggers for asynchronous job completion and failure handling
Maintainers
Readme
What it does
The node groups its work into five resources.
| Resource | Operations | | --- | --- | | Backend | List, Get Configuration, Get Properties, Get Status, Get Least Busy | | Circuit | Build (from a gate list), Import OpenQASM 3 | | Job | Submit to Sampler, Submit to Estimator, Get Status, Get Results, Get Logs, Get Metrics, List (with filters), Update Tags, Cancel, Delete | | Session | Create (batch or dedicated), Get, Set Accepting Jobs, Close | | Account | Get Usage, Get Instance, Get Configuration |
It ships three nodes: the main IBM Quantum action node and two polling triggers (IBM Quantum Trigger and IBM Quantum Error Trigger).
Articles
- My IBM Quantum node for n8n is now live — the launch, the n8n verification, and what the three nodes do.
- Running Quantum Circuits on Real IBM Hardware from n8n — an end-to-end Bell state on a real QPU (ibm_kingston), including the transpilation step that trips up most first attempts.
Architecture
The node is a thin REST wrapper with no runtime dependencies. It does not bundle Qiskit or any quantum library. Circuits are expressed as OpenQASM 3 strings, either built by the node from a gate list or passed in directly. The IBM Cloud API key is exchanged for a short-lived IAM bearer token, which n8n caches and refreshes automatically.
Installation
On self-hosted n8n, open the community nodes screen and enter the package name n8n-nodes-ibm-quantum. It is verified by n8n, so it is also installable directly on n8n Cloud.
Prerequisites
- An IBM Cloud account with access to the IBM Quantum Platform
- An IBM Cloud API key
- The Cloud Resource Name (CRN) of your Qiskit Runtime instance
- n8n on a recent version that supports community nodes
Getting your credentials
Create an IBM Quantum API credential in n8n with these fields.
- API Key: in the IBM Cloud console open Manage > Access (IAM) > API keys, create a key, and copy it immediately (it is shown only once). The node exchanges it for a short-lived IAM token at request time.
- Instance CRN: open the IBM Quantum Platform instances page and copy the CRN of your Qiskit Runtime instance. It is sent as the
Service-CRNheader. - Region: pick US East or EU (Germany) to match your instance. This selects the API host.
- API Version: the date sent in the
IBM-API-Versionheader. The response schema is versioned by this date. The default is a known good value that you can update as IBM publishes newer versions.
The credential includes a test that calls the backends endpoint, so you can confirm all four fields at once with the Test button.
Example workflow
A four-node flow that prepares a Bell state, picks a backend, runs it and reads the counts.
- Circuit > Build: Number of Qubits
2, Number of Classical Bits2. Add gates in order: Hadamard on qubits0; CNOT / CX on qubits0,1; Measure on qubits0with Classical Bit0; Measure on qubits1with Classical Bit1. Outputsqasm3,numQubits,numClbits,gateCount. - Backend > Get Least Busy: Minimum Qubits
2, Include Simulators off. OutputsleastBusywith the backend name. - Job > Submit: Primitive Sampler, Backend
={{ $json.leastBusy }}, OpenQASM 3 Circuit={{ $('Build').item.json.qasm3 }}(use the name of your Circuit node), Shots1024. OutputsjobId. - Job > Get Results: Job ID
={{ $json.jobId }}, Poll Interval5, Max Wait300. Outputs the parsedpubs, each carryingcountsfor the Sampler.
Long-running jobs
Real hardware jobs can spend a long time in the queue, sometimes minutes to hours. How you wait for the result matters.
Get Results blocks the execution while it polls. It calls the job endpoint every Poll Interval seconds until the job finishes or Max Wait is reached, holding that one execution open the whole time. That is fine for quick jobs and simulators, but for a long hardware queue it is fragile: if n8n restarts or the run hits a limit, the execution is interrupted and you see "Execution stopped at this node". IBM exposes no push or callback (verified against the API), so something has to poll; the question is whether it blocks a running execution.
The healthy pattern is to decouple submission from result handling with the IBM Quantum Trigger:
- One workflow submits the job and finishes immediately with the
jobId. Nothing blocks. - A second, active workflow starts with the IBM Quantum Trigger. It polls IBM in the background (the n8n scheduler, not a held-open execution) and fires only when a job reaches a terminal state. Its Get Results then returns at once, because the job is already finished.
So polling still happens, but in the background instead of inside a blocking node held open for the whole Max Wait window (300 seconds by default). Use Get Results directly for short jobs and simulators; use Submit plus the trigger for long hardware runs.
IBM does not push notifications, so the trigger polls. Set the interval with the built-in Poll Times field, and choose which terminal status should fire it. The trigger only polls while its workflow is active (toggle Active); for a one-off test use Fetch Test Event. Each poll scans only finished jobs and skips the circuit payloads, so it stays light. If several workflows share one instance, set Tags on the Submit operation and the matching Tag filter on the trigger, so each workflow reacts only to its own jobs.
For production, pair it with the IBM Quantum Error Trigger, which fires only when a job fails or is canceled (queue timeout, a calibration fault, or a manual cancel from the IBM dashboard). It emits the failure reason, reasonCode and reasonSolution from the job, so a second workflow can alert an engineer or fall back to a simulator instead of stalling.
Sessions and batches
Hybrid quantum-classical loops (VQE, QAOA) submit many circuits in sequence, adjusting parameters between iterations. Submitting each as a standalone job sends every iteration back to the general queue. The Session resource avoids that:
- Create a session with mode Batch (queues jobs to run consecutively; the default and the only mode the Open plan allows) or Dedicated (reserves the backend for low-latency back-to-back jobs, paid plans only). It returns a
sessionId. - Pass that
sessionIdinto the Session ID field of each Submit, so the jobs run inside the reservation. - Close the session at the end of the workflow (or set Accepting Jobs to false), so it does not hold the backend.
Use the Account resource (Get Usage) to check usage_consumed_seconds against usage_limit_seconds before launching a large run.
Gate syntax
In the Circuit Build operation each gate has a Qubits field and, for parametric gates, a Parameters field. Both are comma separated.
- Single qubit gates take one qubit index, for example
0. - Two qubit controlled gates take the control first and the target last, for example
0,1for control 0 and target 1. - The Toffoli (CCX) gate takes two controls and a target, for example
0,1,2. - Parametric gates read their angles in radians from the Parameters field. RX, RY, RZ, Phase and the Controlled-R gates take one value; the U gate takes exactly three (theta, phi, lambda).
- The Measure instruction writes to the classical bit given in the Classical Bit field.
Invalid input (wrong number of qubits or parameters, an out-of-range index, or a non-numeric value) is rejected at build time with a clear error, so a malformed program never reaches IBM.
The Build operation outputs a qasm3 string that you pass into a Job Submit node using an expression.
Primitives and options
The Submit operation is split per primitive, since their inputs differ:
- Submit to Sampler returns measurement counts. Set Shots to the number of repetitions.
- Submit to Estimator returns expectation values. Set Observables to a Pauli string whose length matches the qubit count (for example
ZZfor two qubits) or an array of such strings, pick a Resilience Level, and optionally a Precision.
Both share error-suppression toggles that matter on real hardware: Dynamical Decoupling, Gate Twirling and Measurement Twirling. For parametrized circuits, set Parameters to a JSON object binding parameter names to values, e.g. {"theta": 1.5708}. Additional Options is a JSON escape hatch merged into the primitive options, for example {"default_shots": 4096}.
Both Submit operations also accept Tags (comma separated, stored on the job and usable as a filter in Job List and in the triggers) and a Private toggle that hides the job's inputs and results from other collaborators on plans that support private jobs.
Bit order
Sampler counts follow the classical register order. The bit c[0] is the rightmost bit in each output bitstring, which matches the standard Qiskit convention.
Transpilation
This is the single most common reason a real-hardware job fails, so it is worth understanding.
Why it is needed
A textbook circuit uses high-level gates such as h (Hadamard) and cx (CNOT). A real quantum chip does not run those directly. Each backend executes only a small set of native gates (for example a Heron processor like ibm_fez runs rz, sx, x, cz, plus measure and reset), and its qubits are wired in a fixed topology. Translating a circuit into a backend's native gates and connectivity is called transpilation, and the result is an ISA (Instruction Set Architecture) circuit.
The Qiskit Runtime REST API does not transpile. It expects an ISA circuit and rejects anything else. If you submit a raw circuit to a real backend you get a failed job with reason_code: 1517:
The instruction h on qubits (0,) is not supported by the target system.
Transpile your circuits for the target before submitting a primitive query.This is not a node bug. The node builds, submits and reads the job correctly; the hardware refuses a non-ISA circuit.
How to transpile (free, any plan)
Transpile locally with Qiskit, then feed the ISA string into the node. You do not need live credentials: a fake backend carries the real topology and native gate set. Current Qiskit (2.5+) requires Python with NumPy 2.0 or newer.
from qiskit import QuantumCircuit, qasm3
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime.fake_provider import FakeFez # mirrors ibm_fez
backend = FakeFez()
qc = QuantumCircuit(2, 2)
qc.h(0)
qc.cx(0, 1)
qc.measure(0, 0)
qc.measure(1, 1)
isa = generate_preset_pass_manager(optimization_level=1, backend=backend).run(qc)
print(qasm3.dumps(isa)) # paste this ISA string into the nodeFor a real run, swap the fake backend for the live one (QiskitRuntimeService(channel="ibm_cloud", token=..., instance=...).backend("ibm_fez")) so the layout matches the exact device.
A Bell circuit transpiled for ibm_fez comes out in native gates only:
OPENQASM 3.0;
include "stdgates.inc";
bit[2] c;
rz(pi/2) $0;
sx $0;
rz(pi/2) $0;
rz(pi/2) $1;
sx $1;
rz(pi/2) $1;
cz $0, $1;
rz(pi/2) $1;
sx $1;
rz(pi/2) $1;
c[0] = measure $0;
c[1] = measure $1;How to run it in the node
- Put the ISA string into Circuit, Import OpenQASM 3, or paste it straight into the OpenQASM 3 Circuit field of a Submit operation.
- Pin Backend to the exact device you transpiled for (for example
ibm_fez), not Get Least Busy. An ISA circuit is specific to one topology; another device may reject it. - Submit to Sampler or Estimator as usual.
The cloud Transpiler Service (paid plans only)
IBM also offers the Qiskit Transpiler Service, a separate cloud API (https://cloud-transpiler.quantum.ibm.com/transpile) that transpiles remotely, optionally with AI-powered passes. It is only available on the Premium, Flex and On-Prem plans, not the free Open plan, and it lives at a different host from the Qiskit Runtime API, so it is not wired into this node. On the Open plan, transpile locally as shown above.
Simulators
Simulators accept any gate and need no transpilation, so setting Include Simulators on Get Least Busy lets a circuit run as written. Note that the current IBM Quantum Platform has largely retired cloud simulators, so an instance may have none available and fall back to hardware.
Troubleshooting
- 401 or IAM token errors on every call: the API key is wrong, revoked or expired. Regenerate it in IBM Cloud and update the credential.
- 404 or an empty backends list: the Region does not match the region of your instance CRN. US East and EU (Germany) are separate hosts.
- Job never completes in Get Results: large hardware queues can exceed the Max Wait. Raise Max Wait, or poll separately with Get Status and call Get Results once the job is done.
- Job fails with
reason_code: 1517(instruction not supported): the circuit was not transpiled to the backend's native gates. See the Transpilation section above. - Submit rejected by IBM: the Observables length does not match the qubit count, or the circuit is not valid ISA for the chosen backend.
Development
npm install # on Node 24+, use: npm install --ignore-scripts
npm run lint
npm run build
npm testSee CONTRIBUTING.md for the full workflow. Release history lives in CHANGELOG.md and the GitHub Releases page.
Releasing
Releases publish to npm with provenance through the publish.yml GitHub Actions workflow when a GitHub release is created. Authentication uses an npm automation token stored as the NPM_TOKEN repository secret; provenance still works because the repo is public and the job has id-token: write. The package version must match the release tag.
Notes on the live API
The request and response shapes follow the published Qiskit Runtime REST API reference. The job body sends the primitive as program_id, the circuit inside a PUB, and version 2 in params, with resilience_level at the params level for the Estimator. Sampler results are read from results[i].data[register].samples as hex strings, and the least busy backend is chosen from the backends list, which already carries the status, qubit count and queue length for each device.
