---
url: /sdk-api-quantum.md
description: >-
  Submit quantum jobs and manage sessions, list and inspect backends, and stream
  results through the HubQuantumClient.
---

# HubQuantumClient

> Part of the [Kipu Quantum Hub API SDK reference](./sdk-api.md) - see the landing page for [installation](./sdk-api.md#installation) and [Python credential helpers](./sdk-api.md#python-credential-helpers).

Use `HubQuantumClient` to run jobs and manage sessions.
It is the most common entry point for users of the SDK.

## Authentication

`HubQuantumClient` authenticates with a personal access token or a service execution token, sent as the `X-Auth-Token` request header.
Tokens are never cached or refreshed by the SDK itself - the caller provides a valid credential and rotates service execution tokens before they expire.

Python ships optional credential helpers in `qhub.api.credentials` that read the token from the environment or the shared `qhubctl` config file (see [Python credential helpers](./sdk-api.md#python-credential-helpers)).
They are not wired into the client constructors automatically; pass the resolved token in via `api_key` yourself.

The TypeScript SDK does not ship credential helpers; read `process.env.KQH_PERSONAL_ACCESS_TOKEN` (or your own variable) and pass it via `apiKey`.

## Organization scoping

Most Quantum endpoints accept an `X-OrganizationId` header to scope the request to a specific organization.
`HubQuantumClient` exposes a dedicated `organization_id` / `organizationId` constructor option that sends the header on every request; in TypeScript, individual sub-client methods also accept a per-request override.
Construct one client per organization if you need to switch at runtime.

The Python `qhub.api.context.ContextResolver` reads the `qhubctl` config file and honours the `KQH_ORGANIZATION_ID` (and legacy `PLANQK_ORGANIZATION_ID`) env vars, but, like credentials, you must feed the resolved id into the client yourself.

## Quickstart

Submit a job, wait for it, and fetch the result.

::: tabs key:pythonTS

\== Python

```python
import time

from qhub.api.quantum import HubQuantumClient
from qhub.api.quantum.jobs import CreateJobRequestInput_AzureIonqSimulator
from qhub.api.quantum.types import AzureIonqJobInputCircuitItem

client = HubQuantumClient(api_key="YOUR_PERSONAL_ACCESS_TOKEN")

job = client.jobs.create_job(
    backend_id="aws.sim.sv1",
    shots=1000,
    input=CreateJobRequestInput_AzureIonqSimulator(
        circuit=[
            AzureIonqJobInputCircuitItem(targets=[0]),
            AzureIonqJobInputCircuitItem(targets=[1], controls=[0]),
        ],
        gateset="qis",
        qubits=2,
    ),
)

while client.jobs.get_job_status(job.id).status in ("PENDING", "RUNNING"):
    time.sleep(2)

print(client.jobs.get_job_result(job.id))
```

\== TypeScript

```ts
import { HubQuantumClient } from "@quantum-hub/qhub-api/quantum";

const client = new HubQuantumClient({
  apiKey: process.env.KQH_PERSONAL_ACCESS_TOKEN!,
});

const job = await client.jobs.createJob({
  backend_id: "aws.sim.sv1",
  shots: 1000,
  input: {
    type: "AZURE_IONQ_SIMULATOR",
    circuit: [{ targets: [0] }, { targets: [1], controls: [0] }],
    gateset: "qis",
    qubits: 2,
  },
});

while (true) {
  const status = await client.jobs.getJobStatus(job.id!);
  if (status.status !== "PENDING" && status.status !== "RUNNING") break;
  await new Promise((r) => setTimeout(r, 2000));
}

console.log(await client.jobs.getJobResult(job.id!));
```

:::

## Constructor

| Parameter                            | Required         | Description                                                                                                                 |
| ------------------------------------ | ---------------- | --------------------------------------------------------------------------------------------------------------------------- |
| `api_key` / `apiKey`                 | yes              | Personal access token or service execution token; sent as `X-Auth-Token`.                                                   |
| `organization_id` / `organizationId` | no               | Value for the `X-OrganizationId` header; sent on every request from this client.                                            |
| `base_url` / `baseUrl`               | no               | Overrides both the default environment and `environment` if supplied.                                                       |
| `environment`                        | no               | `HubQuantumClientEnvironment.DEFAULT` (Python) / `HubQuantumEnvironment.Default` (TypeScript), see [Endpoints](#endpoints). |
| `headers`                            | no               | Additional headers merged into every request.                                                                               |
| `timeout` / `timeoutInSeconds`       | no               | Read timeout in seconds; defaults to 60 when no custom HTTP client is supplied.                                             |
| `max_retries` / `maxRetries`         | no               | Number of retries for transient failures; defaults to 2.                                                                    |
| `follow_redirects`                   | no (Python only) | Passed through to `httpx.Client`; defaults to `True`.                                                                       |
| `httpx_client` / `fetch`             | no               | Inject a preconfigured HTTP client (Python) or `fetch` implementation (TypeScript).                                         |
| `logging`                            | no               | Logger instance or `{level, logger, silent}` config dict.                                                                   |

Python also ships `AsyncHubQuantumClient` with the same shape plus an `httpx.AsyncClient` hook.

::: tabs key:pythonTS

\== Python

```python
from qhub.api.quantum import HubQuantumClient

client = HubQuantumClient(
    api_key="YOUR_PERSONAL_ACCESS_TOKEN",
    organization_id="YOUR_ORGANIZATION_ID",
    timeout=120,
    max_retries=3,
)
```

\== TypeScript

```ts
import { HubQuantumClient } from "@quantum-hub/qhub-api/quantum";

const client = new HubQuantumClient({
  apiKey: process.env.KQH_PERSONAL_ACCESS_TOKEN!,
  organizationId: process.env.KQH_ORGANIZATION_ID,
  timeoutInSeconds: 120,
  maxRetries: 3,
});
```

:::

## Behaviour

* `base_url` / `baseUrl` always wins over `environment`.
* `timeout` is ignored in Python when a preconfigured `httpx_client` is supplied; configure the timeout on the injected client instead.
* `max_retries` applies to transient failures (network errors and 5xx responses); per-request overrides in `request_options` (Python) or per-call options (TypeScript) take precedence.

## Namespaces

`HubQuantumClient` exposes four sub-clients, lazily instantiated on first access.

| Namespace   | Purpose                                                         |
| ----------- | --------------------------------------------------------------- |
| `backends`  | Discover backends and read their configuration and calibration. |
| `sessions`  | Open, inspect, and close quantum sessions.                      |
| `jobs`      | Submit, monitor, retrieve, and cancel jobs (session or not).    |
| `workloads` | List jobs and sessions as a single paginated stream.            |

## Backends

Backends describe the hardware or simulators you can target.

| Method                                                                                                          | Description                                                       |
| --------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- |
| `backends.get_backends(provider=?, only_planqk_sdk=?)` / `backends.getBackends({ provider?, onlyPlanqkSdk? })`  | List backends, optionally filtered by provider.                   |
| `backends.get_backend(id)` / `backends.getBackend(id)`                                                          | Fetch one backend with full configuration metadata.               |
| `backends.get_backend_status(id)` / `backends.getBackendStatus(id)`                                             | Current status (`ONLINE`, `PAUSED`, `OFFLINE`, `RETIRED`).        |
| `backends.get_backend_config(id)` / `backends.getBackendConfig(id)`                                             | Raw backend configuration as a JSON object.                       |
| `backends.get_backend_calibration(id, effective_at=?)` / `backends.getBackendCalibration(id, { effectiveAt? })` | Calibration, optionally at a historical timestamp.                |
| `backends.get_least_busy_backend(provider, min_qubits=?)` / `backends.getLeastBusyBackend({ provider, minQubits? })` | Backend with the lowest queue size for a provider (IBM-only today). |

### get\_backends

List backends, optionally filtered by provider.
Publicly accessible: authenticated callers receive full operational details, unauthenticated callers receive a reduced view.

| Parameter                           | Required | Type                  | Default | Description                                                                                              |
| ----------------------------------- | -------- | --------------------- | ------- | -------------------------------------------------------------------------------------------------------- |
| `provider`                          | no       | `str?` / `string?`    | all     | Filter by `AZURE`, `AWS`, `IBM`, `QRYD`, `QUDORA`, `QUANDELA`, `IQM` (case-insensitive; hyphens become underscores). |
| `only_planqk_sdk` / `onlyPlanqkSdk` | no       | `bool?` / `boolean?`  | `false` | When `true`, return only backends usable via the qhub-quantum SDK.                                       |

::: tabs key:pythonTS

\== Python

```python
for backend in client.backends.get_backends(provider="IBM", only_planqk_sdk=True):
    print(backend.id, backend.queue_size)
```

\== TypeScript

```ts
const backends = await client.backends.getBackends({
  provider: "IBM",
  onlyPlanqkSdk: true,
});
for (const backend of backends) {
  console.log(backend.id, backend.queue_size);
}
```

:::

Returns `List[Backend]` / `Backend[]` — see [Backend](#backend).

### get\_backend

Fetch one backend by id with full configuration metadata.

| Parameter | Required | Type               | Default | Description                              |
| --------- | -------- | ------------------ | ------- | ---------------------------------------- |
| `id`      | yes      | `str` / `string`   | —       | Backend identifier (e.g. `aws.sim.sv1`). |

::: tabs key:pythonTS

\== Python

```python
backend = client.backends.get_backend("aws.sim.sv1")
```

\== TypeScript

```ts
const backend = await client.backends.getBackend("aws.sim.sv1");
```

:::

Returns `Backend` — see [Backend](#backend).

### get\_backend\_status

Lightweight operational state, separate from the rich `Backend` record so callers can poll cheaply.

| Parameter | Required | Type             | Default | Description          |
| --------- | -------- | ---------------- | ------- | -------------------- |
| `id`      | yes      | `str` / `string` | —       | Backend identifier.  |

::: tabs key:pythonTS

\== Python

```python
state = client.backends.get_backend_status("aws.sim.sv1")
print(state.status, state.queue_size, state.queue_avg_time)
```

\== TypeScript

```ts
const state = await client.backends.getBackendStatus("aws.sim.sv1");
console.log(state.status, state.queue_size, state.queue_avg_time);
```

:::

Returns `BackendStateInfo`:

| Field            | Type                       | Description                                                       |
| ---------------- | -------------------------- | ----------------------------------------------------------------- |
| `status`         | `BackendStateInfoStatus?`  | `ONLINE`, `PAUSED`, `OFFLINE`, `RETIRED`, or `UNKNOWN`.           |
| `queue_size`     | `int?`                     | Current queue length on the provider side.                        |
| `queue_avg_time` | `int?`                     | Average queue waiting time in seconds, as reported by the provider. |

### get\_backend\_config

Raw, provider-specific configuration JSON for the backend (gate set, qubit count, coupling map, and so on).

| Parameter | Required | Type             | Default | Description         |
| --------- | -------- | ---------------- | ------- | ------------------- |
| `id`      | yes      | `str` / `string` | —       | Backend identifier. |

::: tabs key:pythonTS

\== Python

```python
config = client.backends.get_backend_config("aws.sim.sv1")
```

\== TypeScript

```ts
const config = await client.backends.getBackendConfig("aws.sim.sv1");
```

:::

Returns a free-form JSON object (`Dict[str, Any]` / `Record<string, unknown>`).

### get\_backend\_calibration

Calibration snapshot at the latest known instant, or at a historical instant if `effective_at` is supplied.
Returns 204 when no calibration exists at or before the requested timestamp.

| Parameter                       | Required | Type                       | Default | Description                                                       |
| ------------------------------- | -------- | -------------------------- | ------- | ----------------------------------------------------------------- |
| `id`                            | yes      | `str` / `string`           | —       | Backend identifier.                                               |
| `effective_at` / `effectiveAt`  | no       | `datetime?` / `Date?`      | latest  | ISO-8601 UTC timestamp; returns the calibration effective at that instant. |

::: tabs key:pythonTS

\== Python

```python
import datetime as dt

cal = client.backends.get_backend_calibration(
    "aws.sim.sv1",
    effective_at=dt.datetime.fromisoformat("2026-04-17T12:34:56+00:00"),
)
```

\== TypeScript

```ts
const cal = await client.backends.getBackendCalibration("aws.sim.sv1", {
  effectiveAt: new Date("2026-04-17T12:34:56Z"),
});
```

:::

Returns `CalibrationResponse?`:

| Field           | Type                                            | Description                                                              |
| --------------- | ----------------------------------------------- | ------------------------------------------------------------------------ |
| `backend_id`    | `str?`                                          | Backend the snapshot belongs to.                                         |
| `calibrated_at` | `datetime?` (ISO 8601)                          | Instant the calibration was effective on the backend.                    |
| `calibration`   | `Dict[str, Any]?` / `Record<string, unknown>?`  | Provider-shaped calibration payload; shape depends on backend technology. |

### get\_least\_busy\_backend

Backend with the lowest reported queue size for a provider.
**IBM-only today** — other providers return 400; 404 if no eligible backend is found.

| Parameter                   | Required | Type                | Default   | Description                                              |
| --------------------------- | -------- | ------------------- | --------- | -------------------------------------------------------- |
| `provider`                  | yes      | `str` / `string`    | —         | Quantum provider (IBM only at present).                  |
| `min_qubits` / `minQubits`  | no       | `int?` / `number?`  | unbounded | Exclude backends with fewer qubits than this.            |

::: tabs key:pythonTS

\== Python

```python
ibm = client.backends.get_least_busy_backend(provider="IBM", min_qubits=5)
print(ibm.id, ibm.queue_size)
```

\== TypeScript

```ts
const ibm = await client.backends.getLeastBusyBackend({
  provider: "IBM",
  minQubits: 5,
});
console.log(ibm.id, ibm.queue_size);
```

:::

Returns `Backend` — see [Backend](#backend).

## Sessions

Sessions group jobs on the same backend to amortize provider startup cost.

```
OPEN ──► ACTIVE ──► DRAINING ──► CLOSED
  │         │
  │         └──► INACTIVE
  └──────────────► ABORTED
```

`UNKNOWN` is used for any state the server has not yet resolved (for example, right after creation).

| Method                                                                                                      | Description                                                                             |
| ----------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
| `sessions.create_session(backend_id, mode, provider, ttl=?, tags=?, metadata=?, sdk_provider=?)`            | Open a new session. `mode` is `batch` or `dedicated`; `ttl` is max lifetime in seconds. |
| `sessions.get_session(id)`                                                                                  | Full session record.                                                                    |
| `sessions.get_session_status(id)`                                                                           | Terse `{status}` view for polling.                                                      |
| `sessions.get_session_jobs(id)`                                                                             | Jobs attached to this session.                                                          |
| `sessions.update_session_state(id, accept_jobs=bool)`                                                       | Stop or resume accepting new jobs.                                                      |
| `sessions.close_session(id)`                                                                                | Close the session; subsequent submissions are rejected.                                 |

TypeScript exposes the same methods in camelCase (`sessions.createSession({ backend_id, mode, provider, ttl?, tags?, metadata?, sdk_provider? })`, `sessions.getSession(id)`, `sessions.getSessionStatus(id)`, `sessions.getSessionJobs(id)`, `sessions.updateSessionState(id, { accept_jobs })`, `sessions.closeSession(id)`).

### create\_session

Open a new session that groups related jobs on a shared backend.
Returns 422 if the selected backend does not support sessions.

| Parameter      | Required | Type                                            | Default          | Description                                                                          |
| -------------- | -------- | ----------------------------------------------- | ---------------- | ------------------------------------------------------------------------------------ |
| `backend_id`   | yes      | `str` / `string`                                | —                | Backend identifier.                                                                  |
| `mode`         | yes      | `SessionMode`                                   | —                | `batch` (queued jobs share priority) or `dedicated` (reserves the backend).          |
| `provider`     | yes      | `SessionProvider`                               | —                | Cloud provider exposing the backend.                                                 |
| `ttl`          | no       | `int?` / `number?`                              | provider default | Max session lifetime in seconds.                                                     |
| `tags`         | no       | `List[str]?` / `string[]?`                      | —                | Free-form labels for categorization or filtering.                                    |
| `metadata`     | no       | `Dict[str, Any]?` / `Record<string, any>?`      | —                | Free-form metadata stored on the session.                                            |
| `sdk_provider` | no       | `SessionSdkProvider?`                           | —                | `QISKIT`, `BRAKET`, `PERCEVAL`, or `CLIENT`.                                         |

::: tabs key:pythonTS

\== Python

```python
session = client.sessions.create_session(
    backend_id="aws.sim.sv1",
    mode="batch",
    provider="AWS",
    ttl=900,
    tags=["demo"],
)
```

\== TypeScript

```ts
const session = await client.sessions.createSession({
  backend_id: "aws.sim.sv1",
  mode: "batch",
  provider: "AWS",
  ttl: 900,
  tags: ["demo"],
});
```

:::

Returns `Session` — see [Session](#session).
Newly-created sessions start non-final and transition through the lifecycle in [Sessions](#sessions).

### get\_session

Fetch a session's full record.

| Parameter | Required | Type             | Default | Description         |
| --------- | -------- | ---------------- | ------- | ------------------- |
| `id`      | yes      | `str` / `string` | —       | Session identifier. |

::: tabs key:pythonTS

\== Python

```python
session = client.sessions.get_session(session_id)
```

\== TypeScript

```ts
const session = await client.sessions.getSession(sessionId);
```

:::

Returns `Session`.

### get\_session\_status

Terse status-only view, cheaper to poll than `get_session`.

| Parameter | Required | Type             | Default | Description         |
| --------- | -------- | ---------------- | ------- | ------------------- |
| `id`      | yes      | `str` / `string` | —       | Session identifier. |

::: tabs key:pythonTS

\== Python

```python
status = client.sessions.get_session_status(session_id).status
```

\== TypeScript

```ts
const { status } = await client.sessions.getSessionStatus(sessionId);
```

:::

Returns `SessionStatusResponse` with one field `status: SessionStatusResponseStatus?` — see [Sessions](#sessions) for the lifecycle.

### get\_session\_jobs

List all jobs that have been submitted to this session.

| Parameter | Required | Type             | Default | Description         |
| --------- | -------- | ---------------- | ------- | ------------------- |
| `id`      | yes      | `str` / `string` | —       | Session identifier. |

::: tabs key:pythonTS

\== Python

```python
jobs = client.sessions.get_session_jobs(session_id)
```

\== TypeScript

```ts
const jobs = await client.sessions.getSessionJobs(sessionId);
```

:::

Returns a list of `Job` records — see [Job](#job).

### update\_session\_state

Toggle whether the session accepts new job submissions.
Set `accept_jobs=false` to drain (finish in-flight jobs, reject new ones) without closing; to terminate, call `close_session` instead.

| Parameter     | Required | Type                | Default | Description                                   |
| ------------- | -------- | ------------------- | ------- | --------------------------------------------- |
| `id`          | yes      | `str` / `string`    | —       | Session identifier.                           |
| `accept_jobs` | yes      | `bool` / `boolean`  | —       | `true` → `ACTIVE`, `false` → `DRAINING`.      |

::: tabs key:pythonTS

\== Python

```python
client.sessions.update_session_state(session_id, accept_jobs=False)
```

\== TypeScript

```ts
await client.sessions.updateSessionState(sessionId, { accept_jobs: false });
```

:::

Returns the updated `Session`.

### close\_session

Close the session and release reserved backend resources.
Already-submitted jobs are left to finish; no new jobs can be submitted once the session is closed.
Idempotent — closing an already-closed session has no effect.

| Parameter | Required | Type             | Default | Description         |
| --------- | -------- | ---------------- | ------- | ------------------- |
| `id`      | yes      | `str` / `string` | —       | Session identifier. |

::: tabs key:pythonTS

\== Python

```python
client.sessions.close_session(session_id)
```

\== TypeScript

```ts
await client.sessions.closeSession(sessionId);
```

:::

No body returned.

### Example - batch session on AWS Braket

::: tabs key:pythonTS

\== Python

```python
session = client.sessions.create_session(
    backend_id="aws.sim.sv1",
    mode="batch",
    provider="AWS",
    ttl=900,
)

try:
    for i in range(5):
        client.jobs.create_job(
            backend_id="aws.sim.sv1",
            shots=500,
            input=my_input,
            session_id=session.id,
            tags=[f"iter-{i}"],
        )
finally:
    client.sessions.close_session(session.id)
```

\== TypeScript

```ts
const session = await client.sessions.createSession({
  backend_id: "aws.sim.sv1",
  mode: "batch",
  provider: "AWS",
  ttl: 900,
});

try {
  for (let i = 0; i < 5; i++) {
    await client.jobs.createJob({
      backend_id: "aws.sim.sv1",
      shots: 500,
      input: myInput,
      session_id: session.id,
      tags: [`iter-${i}`],
    });
  }
} finally {
  await client.sessions.closeSession(session.id!);
}
```

:::

## Jobs

```
PENDING ──► RUNNING ──► COMPLETED
    │          │
    │          ├──► FAILED
    │          ├──► CANCELLING ──► CANCELLED
    │          └──► ABORTED
    └──► CANCELLING ──► CANCELLED
```

`UNKNOWN` covers states the server has not yet resolved.
Terminal states are `COMPLETED`, `FAILED`, `ABORTED`, and `CANCELLED`.

| Method                                                                                                    | Description                                                       |
| --------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- |
| `jobs.search_jobs(page=?, size=?, sort=?, service_execution_id=?)`                                        | Paginated listing, optionally filtered by service execution.      |
| `jobs.create_job(backend_id, shots, input, name=?, input_format=?, input_params=?, tags=?, session_id=?, sdk_provider=?)` | Submit a new job.                                                 |
| `jobs.get_job(id)`                                                                                        | Full job record.                                                  |
| `jobs.get_job_status(id)`                                                                                 | Terse `{status}` view for polling.                                |
| `jobs.get_job_result(id)`                                                                                 | Parsed result body (JSON).                                        |
| `jobs.get_job_result_stream(id)`                                                                          | Stream the result file in chunks (Python iterator / Node stream). |
| `jobs.get_job_input(id)`                                                                                  | Input file (string).                                              |
| `jobs.get_job_calibration(id)`                                                                            | Calibration snapshot captured at execution time.                  |
| `jobs.cancel_job(id)`                                                                                     | Request cancellation; state transitions to `CANCELLING`.          |

TypeScript exposes the same methods in camelCase (`jobs.searchJobs({ page?, size?, sort?, serviceExecutionId? })`, `jobs.createJob({ ... })`, `jobs.getJob(id)`, and so on).

A second family of methods with the `service_execution_` prefix (Python) / `ServiceExecution` suffix (TypeScript) addresses jobs and sessions that belong to a managed service execution - for example, `jobs.get_service_execution_job(service_execution_id, job_id)` / `jobs.getServiceExecutionJob(serviceExecutionId, jobId)`, `jobs.close_service_execution_session(...)` / `jobs.closeServiceExecutionSession(...)`, `jobs.cancel_service_execution_job(...)` / `jobs.cancelServiceExecutionJob(...)`, and so on.
Use the plain methods when you created the job yourself; use the service-execution-scoped methods when the job was created by a managed service on your behalf.

### search\_jobs

Paginated job listing.

| Parameter                                       | Required | Type                                                     | Default        | Description                                                                  |
| ----------------------------------------------- | -------- | -------------------------------------------------------- | -------------- | ---------------------------------------------------------------------------- |
| `page`                                          | no       | `int?` / `number?`                                       | `0`            | Zero-based page index.                                                       |
| `size`                                          | no       | `int?` / `number?`                                       | server default | Page size.                                                                   |
| `sort`                                          | no       | `str \| List[str]?` / `string \| string[]?`              | —              | `field,asc\|desc` Spring-style; pass a sequence for multi-field sort.        |
| `service_execution_id` / `serviceExecutionId`   | no       | `str?` / `string?`                                       | —              | Restrict to jobs created by a managed service execution.                     |

::: tabs key:pythonTS

\== Python

```python
page = client.jobs.search_jobs(page=0, size=20, sort="created_at,desc")
for job in page.content or []:
    print(job.id, job.status)
```

\== TypeScript

```ts
const page = await client.jobs.searchJobs({
  page: 0,
  size: 20,
  sort: "created_at,desc",
});
for (const job of page.content ?? []) {
  console.log(job.id, job.status);
}
```

:::

Returns `PageResponseJob` — see [Pagination wrappers](#pagination-wrappers).

### create\_job

Submit a new job for asynchronous execution.
The returned job starts non-terminal; poll `get_job_status` until it reaches a terminal state, then retrieve the result with `get_job_result`.

| Parameter      | Required | Type                                          | Default  | Description                                                                                                                |
| -------------- | -------- | --------------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------- |
| `backend_id`   | yes      | `str` / `string`                              | —        | Target backend identifier.                                                                                                 |
| `shots`        | yes      | `int` / `number`                              | —        | Number of shots to execute.                                                                                                |
| `input`        | yes      | `CreateJobRequestInput`                       | —        | Backend-specific job input (e.g. `AzureIonqJobInput`, `IbmJobInput`, `AwsAhsJobInput`, `AwsQasm3JobInput`, `IqmJobInput`, `KipuJobInput`, `QudoraJobInput`, `QuandelaJobInput`). |
| `name`         | no       | `str?` / `string?`                            | —        | Human-readable job name.                                                                                                   |
| `input_format` | no       | `JobInputFormat?`                             | inferred | Format of the submitted input; must match the backend's `supportedInputFormats`.                                           |
| `input_params` | no       | `CreateJobRequestInputParams?`                | —        | Backend-specific parameters.                                                                                               |
| `tags`         | no       | `List[str]?` / `string[]?`                    | —        | Free-form labels.                                                                                                          |
| `session_id`   | no       | `str?` / `string?`                            | —        | Attach the job to an open session.                                                                                         |
| `sdk_provider` | no       | `JobSdkProvider?`                             | —        | `QISKIT`, `BRAKET`, `PERCEVAL`, or `CLIENT`.                                                                               |

::: tabs key:pythonTS

\== Python

```python
from qhub.api.quantum.jobs import CreateJobRequestInput_AzureIonqSimulator
from qhub.api.quantum.types import AzureIonqJobInputCircuitItem

job = client.jobs.create_job(
    backend_id="azure.ionq.simulator",
    shots=1000,
    input=CreateJobRequestInput_AzureIonqSimulator(
        circuit=[
            AzureIonqJobInputCircuitItem(targets=[0]),
            AzureIonqJobInputCircuitItem(targets=[1], controls=[0]),
        ],
        gateset="qis",
        qubits=2,
    ),
    name="bell-pair",
    tags=["demo"],
)
```

\== TypeScript

```ts
const job = await client.jobs.createJob({
  backend_id: "azure.ionq.simulator",
  shots: 1000,
  input: {
    type: "AZURE_IONQ_SIMULATOR",
    circuit: [{ targets: [0] }, { targets: [1], controls: [0] }],
    gateset: "qis",
    qubits: 2,
  },
  name: "bell-pair",
  tags: ["demo"],
});
```

:::

Returns `Job` — see [Job](#job).

### get\_job

Full job record, including timestamps, backend, and session affiliation.
Use `get_job_status` when only the lifecycle status is needed.

| Parameter | Required | Type             | Default | Description     |
| --------- | -------- | ---------------- | ------- | --------------- |
| `id`      | yes      | `str` / `string` | —       | Job identifier. |

::: tabs key:pythonTS

\== Python

```python
job = client.jobs.get_job(job_id)
```

\== TypeScript

```ts
const job = await client.jobs.getJob(jobId);
```

:::

Returns `Job`.

### get\_job\_status

Cheap status-only poll.

| Parameter | Required | Type             | Default | Description     |
| --------- | -------- | ---------------- | ------- | --------------- |
| `id`      | yes      | `str` / `string` | —       | Job identifier. |

::: tabs key:pythonTS

\== Python

```python
status = client.jobs.get_job_status(job_id).status
```

\== TypeScript

```ts
const { status } = await client.jobs.getJobStatus(jobId);
```

:::

Returns `JobStatusResponse` with one field `status: JobStatusResponseStatus?` — see [Jobs](#jobs) for the lifecycle.

### get\_job\_result

Measurement results as inline JSON.
Only valid once the job is in a terminal result state (`COMPLETED` or `FAILED`); calling it earlier returns 404.

| Parameter | Required | Type             | Default | Description     |
| --------- | -------- | ---------------- | ------- | --------------- |
| `id`      | yes      | `str` / `string` | —       | Job identifier. |

::: tabs key:pythonTS

\== Python

```python
result = client.jobs.get_job_result(job_id)
```

\== TypeScript

```ts
const result = await client.jobs.getJobResult(jobId);
```

:::

Returns a free-form JSON object (`Dict[str, Any]` / `Record<string, unknown>`); shape depends on the backend.

### get\_job\_result\_stream

Same data as `get_job_result`, streamed as bytes — use for large result files.

| Parameter | Required | Type             | Default | Description     |
| --------- | -------- | ---------------- | ------- | --------------- |
| `id`      | yes      | `str` / `string` | —       | Job identifier. |

Returns `Iterator[bytes]` (Python) / `ReadableStream<Uint8Array>` (TypeScript).
See [Streaming the result file](#streaming-the-result-file) for a runnable example.

### get\_job\_input

The exact input file submitted via `create_job`, returned as a JSON object.

| Parameter | Required | Type             | Default | Description     |
| --------- | -------- | ---------------- | ------- | --------------- |
| `id`      | yes      | `str` / `string` | —       | Job identifier. |

::: tabs key:pythonTS

\== Python

```python
inp = client.jobs.get_job_input(job_id)
```

\== TypeScript

```ts
const inp = await client.jobs.getJobInput(jobId);
```

:::

Returns `Dict[str, Any]` / `Record<string, unknown>`.

### get\_job\_calibration

Calibration snapshot captured at execution time.
Returns `None` / `undefined` when no calibration is available for the job's backend.

| Parameter | Required | Type             | Default | Description     |
| --------- | -------- | ---------------- | ------- | --------------- |
| `id`      | yes      | `str` / `string` | —       | Job identifier. |

::: tabs key:pythonTS

\== Python

```python
cal = client.jobs.get_job_calibration(job_id)
```

\== TypeScript

```ts
const cal = await client.jobs.getJobCalibration(jobId);
```

:::

Returns `CalibrationResponse?` — same shape as [get\_backend\_calibration](#get_backend_calibration).

### cancel\_job

Request cancellation; state transitions to `CANCELLING` and then `CANCELLED`.

| Parameter | Required | Type             | Default | Description     |
| --------- | -------- | ---------------- | ------- | --------------- |
| `id`      | yes      | `str` / `string` | —       | Job identifier. |

::: tabs key:pythonTS

\== Python

```python
client.jobs.cancel_job(job_id)
```

\== TypeScript

```ts
await client.jobs.cancelJob(jobId);
```

:::

No body returned.

### Service-execution-scoped methods

These mirror the plain job and session methods but address workloads owned by a managed service execution.
Use them when the job or session was created by a managed service on your behalf.

| Method                                                                                                                                                                | Description                                                          |
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- |
| `jobs.get_service_execution_workloads(service_execution_id, page=?, size=?, sort=?)` / `jobs.getServiceExecutionWorkloads(serviceExecutionId, { page?, size?, sort? })` | Paginated workloads (jobs + sessions) running inside the execution.  |
| `jobs.get_service_execution_session_status(service_execution_id, session_id)` / `jobs.getServiceExecutionSessionStatus(serviceExecutionId, sessionId)`               | Status of a session inside an execution.                             |
| `jobs.get_service_execution_session_jobs(service_execution_id, session_id)` / `jobs.getServiceExecutionSessionJobs(serviceExecutionId, sessionId)`                   | Jobs belonging to a session inside an execution.                     |
| `jobs.update_service_execution_session_state(service_execution_id, session_id, accept_jobs=bool)` / `jobs.updateServiceExecutionSessionState(serviceExecutionId, sessionId, { accept_jobs })` | Drain or resume a session inside an execution.                       |
| `jobs.close_service_execution_session(service_execution_id, session_id)` / `jobs.closeServiceExecutionSession(serviceExecutionId, sessionId)`                         | Close a session inside an execution.                                 |
| `jobs.get_service_execution_job(service_execution_id, job_id)` / `jobs.getServiceExecutionJob(serviceExecutionId, jobId)`                                             | Fetch one job inside an execution.                                   |
| `jobs.get_service_execution_job_status(service_execution_id, job_id)` / `jobs.getServiceExecutionJobStatus(serviceExecutionId, jobId)`                                | Status-only view for the same job.                                   |
| `jobs.get_service_execution_job_input(service_execution_id, job_id)` / `jobs.getServiceExecutionJobInput(serviceExecutionId, jobId)`                                  | Input JSON for the job.                                              |
| `jobs.get_service_execution_job_result_stream(service_execution_id, job_id)` / `jobs.getServiceExecutionJobResultStream(serviceExecutionId, jobId)`                   | Stream the job's result file.                                        |
| `jobs.get_service_execution_job_calibration(service_execution_id, job_id)` / `jobs.getServiceExecutionJobCalibration(serviceExecutionId, jobId)`                      | Calibration snapshot for the job.                                    |
| `jobs.cancel_service_execution_job(service_execution_id, job_id)` / `jobs.cancelServiceExecutionJob(serviceExecutionId, jobId)`                                       | Cancel the job.                                                      |

::: tabs key:pythonTS

\== Python

```python
workloads = client.jobs.get_service_execution_workloads(
    service_execution_id=execution_id,
    page=0,
    size=50,
    sort="created_at,desc",
)
for w in workloads.content or []:
    print(w.id, w.type, w.status)

job = client.jobs.get_service_execution_job(execution_id, job_id)
```

\== TypeScript

```ts
const workloads = await client.jobs.getServiceExecutionWorkloads(executionId, {
  page: 0,
  size: 50,
  sort: "created_at,desc",
});
for (const w of workloads.content ?? []) {
  console.log(w.id, w.type, w.status);
}

const job = await client.jobs.getServiceExecutionJob(executionId, jobId);
```

:::

Return shapes mirror the plain methods: `PageResponseWorkloadResponse` for the workload listing, `Job` for single-job lookups, `SessionStatusResponse` and `JobStatusResponse` for the status-only variants, `Iterator[bytes]` / `ReadableStream<Uint8Array>` for the result stream, `Dict[str, Any]` / `Record<string, unknown>` for the input file, `CalibrationResponse?` for calibration, and no body for cancel and close.

### Polling for completion

::: tabs key:pythonTS

\== Python

```python
import time

TERMINAL = {"COMPLETED", "FAILED", "ABORTED", "CANCELLED"}

def wait(job_id: str, poll_interval: float = 2.0) -> str:
    while True:
        status = client.jobs.get_job_status(job_id).status
        if status in TERMINAL:
            return status
        time.sleep(poll_interval)

final = wait(job.id)
if final == "COMPLETED":
    result = client.jobs.get_job_result(job.id)
```

\== TypeScript

```ts
const TERMINAL = new Set(["COMPLETED", "FAILED", "ABORTED", "CANCELLED"]);

async function wait(jobId: string, pollMs = 2000): Promise<string> {
  for (;;) {
    const { status } = await client.jobs.getJobStatus(jobId);
    if (status && TERMINAL.has(status)) return status;
    await new Promise((r) => setTimeout(r, pollMs));
  }
}

const final = await wait(job.id!);
if (final === "COMPLETED") {
  const result = await client.jobs.getJobResult(job.id!);
}
```

:::

### Streaming the result file

Large results are better consumed as a byte stream; the `*_stream` / `*Stream` variants hand you chunks rather than parsing the full JSON into memory.

::: tabs key:pythonTS

\== Python

```python
with open("result.json", "wb") as out:
    for chunk in client.jobs.get_job_result_stream(job.id):
        out.write(chunk)
```

\== TypeScript

```ts
import { Writable } from "node:stream";
import { createWriteStream } from "node:fs";

const stream = await client.jobs.getJobResultStream(job.id!);
await stream.pipeTo(Writable.toWeb(createWriteStream("result.json")));
```

:::

## Workloads

`workloads.get_workloads(...)` / `workloads.getWorkloads({ ... })` returns a single paginated list mixing jobs and sessions, tagged with a `type` discriminator (`JOB` or `SESSION`).
Use it when you want a uniform "show me everything running" view; otherwise reach for `jobs.search_jobs` or list sessions individually.

### get\_workloads

| Parameter | Required | Type                                         | Default        | Description                                                            |
| --------- | -------- | -------------------------------------------- | -------------- | ---------------------------------------------------------------------- |
| `page`    | no       | `int?` / `number?`                           | `0`            | Zero-based page index.                                                 |
| `size`    | no       | `int?` / `number?`                           | server default | Page size.                                                             |
| `sort`    | no       | `str \| List[str]?` / `string \| string[]?`  | —              | `field,asc\|desc` Spring-style; pass a sequence for multi-field sort.  |

::: tabs key:pythonTS

\== Python

```python
page = client.workloads.get_workloads(page=0, size=50, sort="created_at,desc")
for w in page.content or []:
    print(w.id, w.type, w.backend_id, w.status)
```

\== TypeScript

```ts
const page = await client.workloads.getWorkloads({
  page: 0,
  size: 50,
  sort: "created_at,desc",
});
for (const w of page.content ?? []) {
  console.log(w.id, w.type, w.backend_id, w.status);
}
```

:::

Returns `PageResponseWorkloadResponse`.
Each `WorkloadResponse` has:

| Field                                       | Type                            | Description                                                                       |
| ------------------------------------------- | ------------------------------- | --------------------------------------------------------------------------------- |
| `id`                                        | `str?`                          | Resolves to a job id or session id depending on `type`.                           |
| `type`                                      | `WorkloadResponseType?`         | `JOB` or `SESSION`.                                                               |
| `backend_id`                                | `str?`                          | Backend the workload targets.                                                     |
| `provider`                                  | `WorkloadResponseProvider?`     | Cloud provider exposing the backend.                                              |
| `status`                                    | `str?`                          | `JobStatus` value when `type=JOB`, `SessionStatus` value when `type=SESSION`.     |
| `created_at` / `started_at` / `ended_at`    | `str?` (ISO 8601)               | Lifecycle timestamps.                                                             |
| `sdk_provider`                              | `WorkloadResponseSdkProvider?`  | SDK that produced the workload.                                                   |

## Advanced usage

### Running N jobs in parallel

::: tabs key:pythonTS

\== Python

```python
import asyncio

from qhub.api.quantum import AsyncHubQuantumClient

async def run_batch(inputs):
    client = AsyncHubQuantumClient(api_key="YOUR_TOKEN")
    jobs = await asyncio.gather(*(
        client.jobs.create_job(backend_id="aws.sim.sv1", shots=1000, input=i)
        for i in inputs
    ))
    return [j.id for j in jobs]
```

\== TypeScript

```ts
import { HubQuantumClient } from "@quantum-hub/qhub-api/quantum";

async function runBatch(client: HubQuantumClient, inputs: unknown[]) {
  const jobs = await Promise.all(
    inputs.map((input) =>
      client.jobs.createJob({ backend_id: "aws.sim.sv1", shots: 1000, input }),
    ),
  );
  return jobs.map((j) => j.id!);
}
```

:::

### Polling with exponential backoff

::: tabs key:pythonTS

\== Python

```python
import random, time

def wait_with_backoff(job_id, initial=1.0, cap=30.0):
    delay = initial
    while True:
        status = client.jobs.get_job_status(job_id).status
        if status in ("COMPLETED", "FAILED", "ABORTED", "CANCELLED"):
            return status
        time.sleep(delay + random.uniform(0, delay * 0.1))
        delay = min(delay * 2, cap)
```

\== TypeScript

```ts
async function waitWithBackoff(jobId: string, initialMs = 1000, capMs = 30000) {
  let delay = initialMs;
  for (;;) {
    const { status } = await client.jobs.getJobStatus(jobId);
    if (
      status &&
      ["COMPLETED", "FAILED", "ABORTED", "CANCELLED"].includes(status)
    ) {
      return status;
    }
    await new Promise((r) =>
      setTimeout(r, delay + Math.random() * delay * 0.1),
    );
    delay = Math.min(delay * 2, capMs);
  }
}
```

:::

### Fetch passthrough (TypeScript)

The TypeScript client exposes `client.fetch(input, init?, requestOptions?)`, which reuses the client's base URL, auth provider, retry policy, and logging to call an endpoint that does not yet have a typed wrapper.
Python does not ship a public equivalent; for that case, use `client._client_wrapper` at your own risk or drop down to `httpx` directly.

## Endpoints

| Default base URL                           | Override                                                                                                                                  |
| ------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `https://api.hub.kipu-quantum.com/quantum` | `base_url=...` / `baseUrl: ...`, or `environment=HubQuantumClientEnvironment.DEFAULT` (Python) / `environment: HubQuantumEnvironment.Default` (TypeScript). |

To target a staging or on-prem deployment, set `base_url` / `baseUrl` at construction time.

## Errors

HTTP errors are raised as typed exceptions.
All Python errors extend `ApiError` (in `qhub.api.quantum.core.api_error`); all TypeScript errors extend `HubQuantumError`.

| Status | Python class                | TypeScript class            | Meaning                                 |
| ------ | --------------------------- | --------------------------- | --------------------------------------- |
| 400    | `BadRequestError`           | `BadRequestError`           | Malformed request.                      |
| 401    | `UnauthorizedError`         | `UnauthorizedError`         | Missing or invalid credentials.         |
| 403    | `ForbiddenError`            | `ForbiddenError`            | Authenticated but not permitted.        |
| 404    | `NotFoundError`             | `NotFoundError`             | Resource does not exist.                |
| 422    | `UnprocessableEntityError`  | `UnprocessableEntityError`  | Validation failure on the request body. |
| 500    | `InternalServerError`       | `InternalServerError`       | Server-side failure.                    |

Other failure modes:

* **Auth misconfiguration (TypeScript)** - constructing `HubQuantumClient` without `apiKey` throws `HubQuantumError` with message `"Please provide 'apiKey' when initializing the client"`.
* **Timeouts** - hitting `timeoutInSeconds` raises `HubQuantumTimeoutError` in TypeScript; in Python, timeouts surface as the underlying `httpx.TimeoutException`.
* **Missing base URL** - passing `environment=None` *and* omitting `base_url` to the Python constructor raises `Exception("Please pass in either base_url or environment to construct the client")`.

All HTTP error instances expose `status_code`, `headers`, and `body` (Python) / `statusCode`, `rawResponse`, and `body` (TypeScript) so you can inspect the server's response detail.

::: tabs key:pythonTS

\== Python

```python
from qhub.api.quantum.errors import NotFoundError, UnauthorizedError

try:
    client.jobs.get_job("does-not-exist")
except NotFoundError as e:
    print("missing", e.status_code, e.body)
except UnauthorizedError:
    print("refresh your token")
```

\== TypeScript

```ts
import { HubQuantumError } from "@quantum-hub/qhub-api/quantum";

try {
  await client.jobs.getJob("does-not-exist");
} catch (err) {
  if (err instanceof HubQuantumError) {
    console.error(err.statusCode, err.body);
  } else {
    throw err;
  }
}
```

:::

## Reference

### Job

| Field                                    | Type              | Description                                         |
| ---------------------------------------- | ----------------- | --------------------------------------------------- |
| `id`                                     | `str?`            | Unique job identifier.                              |
| `name`                                   | `str?`            | Optional human-readable name.                       |
| `backend_id`                             | `str?`            | Backend the job runs on.                            |
| `provider`                               | `JobProvider?`    | Provider enum (see below).                          |
| `provider_job_id`                        | `str?`            | Provider-assigned job id.                           |
| `input_params`                           | `JsonNode?`       | Backend-specific parameters supplied at submission. |
| `input_format`                           | `JobInputFormat?` | Format of the submitted input.                      |
| `tags`                                   | `List[str]?`      | Free-form tags.                                     |
| `status`                                 | `JobStatus?`      | See [Jobs](#jobs).                                  |
| `created_at` / `started_at` / `ended_at` | `str?` (ISO 8601) | Lifecycle timestamps.                               |
| `runtime`                                | `int?`            | Runtime in milliseconds.                            |
| `shots`                                  | `int?`            | Number of shots.                                    |
| `session_id`                             | `str?`            | Parent session, if any.                             |
| `sdk_provider`                           | `JobSdkProvider?` | SDK that produced the input.                        |

### Session

| Field                                                    | Type                  | Description                                                           |
| -------------------------------------------------------- | --------------------- | --------------------------------------------------------------------- |
| `id`                                                     | `str?`                | Unique session identifier.                                            |
| `backend_id`                                             | `str?`                | Backend the session runs on.                                          |
| `provider`                                               | `SessionProvider?`    | Provider enum.                                                        |
| `status`                                                 | `SessionStatus?`      | See [Sessions](#sessions).                                            |
| `mode`                                                   | `SessionMode?`        | `"batch"` or `"dedicated"`.                                           |
| `created_at` / `started_at` / `closed_at` / `expires_at` | `str?`                | Lifecycle timestamps.                                                 |
| `usage_time_millis`                                      | `int?`                | Billed usage time.                                                    |
| `provider_id`                                            | `str?`                | Provider-assigned session id.                                         |
| `tags`                                                   | `List[str]?`          | Free-form tags.                                                       |
| `metadata`                                               | `JsonNode?`           | Free-form metadata supplied at creation.                              |
| `sdk_provider`                                           | `SessionSdkProvider?` | SDK that opened the session.                                          |
| `final`                                                  | `bool?`               | True once the session is in a terminal state.                         |
| `final_not_aborted`                                      | `bool?`               | True when the session reached a terminal state without being aborted. |

### Backend

| Field                   | Type                       | Description                                                               |
| ----------------------- | -------------------------- | ------------------------------------------------------------------------- |
| `id`                    | `str?`                     | Backend identifier (e.g. `aws.sim.sv1`).                                  |
| `internal_id`           | `str?`                     | Hub-internal id.                                                          |
| `provider`              | `BackendProvider?`         | AZURE, AWS, IBM, QRYD, QUDORA, QUANDELA, IQM, KIPU.                       |
| `hardware_provider`     | `BackendHardwareProvider?` | Actual hardware vendor.                                                   |
| `name` / `display_name` | `str?`                     | Machine-readable / human-readable name.                                   |
| `type`                  | `BackendType?`             | `QPU`, `SIMULATOR`, `ANNEALER`, `UNKNOWN`.                                |
| `technology`            | `BackendTechnology?`       | `SUPERCONDUCTING`, `TRAPPED_ION`, `PHOTONIC`, `NEUTRAL_ATOMS`, `UNKNOWN`. |
| `queue_size`            | `int?`                     | Current queue length.                                                     |
| `updated_at`            | `str?`                     | Last-updated timestamp.                                                   |
| `access_type`           | `BackendAccessType?`       | How access is billed (e.g. pay-per-use).                                  |
| `documentation`         | `Documentation?`           | Doc links and per-SDK guidance.                                           |
| `configuration`         | `Configuration?`           | Native gate set, qubit count, supported formats.                          |
| `availability`          | `List[AvailabilityTimes]?` | Operating windows.                                                        |
| `costs`                 | `List[Cost]?`              | Pricing per provider.                                                     |
| `has_calibration`       | `bool?`                    | True if calibration data is available.                                    |
| `free_of_charge`        | `bool?`                    | True if execution is not billed.                                          |

### Pagination wrappers

All paginated endpoints return a `PageResponse*` object with the shape `{content: List[T]?, page: int?, size: int?, total_elements: int?, total_pages: int?}`.
`page` is zero-based.

### Enums

* `JobStatus` - `UNKNOWN | PENDING | ABORTED | RUNNING | COMPLETED | FAILED | CANCELLING | CANCELLED`
* `SessionStatus` - `UNKNOWN | ABORTED | OPEN | ACTIVE | INACTIVE | DRAINING | CLOSED`
* `BackendStateInfoStatus` - `UNKNOWN | ONLINE | PAUSED | OFFLINE | RETIRED` (returned by `backends.get_backend_status` / `backends.getBackendStatus`).
* `SessionMode` - `batch | dedicated`
* `JobInputFormat` - `OPEN_QASM_V1 | OPEN_QASM_V2 | OPEN_QASM_V3 | QIR_V1 | BRAKET_OPEN_QASM_V3 | BRAKET_AHS_PROGRAM | IONQ_CIRCUIT_V1 | QISKIT_QPY | QOQO | PERCEVAL | IQM_JOB_INPUT_V1`
* `JobProvider` / `SessionProvider` / `WorkloadResponseProvider` - `AZURE | AWS | IBM | QRYD | QUDORA | QUANDELA | IQM | KIPU`
* `BackendProvider` - `AZURE | AWS | IBM | QRYD | QUDORA | QUANDELA | IQM | KIPU`
* `BackendHardwareProvider` - `IONQ | RIGETTI | OQC | AWS | AZURE | IBM | QUERA | IQM | QUDORA | QUANTINUUM | QUANDELA | KIPU`
* `JobSdkProvider` / `SessionSdkProvider` / `WorkloadResponseSdkProvider` - `QISKIT | BRAKET | PERCEVAL | CLIENT`
* `BackendType` - `QPU | SIMULATOR | ANNEALER | UNKNOWN`
* `BackendTechnology` - `SUPERCONDUCTING | TRAPPED_ION | PHOTONIC | NEUTRAL_ATOMS | UNKNOWN`
* `WorkloadResponseType` - `JOB | SESSION`

### Field casing

Quantum-API DTOs are snake\_case in both languages (`backend_id`, `created_at`) - the generator preserves the wire casing for this API in TypeScript.
TypeScript method arguments themselves use camelCase (`sessionId`, `serviceExecutionId`), but request-body shapes follow the DTO casing above.
