Back to Nodes

IBM Quantum (Unofficial)

Last updated Aug 22, 2026

Verified n8n community node for IBM Quantum. Build OpenQASM 3 circuits, submit to Qiskit Runtime, and trigger on job completion or failure. Usable as an AI Agent tool.

187 Weekly Downloads
442 Monthly Downloads

Included Nodes

IBM Quantum (Unofficial)
IBM Quantum (Unofficial) Trigger
IBM Quantum Error (Unofficial) Trigger

Description

n8n-nodes-ibm-quantum, a Bell state measured on ibm_kingston

CI
npm
Downloads
Verified n8n community node
Node
Setup walkthrough
MIT

Unofficial, community-maintained node. Not affiliated with, endorsed by, or sponsored by IBM. IBM Quantum and Qiskit are trademarks of International Business Machines Corporation.

Build, run and retrieve quantum circuits on the IBM Quantum Platform, straight from n8n.

Verified by n8n, so it installs on n8n Cloud as well as self-hosted. Zero runtime dependencies: no Qiskit, no quantum library, nothing to compile. Circuits travel as OpenQASM 3 strings and the IBM Cloud API key is exchanged for a short-lived IAM bearer token that n8n caches and refreshes on its own.

What it does

Three entries in the n8n picker, all carrying an (Unofficial) marker to keep them clearly
distinct from anything IBM publishes. A fourth node, the retired Error Trigger, still ships but is
hidden from the panel so that workflows built on it keep running.

| node | type | what it is for |
| :– | :– | :– |
| IBM Quantum (Unofficial) | action | Every operation below |
| IBM Quantum (Unofficial) Trigger | polling trigger | Fires when a job reaches a terminal state, with the failure reason when there is one |
| IBM Quantum (Unofficial) Tool | AI Agent tool | The same operations, callable by a model. Generated by n8n from the action node, so it needs no separate setup |

33 operations across 6 resources.

| resource | operations |
| :– | :– |
| Backend | Get Many, Get Configuration, Get Defaults, Get Properties, Get Status, Get Least Busy |
| Circuit | Build (from a gate list), Import OpenQASM 3 |
| Job | Submit to Sampler, Submit to Estimator, Submit to Noise Learner, Get Status, Get Results, Get Logs, Get Metrics, Get Many (with filters), Get Many Tags, Update Tags, Cancel, Delete |
| Workload | Get Many (jobs, sessions and batches together, with cursor paging) |
| Session | Create (batch or dedicated), Get, Set Accepting Jobs, Close |
| Account | Get Usage, Get Instance, Get Configuration, Get API Versions, Get Usage Analytics, Get Usage Analytics Grouped, Get Usage Analytics Grouped by Date, Get Usage Analytics Filters |

Five of the six Backend fields are dropdowns filled from your own account, labeled with the device status
and its queue depth, for example ibm_kingston (online, 290 queued). The list is fetched when you
open the node and again on Refresh List in the field menu; it is not polled, so those numbers
are a snapshot rather than a live reading. Switch the field to Expression to type or compute a
name instead, which is what you want when the backend comes from an earlier node such as Get Least
Busy. Only the bare name is stored, so nothing breaks if a label changes.

Searching for Quantum in the n8n trigger picker returns one IBM Quantum entry

One entry in the trigger picker. The retired Error Trigger is hidden, so there is nothing to choose between.

Node details panel listing five triggers and the eight Account actions
The same panel scrolled to the Backend, Circuit and Job actions

Triggers (5) and Actions (33) in one entry: the five job states on the left, then Account, Backend, Circuit and Job as you scroll. Session and Workload follow below.

Use it as an AI Agent tool

