Run an off-target module over HTTP¶
Use this workflow when your organization has a published, approved off-target prediction module and you need an HTTP integration. Module identifiers, input fields, pricing and runtimes belong to that deployed version; this page does not prescribe a universal scientific model or claim a production benchmark. For Python applications, the SDK prepare-and-run flow handles the corresponding requests.
This guide targets the prepared-run API used by SDK 5.7. Confirm that the chosen
environment has that API before continuing. If quoting does not return
admissionId, requestFingerprint and preparedRequest, stop and ask the
administrator to complete the rollout. Do not fall back to an unreviewed start.
Credentials and environment¶
Use the exact API base URL for your environment, without a trailing /v1.
Supply HLA_API_BASE_URL and HLA_API_KEY through your process configuration and
secret store. A personal automation key needs the relevant read/write permissions,
module execution scope and access to the selected module and data. An API key
cannot choose a different organization by changing a request field.
See API-key permissions and scopes.
All paths below are the API-key /v1/api/... paths. A signed-in user's bearer
session uses the corresponding /v1/... paths through the SDK. Do not place
bearer tokens in X-API-Key, or reuse a module-run credential as a personal key.
Discover the actual module and inputs¶
Read the accessible module inventory. It is paginated; inspect pagination.pages
and repeat with subsequent page values when needed:
curl --fail-with-body --silent --show-error \
-H "X-API-Key: $HLA_API_KEY" \
"$HLA_API_BASE_URL/v1/api/modules?limit=100&page=1" > modules.json
python -m json.tool modules.json
Choose the intended off-target module and set MODULE_ID to its full UUID.
Read its definition, including published version, input schema, execution
requirements and any declared data inputs:
curl --fail-with-body --silent --show-error \
-H "X-API-Key: $HLA_API_KEY" \
"$HLA_API_BASE_URL/v1/api/modules/$MODULE_ID" > module.json
python -m json.tool module.json
Create inputs.json as a JSON object matching that version's schema. A previous
module accepting peptide, top_n or catalog_id does not prove a redeployed
module accepts those fields. Validate peptide, HLA and catalog semantics against
the selected module's documentation; an empty result is not evidence of safety.
Do not run a paid module merely to discover catalogs: use GET /v1/api/data-catalogs.
Its items array contains Catalog identities and any ready currentVersion.
A Catalog UUID selects a catalog; it does not pin its data. Reproducible selected
data requires the module's supported governed data-context binding and exact
Catalog Version, or an explicitly declared versioned input. Supplying an
arbitrary catalogVersionId inside module parameters does not add that capability.
See saved views and run inputs.
Save a plan, then confirm it separately¶
Install requests in your virtual environment and save this complete example as
off_target_http.py. It makes authenticated calls only when you run a command.
The optional --data-context file must contain a supported saved-view selection;
this script does not invent one or transform unrelated catalogs into a join.
import argparse
import json
import os
import time
from pathlib import Path
from uuid import UUID
import requests
def request(method, base, path, payload=None):
response = requests.request(
method,
base + path,
headers={"X-API-Key": os.environ["HLA_API_KEY"]},
json=payload,
timeout=(10, 40),
)
if not response.ok:
raise RuntimeError(f"HTTP {response.status_code}: {response.text}")
return response.json()
def read_object(path):
value = json.loads(Path(path).read_text())
if not isinstance(value, dict):
raise ValueError(f"{path} must contain a JSON object")
return value
def main():
parser = argparse.ArgumentParser()
commands = parser.add_subparsers(dest="action", required=True)
prepare = commands.add_parser("prepare")
prepare.add_argument("module_id")
prepare.add_argument("inputs")
prepare.add_argument("plan_file")
prepare.add_argument("--data-context")
prepare.add_argument("--mode", choices=("interactive", "async"), default="interactive")
confirm = commands.add_parser("confirm")
confirm.add_argument("plan_file")
result = commands.add_parser("result")
result.add_argument("run_id")
args = parser.parse_args()
base = os.environ["HLA_API_BASE_URL"].rstrip("/")
if not base.startswith("https://"):
raise ValueError("Use your environment's HTTPS API base URL")
if args.action == "prepare":
module_id = str(UUID(args.module_id))
payload = {"mode": args.mode, "parameters": read_object(args.inputs)}
if args.data_context:
payload["dataContext"] = read_object(args.data_context)
plan = request("POST", base, f"/v1/api/modules/{module_id}/quote", payload)
if not all(plan.get(k) for k in ("admissionId", "requestFingerprint", "preparedRequest")):
raise RuntimeError("API returned no complete prepared plan; do not start")
# Exclusive creation prevents accidentally replacing a reviewed plan.
with Path(args.plan_file).open("x") as handle:
json.dump({"apiBaseUrl": base, "plan": plan}, handle, indent=2)
print(json.dumps(plan, indent=2))
return
if args.action == "confirm":
saved = read_object(args.plan_file)
if saved["apiBaseUrl"] != base:
raise ValueError("Plan belongs to a different API environment")
plan = saved["plan"]
payload = dict(plan["preparedRequest"])
payload.update(admissionId=plan["admissionId"], requestFingerprint=plan["requestFingerprint"])
accepted = request("POST", base, "/v1/api/module-runs", payload)
run_id = accepted.get("runId") or (accepted.get("_run") or {}).get("runId")
if not run_id:
raise RuntimeError("No Run ID returned; retain the plan for recovery")
print(json.dumps({"runId": run_id, "response": accepted}, indent=2))
return
run_id = str(UUID(args.run_id))
deadline = time.monotonic() + 600
while time.monotonic() < deadline:
status = request("GET", base, f"/v1/api/module-runs/{run_id}")
state = str(status.get("status", "")).lower()
if state in {"completed", "failed", "cancelled"}:
output = request("GET", base, f"/v1/api/module-runs/{run_id}/result")
print(json.dumps(output, indent=2))
if (
state != "completed"
or not output.get("success")
or output.get("error")
or output.get("artifactErrors")
):
raise SystemExit("Run failed or result artifacts are incomplete; inspect output")
return
time.sleep(5)
raise TimeoutError(f"Stopped waiting for {run_id}; the server run may still be active")
if __name__ == "__main__":
main()
Prepare only; this saves admission metadata but does not start compute:
Review the module version, parameters, mode, profile and any frozen data context
in plan.preparedRequest, plus estimated_cost, currency and expiresAt in
plan. If you accept those values, confirm the saved plan:
Copy the returned runId into RUN_ID, then retrieve its terminal result:
Handle uncertainty without creating another run¶
A successful start may return an inline result or a background acceptance. Either way, retain the returned Run ID. A network timeout is an unknown outcome, not proof that no run exists. Retry confirmation of the same unchanged plan with the same user/organization credentials; an admitted plan recovers its original Run ID. Never recover by choosing the newest run in a list.
If an unused plan expires, its price changes or the request changes, prepare a new plan and review it again before confirming. Do not automatically reprepare or remove the admission identity. The plan file contains scientific inputs and should be stored with the same access controls as the analysis.
A polling timeout does not cancel compute. Continue inspecting the same Run ID.
On 429, honor Retry-After before retrying the read; the example stops and
reports the response rather than claiming a fixed per-key rate. Concurrency,
credit and query budgets vary by environment and organization.
/result returns 409 before a terminal state. At completion, inspect platform
success, error, artifactErrors and the selected module's output contract.
The result envelope preserves Run ID, module version, inputs/context and artifact
references. It does not guarantee scientific validity, replay availability, or
that an arbitrary off-target module supports pinned governed data.