QuantumBPM Python SDK
Official Python SDK for the QuantumBPM platform - DMN evaluation, BPMN process orchestration, and external job workers.
Installation
pip install quantumbpm-sdk
Python 3.10+. Async-first; built on asyncio.
What's in the box
| Module | Purpose |
|---|---|
quantumbpm.QuantumBPM |
Top-level client exposing .dmn, .bpmn, plus new_worker(...) |
quantumbpm.auth |
TokenProvider, ZitadelTokenProvider, StaticTokenProvider |
quantumbpm.dmn |
DMN evaluation: stored definitions, ad-hoc XML, batch |
quantumbpm.bpmn |
BPMN resources, instances, messaging, user tasks, processes |
quantumbpm.workers |
External job worker runtime - long-poll, lock heartbeat, dispatch |
quantumbpm.variables |
Vars wrapper with typed accessors and FEEL-context conversion |
quantumbpm.api[_client] |
OpenAPI-generated client. Reachable via client.raw, never hand-edited |
Quick start
import asyncio
from quantumbpm import QuantumBPM, Vars, ZitadelTokenProvider
async def main():
provider = ZitadelTokenProvider(
"./service-account.json", # Zitadel JSON Key file
"https://auth.quantumbpm.com", # issuer
"your-zitadel-project-id", # audience scope
)
async with QuantumBPM(
base_url="https://api.quantumbpm.com",
project_id="00000000-0000-0000-0000-000000000000",
token_provider=provider,
) as client:
result = await client.dmn.evaluate(
"loan-eligibility",
Vars().set("requestedAmt", 1000).set("creditScore", 720),
)
print(result)
asyncio.run(main())
The async context manager (async with) acquires a fresh bearer token on entry. Skipping the context manager and calling methods directly works too - the SDK refreshes tokens on demand.
Authentication
The TokenProvider Protocol returns a bearer token on each request. Two implementations ship out of the box.
Zitadel service account
from quantumbpm import ZitadelTokenProvider
provider = ZitadelTokenProvider(
"./service-account.json", # path to JSON Key file
"https://auth.quantumbpm.com", # issuer URL
"your-zitadel-project-id", # adds the audience scope
ssl_ca_cert="/path/to/ca.crt", # optional, for self-signed CAs
)
The provider caches tokens in-memory until shortly before expiry.
Static bearer token
For Enterprise deployments that issue long-lived API keys, or in tests where a token is acquired out of band:
from quantumbpm import StaticTokenProvider
provider = StaticTokenProvider("eyJhbGciOi...")
Bring your own
Implement the Protocol:
from quantumbpm import TokenProvider
class MyProvider:
async def get_token(self) -> str:
return await fetch_my_token()
provider: TokenProvider = MyProvider()
DMN evaluation
The client.dmn sub-client offers four async methods.
Evaluate a stored definition
result = await client.dmn.evaluate(
"loan-eligibility",
Vars().set("requestedAmt", 5000).set("creditScore", 720),
)
Returns dict[str, EvaluationResult] keyed by decision name. Each result has value, hit_rules, error, and type.
Pin a version, restrict the evaluated decisions, or attach decision services:
result = await client.dmn.evaluate(
"loan-eligibility",
vars,
version=3,
decisions=["eligibility", "rate"],
)
Evaluate by platform UUID
When you already hold a database-version pointer:
result = await client.dmn.evaluate_by_id(definition_uuid, vars)
Ad-hoc XML evaluation
For "evaluate while editing" flows that don't store the XML:
result = await client.dmn.evaluate_design(
dmn_xml,
vars,
additional_xmls=[imported_xml1, imported_xml2],
decisions=["eligibility"],
)
Batch ad-hoc evaluation
rows = [
Vars().set("requestedAmt", 1000),
Vars().set("requestedAmt", 5000),
Vars().set("requestedAmt", 25000),
]
batch = await client.dmn.evaluate_design_batch(dmn_xml, rows)
BPMN processes
client.bpmn covers the full BPMN runtime surface.
Deploy and start
draft = await client.bpmn.create_resource("loan-process", bpmn_xml)
await client.bpmn.deploy_resource(draft.id)
# Re-fetch to get the populated process-definition list.
deployed = await client.bpmn.get_resource(draft.id)
process_def = deployed.processes[0]
workflow_id = await client.bpmn.start_instance(
process_def.id,
Vars().set("applicantID", "u-123").set("requestedAmt", 25000),
)
Inspect runtime state
state = await client.bpmn.get_instance(workflow_id)
print(state.status, state.active_scopes)
vars = await client.bpmn.get_instance_variables(workflow_id)
children = await client.bpmn.get_instance_children(workflow_id)
Send messages and signals
await client.bpmn.publish_message(
"loan-approved",
Vars().set("approvedAmt", 24000),
correlation_keys={"applicantID": "u-123"},
ttl="PT5M",
)
await client.bpmn.publish_signal("system-maintenance")
User tasks
page = await client.bpmn.list_user_tasks(
assignee="alice@example.com",
status="CREATED",
)
await client.bpmn.complete_user_task(execution_key, Vars().set("approved", True))
# Or fail with a BPMN error code (matches boundary error events):
await client.bpmn.throw_user_task_error(execution_key, "REVIEW_REJECTED")
External job workers
Workers handle service tasks asynchronously. Register a handler per task type with a decorator, then call run. The runtime owns long-polling, lock heartbeats, dispatch, and outcome mapping.
Minimal worker
import asyncio
from quantumbpm import QuantumBPM, Vars, BpmnError
from quantumbpm.workers import Job
async def main():
async with QuantumBPM(...) as client:
worker = client.new_worker(client_id="billing-svc")
stop = asyncio.Event()
@worker.handler("send-email")
async def handle(job: Job) -> Vars:
recipient = job.vars.lookup("recipient")
subject = job.vars.lookup("subject")
await emailer.send(recipient, subject)
return Vars().set("messageID", "msg-123") # → Complete
await worker.run(stop) # blocks until stop is set
asyncio.run(main())
run(stop) resolves when the asyncio.Event is set, after in-flight handlers settle.
Concurrency, polling, and locks
@worker.handler(
"send-email",
max_jobs=10, # up to 10 in flight per task type
poll_timeout="45s", # long-poll wait
lock_duration="2m", # exclusive lock per job
)
async def handle(job: Job) -> Vars:
...
Concurrency is per task type. Different task types run independently. The runtime auto-renews the lock at half the lock-duration interval while the handler runs.
Throwing typed BPMN errors
Raise a BpmnError to fail the job with a code that boundary error events on the originating service task can catch:
@worker.handler("charge-card")
async def handle(job: Job) -> Vars:
try:
tx_id = await charge(job.vars.to_dict())
return Vars().set("transactionID", tx_id)
except InsufficientFundsError:
raise BpmnError("INSUFFICIENT_FUNDS", Vars().set("availableBalance", 12.0))
Any other exception is reported as WORKER_ERROR, which the server treats as a retryable failure that decrements the job's retry budget.
Typed handlers (Pydantic)
Type-annotate the Job parameter with a Pydantic model - the runtime validates the job's input variables before invoking the handler. The decoded value lands in job.typed.
from pydantic import BaseModel
from quantumbpm.workers import Job
class EmailJob(BaseModel):
recipient: str
subject: str
@worker.handler("send-email")
async def handle(job: Job[EmailJob]) -> Vars:
await emailer.send(job.typed.recipient, job.typed.subject)
return Vars().set("messageID", "msg-123")
Decode failures become WORKER_ERROR automatically, with the validation message in the variables.
Variables
Vars is a thin wrapper around dict[str, Any] shared by DMN, BPMN, and workers.
Construction
v = Vars().set("amount", 100).set("name", "Alice")
v = Vars.from_dict({"amount": 100, "name": "Alice"})
Typed access
amount = v.get("amount", float)
flag = v.get("approved", bool)
class Loan(BaseModel):
requestedAmt: float
approved: bool
loan = v.as_type(Loan)
Vars.get(name, type_) and Vars.as_type(type_) accept Pydantic models, dataclasses, and primitives - Pydantic's TypeAdapter does the validation.
Escape hatch
The client.raw property exposes the underlying generated ApiClient for endpoints not yet wrapped (instance migration, modification, ad-hoc triggers, batch job complete/error, etc.):
from quantumbpm.api.bpmn_api import BpmnApi
api = BpmnApi(client.raw)
result = api.migrate_bpmn_instance(client.project_id, workflow_id, body)
License
MIT License - see LICENSE for details.
Metadata
Release files for quantumbpm-sdk 1.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| quantumbpm_sdk-1.1.0.tar.gz | 100.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| quantumbpm_sdk-1.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 297.9 kB
Release files / quantumbpm_sdk-1.1.0.tar.gz
| Download URL | quantumbpm_sdk-1.1.0.tar.gz |
|---|---|
| Size | 100.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
cfd592d7aebd95b9fd9e392163417a652320cc9f91037f6764fe16031c75a0c1
|
|
BLAKE2b-256 checksum How to use checksums |
734658636b718ce84e4955582b391a1fba9f4e2a452784af9812c627f479b372
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 25, 2026.
Transparency logRelease files / quantumbpm_sdk-1.1.0-py3-none-any.whl
| Download URL | quantumbpm_sdk-1.1.0-py3-none-any.whl |
|---|---|
| Size | 197.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
e6d9f25c69b8da84e5f4675940d7368e404fda75078b3734be1c962b659094d4
|
|
BLAKE2b-256 checksum How to use checksums |
cd8799635c06f3279c1674cf100b0d56429fbf98d91f2ea1c4c776bfc2777e85
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 25, 2026.
Transparency log