The action node sets usableAsTool, so it can be attached to an n8n AI Agent as a tool and the model calls its operations directly. It is a good fit for the read paths, where the agent asks a question and gets a structured answer:

  • “Which QPU has the shortest queue right now?” → Backend, Get Least Busy
  • “How much runtime is left on my instance?” → Account, Get Usage
  • “Did job d1abc finish?” → Job, Get Status
  • Submission works too, but the agent has to supply a circuit the backend accepts. On real hardware that means a transpiled ISA circuit, so pair it with a pre-built circuit rather than asking the model to write one. See Transpilation.

    The two trigger nodes carry no usableAsTool: a polling trigger has nothing for an agent to call, and the verification ruleset now forbids the property on triggers. Until 0.3.3 the same ruleset required it on every node, so n8n listed a tool variant of each trigger that could never actually run; if you still see those variants, ignore them and attach the action node.

    What the model actually reads is the operation’s own action text, not the node’s description: with
    n8n’s default Set Automatically, the tool description is built per operation, which is why the
    three submit actions spell out that the circuit must already be transpiled. Older self-hosted n8n needed N8NCOMMUNITYPACKAGESALLOWTOOLUSAGE=true before any community node could be used as an agent tool. That variable no longer exists in n8n 2.x, so nothing extra is needed there. What remains is N8NCOMMUNITYPACKAGESENABLED, which governs community packages as a whole and is on by default.

    The IBM Quantum Tool attached to an OpenAI node, both showing a successful run

    The tool attached to a model node. Search finds it as IBM Quantum (Unofficial) Tool; the green ticks are a real run where the model called Get Instance Details and answered from the result.

    Docs for AI assistants

    The repository follows the llms.txt convention. llms.txt is the index; llms-full.txt is a complete machine-oriented reference: every operation, every parameter under its internal name, output shapes, error codes, a runnable workflow JSON and the hardware pitfalls. Hand either file to an LLM before asking it to build workflows with this node, and it will stop guessing parameter names.

    Both files ship inside the npm tarball as well as sitting in the repository, so an assistant can reach them three ways: from a checkout, from the installed package, or by URL.

    https://raw.githubusercontent.com/TuguiDragos/n8n-nodes-ibm-quantum/main/llms.txt
    https://raw.githubusercontent.com/TuguiDragos/n8n-nodes-ibm-quantum/main/llms-full.txt
    

    Neither URL is reachable from inside n8n. The Docs link on the node opens this README, because each node class sets documentationUrl and the editor returns that before it ever consults the codex file; the codex entry for llms-full.txt is therefore never read, and llms.txt is not in the codex at all. The MCP server passes on no documentation URL either: what it gives a model is the parameter definitions and each operation’s action text. So paste one of the raw URLs above into an assistant yourself, rather than assuming it will find them.

    AGENTS.md is the companion for agents changing the code rather than using it.

    Install

    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.

    You need

  • 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 version that supports community nodes, running on Node.js 22 or 24
  • Credentials

    Create an IBM Quantum API credential with four fields.

    | field | where it comes from |
    | :– | :– |
    | API Key | IBM Cloud, Manage › Access (IAM) › API keys. Copy it immediately, it is shown once. The node exchanges it for a short-lived IAM token at request time. |
    | Instance CRN | The IBM Quantum Platform instances page. Starts with crn:v1, sent as the Service-CRN header. |
    | Region | US East or EU (Germany), matching your instance. This picks the API host, and the two are separate. The region belongs to the instance, not to the credential: measured against the EU host with a US East CRN, authentication succeeds and only the instance is missing. |
    | API Version | The date in the IBM-API-Version header, which selects the response schema. Defaults to 2026-04-15, the only version IBM does not list as deprecated; change it only when IBM’s REST API reference calls for a newer one. The node checks the field before every run: a value that is not a real date fails immediately with a message naming the field, and an older-but-still-live date logs a warning rather than blocking a credential that works today. Every version before 2026-04-15 has a published sunset during 2027. |

    The credential ships a test that calls the backends endpoint, so the Test button confirms all four fields at once. If you would rather watch it done, the setup walkthrough covers this screen.

    The IBM Quantum API credential in n8n reporting a successful connection test

    All four fields filled, and the green bar after pressing Test. Read our docs opens the table above.

    A first workflow

    4 nodes that prepare a Bell state, pick a backend, run it and read the counts.

    1. Circuit › Build. Number of Qubits 2, Number of Classical Bits 2. Gates in order: Hadamard on 0; CNOT/CX on 0,1; Measure on 0 with Classical Bit 0; Measure on 1 with Classical Bit 1. Outputs qasm3, numQubits, numClbits, gateCount. Rename this node to Build, because step 3 refers to it by name; n8n names it after the operation, Build a circuit from gates, otherwise.
    2. Backend › Get Least Busy. Minimum Qubits 2, Include Simulators off. Outputs leastBusy.
    3. Job › Submit to Sampler. Backend ={{ $json.leastBusy }}, OpenQASM 3 Circuit ={{ $('Build').item.json.qasm3 }}, Shots 1024. Outputs jobId.
    4. Job › Get Results. Job ID ={{ $json.jobId }}, Poll Interval 5, Max Wait 300. Outputs the parsed pubs, each carrying counts.

    This flow uses the textbook h and cx gates so it stays readable, and a real QPU does not run
    those natively: submitted as-is to hardware, the job fails with reason_code: 1517 and still
    spends quota. Before a hardware run, read Transpilation, or build the same Bell
    state from native gates only as shown in
    Building an ISA circuit directly in the node,
    which runs as written.

    Get Least Busy returning ibm_marrakesh with a queue of 3, next to two busier devices

    Step 2 on real devices: ibmmarrakesh wins with 3 queued, against 10 on ibmfez and 294 on ibm_kingston. Every candidate comes back too, so you can rank them differently.

    An end-to-end run

    The same four steps, wired up and run against real hardware, with two extra nodes that read the
    metrics and flatten the answer. Every gate is native, so nothing had to be transpiled first.

    Seven connected nodes, each with a green success tick, passing one item along the chain

    Build, pick a QPU, submit, wait, read the metrics, summarize. Each node hands one item to the next; the backend and the circuit are expressions, not typed in.

    The circuit is built by the node itself. A Bell state needs a Hadamard, which no IBM device runs
    natively, so it is written as rz(pi/2) sx rz(pi/2) and the CNOT becomes cz between two of
    those. Twelve gates, all inside the device’s basis set.

    The Build operation with its gate list, and the generated OpenQASM 3 in the output panel

    Gates go in one row at a time; qasm3, numQubits, numClbits and gateCount come out. This is the circuit the Sampler receives.

    Submitting returns a jobId straight away. Get Results then polls until the job reaches a
    terminal state and parses the samples into counts, and Get Metrics reports what IBM actually
    charged, which is the QPU time alone and not the minutes the job spends being loaded and compiled.

    The final summary showing 4096 shots split between 00 and 11, and 3 QPU seconds charged

    The payoff. 2066 00 and 1950 11 out of 4096 shots, the two correlated outcomes a Bell state should give, and 53 plus 27 shots of real device error. IBM charged 3 QPU seconds for it.

    Long-running jobs

    Real hardware jobs can sit in the queue for minutes or 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. Fine for simulators and quick jobs; fragile for a long hardware queue, because 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 splits submission from result handling.

  • One workflow submits and finishes immediately with the jobId. Nothing blocks.
  • A second, active workflow starts with the IBM Quantum Trigger. It polls in the background on the n8n scheduler, not inside a held-open execution, and fires only when a job reaches a terminal state. Its Get Results returns at once, because the job is already done.
  • Set the cadence with the built-in Poll Times field and choose which terminal status fires it. The trigger only runs while its workflow is active; for a one-off check use Fetch Test Event. Each poll requests pending=false and exclude_params=true, so it scans only finished jobs and skips circuit payloads, which keeps it light and stops a burst of new submissions from pushing a finished job out of the scan window. If several workflows share one instance, set Tags on Submit and the matching Tags filter on the trigger, so each workflow reacts only to its own jobs. The filter takes several comma-separated tags, and a job must carry all of them to match.

    For production alerting, set Trigger On to Failed or Canceled, which ignores successful runs and fires only on a queue timeout, a calibration fault, or a manual cancel from the IBM dashboard. Every event carries reason, reasonCode and reasonSolution from the job’s state, alongside the untouched job, so a workflow can page an engineer or fall back to a simulator instead of stalling. The fields are null when the job did not fail, and IBM leaves them empty for cancellations.

    Earlier releases shipped a separate IBM Quantum Error Trigger for this. It is hidden from the node panel from this release on, but it remains installed and any workflow already using it keeps running unchanged.

    The option above covers the same jobs, and carries the same three reason fields, but it is not a drop-in swap: the two nodes shape their output differently, so moving a workflow across means rewriting the expressions that read it.

    | | Error Trigger | Trigger, Failed or Canceled |
    | :– | :– | :– |
    | the job ID | jobId | id |
    | the status | status, always lowercase, for example cancelled | status as IBM wrote it, for example Cancelled, and absent entirely if IBM omits it |
    | the raw job | nested under job | spread across the top level |
    | the reason fields | reason, reasonCode, reasonSolution | the same three, identical |

    The payloads are deliberately left as they are: changing either would break the workflows already running on it.

    Sessions and batches

    Hybrid 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 in mode Batch (jobs 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 sessionId into the Session ID field of each Submit, so the jobs run inside the reservation.
  • Close the session at the end so it stops holding the backend. Setting Accepting Jobs to false does the same thing and is equally final: IBM ends the session rather than pausing it, and turning the flag back on does not reopen it.
  • Use Account › Get Usage to check usageconsumedseconds against usagelimitseconds before launching a large run.

    Batch is the only mode the Open plan allows. The current plan lineup is Open, Pay-As-You-Go, Flex, Premium and On-Prem, and the Open allowance is 600 seconds per rolling 28 days rather than per calendar month.

    The action list scrolled to the four Session operations and the Workload operation

    The four Session operations, with Workload underneath, at the end of the action list.

    Building circuits

    The Circuit Build operation takes a gate list and emits an OpenQASM 3 string. Each gate has a Qubits field and, for parametric gates, a Parameters field; both are comma separated.

  • Single-qubit gates take one index, for example 0.
  • Controlled gates take the control first and the target last, so 0,1 is control 0 and target 1.
  • Toffoli takes two controls and a target, 0,1,2.
  • Angles are radians. The U gate takes exactly three (theta, phi, lambda).
  • Measure writes to the classical bit given in the Classical Bit field.
  • Supported gates

    | gate | qubits | params | what it is | on IBM hardware |
    | :– | :– | :– | :– | :– |
    | id | 1 | 0 | Identity | accepted, emitted as nothing, see below |
    | x | 1 | 0 | Pauli X | runs as-is |
    | sx | 1 | 0 | Square root of X | runs as-is |
    | y z | 1 | 0 | Pauli Y, Z | transpile first |
    | h | 1 | 0 | Hadamard | transpile first |
    | s sdg | 1 | 0 | Phase π/2 and its inverse | transpile first |
    | t tdg | 1 | 0 | Phase π/4 and its inverse | transpile first |
    | rx rz | 1 | 1 | Rotation about X, Z | runs as-is |
    | ry | 1 | 1 | Rotation about Y | transpile first |
    | p | 1 | 1 | Phase | transpile first |
    | u | 1 | 3 | Generic single-qubit unitary (theta, phi, lambda) | transpile first |
    | cz | 2 | 0 | Controlled-Z | runs as-is |
    | cx | 2 | 0 | CNOT, control first | transpile first |
    | swap | 2 | 0 | Swap | transpile first |
    | crx cry crz | 2 | 1 | Controlled rotation, control first | transpile first |
    | ccx | 3 | 0 | Toffoli, two controls then the target | transpile first |
    | measure | 1 | 0 | Writes to the classical bit you name | runs as-is |
    | reset | 1 | 0 | Reset to |0⟩ | runs as-is |
    | barrier | any | 0 | Optimization barrier; omit qubits for the whole register | directive, always fine |

    Runs as-is means the instruction is in the backend’s own basisgates and reaches the target unchanged, so a circuit made only of those needs no transpiler. On a Heron processor that set is x, sx, rx, rz, cz, plus measure, reset and barrier, which is enough to build real circuits: see Building an ISA circuit directly in the node. Everything marked transpile first is defined by the OpenQASM 3 standard library in terms of the builtin U, or is a two-qubit gate the chip does not implement, and IBM rejects it with the instruction ... is not supported. Check any given backend with Backend › Get Configuration, which returns its basisgates directly.

    The U gate is emitted as the OpenQASM 3 builtin U, uppercase. A lowercase u is not defined in stdgates.inc and IBM’s parser rejects it.

    Identity is the one gate that is accepted and then dropped. stdgates.inc defines id as U(0, 0, 0), and IBM’s target refuses the builtin U, so a circuit containing an identity failed every time with “the instruction u is not supported” even though the backend lists id among its basis gates. Since identity is the no-op, omitting it leaves a mathematically identical circuit that runs. Its operands are still validated, so a bad index is still an error rather than a silent drop.

    Bad input is caught at build time, not at IBM: the wrong number of qubits or parameters, an index outside the register, a multi-qubit gate given the same qubit index twice, a non-numeric value, a measure aimed past the classical register, or a zero, negative or non-integer register size. The error names the gate and what it expected.

    Primitives and options

    Submit is split per program, because their inputs differ.

  • Submit to Sampler returns measurement counts. Set Shots.
  • Submit to Estimator returns expectation values. Set Observables to a Pauli string whose length matches the qubit count (ZZ for two qubits) or an array of them, pick a Resilience Level, and optionally a Precision.
  • Submit to Noise Learner returns the noise itself rather than a result: it characterizes the Pauli-Lindblad error channels on the entangling layers your circuit uses, which is what error mitigation consumes underneath. It takes the same circuit and its own options, and Get Results hands back one noiseLearner entry per learned layer, carrying the qubits it covers and its Pauli generators. Set Number of Classical Bits to 0 on the circuit you feed it: the learner never measures your circuit, and IBM rejects one that carries a classical register as soon as it splits into more than one entangling layer. The node warns when it sees one. Max Layers to Learn, Number of Randomizations and Shots per Randomization can each be left at zero to let IBM choose. Layer Pair Depths is left empty instead, because 0 there is a real depth that gets sent, and Twirling Strategy always sends whichever value is selected. Note that randomizations multiply shots, so this is the easiest way to spend a lot of quota quickly; pair it with Max Cost.
  • Both share the error-suppression toggles that matter on hardware: Dynamical Decoupling, Gate Twirling, Measurement Twirling. Leave Gate Twirling off for a circuit containing fractional gates, meaning a parametrized rx or rzz, which includes anything Qiskit transpiles for a Heron processor: IBM refuses that combination with “gate twirling does not support fractional gates”. The other two have no such restriction. For parametrized circuits, Parameters takes a JSON object binding names to values, e.g. {"theta": 1.5708}. Additional Options is a JSON escape hatch merged into the primitive options, e.g. {"environment": {"loglevel": "DEBUG"}}. The dedicated fields win over anything repeated there: Shots travels with the circuit and overrides defaultshots, and the three toggles above overwrite their own keys.

    Both also accept Tags (comma separated, stored on the job and usable as a filter in Job › Get Many and in both triggers) and a Private toggle that hides the job’s inputs and results from collaborators, on plans that support private jobs.

    Max Cost caps how many runtime seconds a job may consume before IBM cancels it. IBM allows at most 10800 (3 hours) and the node clamps anything larger. Zero, the default, omits the field, and IBM then stamps the job with the plan maximum, which on the Open plan is the entire 600 second allowance for the 28 day window, so leaving it at zero is the worst case rather than a neutral one. Verified live: a job submitted with Max Cost at zero came back carrying cost: 600. Set a real number to stop one runaway job from spending everything. For a ceiling across every job on the instance rather than one, set it on the IBM Cloud console; the node no longer writes it, because IBM’s endpoint for that stopped answering.

    Circuit Format picks how the circuit is written. OpenQASM 3 is the default and the text form the Circuit resource builds. Choosing QPY instead swaps that field for QPY Circuit, which preserves circuits OpenQASM 3 cannot express. The encoding is the one IBM’s own client uses, and it is easy to get wrong: the QPY bytes must be zlib compressed and only then base64 encoded, because the service decompresses them on arrival. In Python that is qiskit.qpy.dump(circuit, buffer) followed by base64.b64encode(zlib.compress(buffer.getvalue())). Base64 of the raw bytes is refused locally, with a message naming the missing step, because IBM otherwise accepts the job and fails it with reason code 1603 after trying to read the text as QASM. Either way the circuit still has to be ISA for the chosen backend, and either way the node validates it locally before spending a submission.

    Finding jobs again

    Job › Get Many returns recent jobs newest first, capped at IBM’s maximum of 200 per call, and takes a Filters collection:

    | filter | what it narrows to |
    | :– | :– |
    | Backend | Jobs that ran on one device |
    | Program | Sampler jobs or Estimator jobs |
    | Tags | Jobs carrying every tag you list, comma separated, up to the 8 the API accepts |
    | Session ID | Jobs that ran inside one session or batch |
    | Status | All, only finished, or only queued and running |
    | Created After / Created Before | A time window |
    | Sort / Offset | Order and paging |
    | Include Circuit Params | Brings each job’s submitted circuit back into the response, which is omitted by default to keep listings small |

    Tags are the practical way to find your own work on a shared instance: set them on Submit, filter on them here and in both triggers. Job › Get Many Tags shows which tags actually exist, narrowed by a search term of 3 to 100 characters, which saves guessing at a name you set weeks ago. IBM offers no way to list every tag, so the term is required; a shorter one is refused locally rather than by a bare 400 from the API.

    Log Level on any Submit raises the verbosity IBM records for that job, and Get Logs reads it back afterwards. Leave it on Default unless you are chasing a failure.

    Workload › Get Many answers the question Job › Get Many cannot: it returns jobs, sessions and batches in one listing, with a free-text Search over IDs and tags and a Mode filter to keep only one kind. It pages by cursor rather than by offset, so a response carries the cursors you feed back into Next Cursor or Previous Cursor, and IBM caps it at 50 per call rather than the 200 Job › Get Many allows.

    Watching the quota

    Account › Get Usage is the quick check. For reporting, the analytics operations return the same spend broken down: Get Usage Analytics for totals, Get Usage Analytics Grouped, whose Group By field takes backend, instance, plan, user or subscription, and Get Usage Analytics Grouped by Date for a time series. All three take the same Filters collection (backends, instances, plans, users, subscriptions, a date window, and whether to count simulators), and Get Usage Analytics Filters lists the values your account may filter on. None of them consume QPU time, so a Schedule Trigger plus Get Usage Analytics Grouped is a cheap monthly spend report.

    An instance-wide spend ceiling is set on the IBM Cloud console rather than from here. The node shipped a Set Cost Limit operation for it until 0.5.0, when it was removed: IBM has deprecated the write endpoint and it no longer answers, so the operation could only ever hang and fail. Account › Get Configuration still reads the current ceiling.

    Reading results

    Bit order. Sampler counts follow the classical register: c[0] is the rightmost bit of each bitstring, the standard Qiskit convention. Samples arrive as hex and are decoded with BigInt, so registers wider than 53 bits keep every bit instead of silently collapsing distinct outcomes. A sample the parser cannot read is dropped rather than folded into a neighboring outcome, and the pub then carries unparsedSamples so the gap between counts and shots is visible instead of silent.

    Get Results detects the classical register on its own. Register Name overrides that, for the rare circuit that declares more than one and you want the counts from a specific register.

    Transpilation

    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 like h and cx. A real chip does not run those. Each backend executes a small set of native gates (a Heron processor such as ibm_fez runs rz, sx, x, cz, plus measure and reset) over a fixed qubit topology. Translating a circuit into that gate set and connectivity is 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. Submit a raw circuit to real hardware and the job fails 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.
    

    That is not a node bug. The node builds, submits and reads the job correctly; the hardware refuses a non-ISA circuit.

    Building an ISA circuit directly in the node

    Transpiling is the general answer, but for small circuits you can skip it entirely: build straight from the native gate set with the Circuit Build operation. The palette covers a Heron processor’s basis apart from rzz, which is enough for the Bell state below and needs no external tooling at all.

    2 identities do most of the work:

    H            = rz(pi/2) . sx . rz(pi/2)            (up to a global phase)
    CNOT(c -> t) = H(t) . cz(c, t) . H(t)
    

    A Bell state built that way, with 12 gate entries and no Qiskit anywhere, runs as-is. Copy this table row by row into the Gates field:

    | gate | qubits | parameters |
    | :– | :– | :– |
    | RZ | 0 | 1.5707963267948966 |
    | SX | 0 | |
    | RZ | 0 | 1.5707963267948966 |
    | RZ | 1 | 1.5707963267948966 |
    | SX | 1 | |
    | RZ | 1 | 1.5707963267948966 |
    | CZ | 0,1 | |
    | RZ | 1 | 1.5707963267948966 |
    | SX | 1 | |
    | RZ | 1 | 1.5707963267948966 |
    | Measure | 0 | Classical Bit 0 |
    | Measure | 1 | Classical Bit 1 |

    Set Number of Qubits and Number of Classical Bits to 2. The circuit the node emits is exactly what Qiskit writes for a transpiled Bell state, and every gate in it is marked runs as-is in the palette.

    Using the right gates is only half of it: a two-qubit gate also has to land on a pair the chip physically connects. cz q[0], q[1] runs on ibm_fez, cz q[0], q[5] does not, and IBM accepts that job, queues it, and only then fails it with code 1517, the instruction cz on qubits (0, 5) is not supported by the target system. The node reads the backend’s coupling map at submit time and says so first, naming the pair and what the qubit does connect to. It is a warning, not a block, and it costs one extra call, skipped entirely for a circuit with no two-qubit gate.

    Measured on ibm_fez over 2048 shots: 48.7% 00 and 46.2% 11, with 5.0% leaking into 01 and 10 from readout error. That is the correlation an entangled pair should show.

    This recipe is deliberately built on sx rather than a parametrized rx. Both give a valid Hadamard on Heron, but rx(pi/2) is a fractional gate, and fractional gates are incompatible with Gate Twirling and with the ZNE and PEC mitigation behind Estimator resilience level 2. IBM’s newer Nighthawk processors (ibmmiami, ibmberlin, on Premium and Flex) run cz, id, rz, sx, x with no fractional rx at all. The sx form above therefore runs unchanged on both fleets and stays compatible with every mitigation option.

    Gates that are safe to use this way: X, SX, RX, RZ, CZ, Reset, Barrier, Measure. sx was added to the palette after being verified on ibmfez: stdgates.inc defines it, so the node emits the bare sx q[n]; that Qiskit itself writes for a transpiled circuit, and IBM runs it. Two of them in a row read 249 of 256 shots as 1, which is X, and a single one reads a 47/53 split, which is the superposition. rzz is the one basis gate the palette still leaves out, and the reason is the definition rather than the gate. stdgates.inc does not define rzz, and the palette writes bare calls with no definitions, so the emitted rzz q[0], q[1]; comes back Failed naming gate 'rzz' is not defined, measured on ibmfez. Do not match on the reason code: that identical program was rejected as 1506 at 04:37 UTC and as 1603 at 17:29 UTC the same day on the same device, so IBM varies it and only the message is dependable. Supplied as your own OpenQASM it runs: the same gate carrying the gate rzz(p0) a, b { cx a, b; rz(p0) b; cx a, b; } block that Qiskit’s exporter writes completed on ibm_fez and returned 64 of 64 shots on 00, the correct reading for a diagonal phase gate on the ground state. The node warns about the bare form and stays quiet about the transpiled one. Everything else in the palette (h, cx, u, swap, ccx, the daggered gates) is defined by the OpenQASM 3 standard library in terms of the builtin U, which hardware rejects, so those need a transpiler pass first. Identity is a special case: it is accepted but emits nothing, because stdgates.inc defines it as U(0, 0, 0) and it would otherwise fail every job it appears in.

    How to transpile, free, on 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+) needs Python with NumPy 2.0 or newer.

    from qiskit import QuantumCircuit, qasm3
    from qiskit.transpiler.presetpassmanagers import generatepresetpassmanager
    from qiskitibmruntime.fakeprovider import FakeFez  # mirrors ibmfez

    backend = FakeFez()

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

    isa = generatepresetpassmanager(optimizationlevel=1, backend=backend).run(qc) print(qasm3.dumps(isa)) # paste this into the node

    For a real run, swap the fake backend for the live one (QiskitRuntimeService(channel="ibmcloud", token=..., instance=...).backend("ibmfez")) 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;
    

    Running it in the node

    1. Put the ISA string into Circuit › Import OpenQASM 3, or paste it into the OpenQASM 3 Circuit field of a Submit operation. That field also takes a list: an expression resolving to an array of circuits submits all of them as one job, which matters because a job costs about two QPU seconds before it runs a single gate.
    2. Pin Backend to the exact device you transpiled for, not Get Least Busy. An ISA circuit is specific to one topology and another device may reject it.
    3. Submit to Sampler or Estimator as usual.

    AI transpiler passes

    IBM used to offer transpilation as a remote REST API. That is gone: the guide for it now answers 410, and the host it lived at no longer resolves. The successor, AI-powered transpiler passes, ships in the qiskit-ibm-transpiler package and, in IBM’s words, runs “on your local environment”. It is in beta. Since there is no longer a service to call, transpiling is a local step on every plan, which is what the recipe above already does.

    Simulators

    Simulators accept any gate and need no transpilation, so 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 and fall back to hardware.

    Troubleshooting

    | symptom | cause and fix |
    | :– | :– |
    | 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. IBM answers code 1279 and names your CRN: “Instance crn:… not found”. The key itself is fine, which is why the error mentions the instance rather than authentication. Verified by calling the EU host with a US East CRN. |
    | Get Results returns timedOut: true and no counts | The job was still queued or running when Max Wait ran out. This is not an error: the job keeps running on IBM, so read it back later with Get Results, raise Max Wait, or submit and use the trigger instead of blocking. |
    | Job fails with reason_code: 1517 | The circuit was not transpiled to the backend’s native gates. See Transpilation. |
    | Submit rejected by IBM | Observables length does not match the qubit count, or the circuit is not valid ISA for the chosen backend. |
    | “is not a YYYY-MM-DD date” | The credential’s API Version field holds something that is not a real date. Set it back to 2026-04-15. Account › Get API Versions lists what IBM currently serves. |
    | Submissions failing in bursts | IBM rate-limits job submission to five per minute per user. Space them out, or run them inside a session. |

    Errors coming back from IBM are unwrapped before they reach you. IBM returns { errors: [{ code, message, solution }] }, which n8n’s default error handling never reads, so it would show a generic “Bad request”. The node pulls the real message out, and IBM’s suggested solution becomes the error description.

    Development

    npm install      # --ignore-scripts skips the isolated-vm native build
    npm run lint     # ESLint with the n8n community node ruleset
    npm run format:check   # Prettier, gated in CI because ESLint does not enforce it
    npm run build    # compile TypeScript, then copy icons and codex files into dist
    npm test         # Vitest, the full unit suite
    npm run test:coverage  # the same suite against the coverage gate
    npm run scan     # the official n8n community package scanner
    

    Lint, formatting, build and coverage run in CI on Node 22 and 24. Coverage is a gate rather than a report: the thresholds are 100 for statements, branches, functions and lines, so an untested line fails the build. isolated-vm, a native transitive dev dependency pulled in by n8n-workflow, is not needed to lint, build or test, which is why install scripts are skipped. The build step runs scripts/copy-assets.mjs after tsc, because TypeScript compiles neither the icons nor the codex .node.json files.

    The scan runs on its own schedule too. The n8n verification ruleset changes independently of this repository, and has already broken a release that was compliant when it shipped, so scan.yml runs the official scanner monthly and on demand rather than waiting for the next publish to discover it.

    See CONTRIBUTING.md and the AGENTS.md if you are pointing an AI coding agent at this repository, SECURITY.md for reporting a vulnerability, and CHANGELOG.md for release history.

    Notes on the live API

    IBM renamed the service IBM Quantum Compute Service in July 2026. The endpoints, headers and payloads are unchanged, so the rename affects only what the documentation calls it.

    Request and response shapes follow the published Qiskit Runtime REST API reference. The job body sends the primitive as programid, the circuit inside a PUB, and version 2 in params, with resiliencelevel at the params level for the Estimator. Sampler results are read from results[i].data[register].samples as hex strings. The least busy backend is chosen from the backends list, which already carries status, qubit count and queue length per device. Array query parameters are sent as repeated keys (tags=a&tags=b), the form the API recognizes; the default bracket encoding is ignored by IBM, which would make a tag filter quietly return everything. Every request the node itself issues carries a 30 second timeout so a hung connection cannot stall an execution. The credential Test button and the IAM token exchange run through n8n’s own helpers and take n8n’s defaults instead.

    IBM’s own limits are worth knowing before a busy workflow meets them: job submission is rate limited to five per minute per user, a job payload may not exceed 50 MB, a single job may run at most 3 hours, and a Sampler job is capped at ten million executions. Job data is retained for 3 years.

    The action node is on typeVersion 2. The only difference from version 1 is the internal name of the session mode parameter, renamed from mode to sessionMode: n8n’s MCP server treats a parameter literally called mode as a node discriminator and drops it from the type definitions it gives AI workflow builders, so on version 1 an agent could not choose between a batch and a dedicated session. Existing version 1 workflows keep working untouched, and both parameters are exercised by the test suite.

    Watch it run

    A full walkthrough: creating the credential, wiring the nodes and running a circuit end to end.

    Setting up and running the IBM Quantum node

    Articles

  • My IBM Quantum node for n8n is now live. The launch, the n8n verification, and what the 3 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.
  • Six defects in a verified n8n node for IBM Quantum. What 121 seconds of real QPU time found that green tests and 98 percent coverage could not, including the identity gate that fails every job despite being listed as native, and why a failed circuit still costs you quota.
  • One circuit, two IBM Quantum error codes: 1506 and 1603. The same rejected program submitted twenty times in a row came back eleven times as one code and nine times as the other, and the job metrics say why: one code arrives from validation in half a second, the other from execution two seconds later, and only one of them names the gate that caused it.

License

MIT  ·  contact@tuguidragos.com

Built with đź–¤ by Čšugui DragoČ™