Escalating to hosted reasoning
The embedded runtime answers questions that local rules can settle. Some decisions need evidence, multi-step reasoning, or a calibrated estimate of whether the answer is right. Those go to the hosted Helixor reasoning API.
Embedded or hosted?#
| Embedded runtime | Hosted reasoning API | |
|---|---|---|
| Where it runs | In your process | A Helixor service, over HTTPS |
| Latency | Microseconds | Milliseconds and up |
| Decides with | The rules in the pack | The evidence and questions you send |
| Can abstain | No: every input gets an action | Yes: abstain when the evidence does not support an answer |
| Data leaves your process | Never | Yes, whatever you send |
A common pattern is to run the embedded decision first, on everything, and escalate only the cases it cannot settle. The example below uses the built-in example pack (data protection) as that first step, so only its redacted text ever leaves your process.
The decide request#
POST/v1/decide takes a goal, the questions to answer, and the evidence to answer them from:
{
"goal": "Route the support ticket",
"questions": [
{
"question_id": "q1",
"family_id": "support.ticket.route",
"kind": "choice",
"prompt": "Which team should handle this ticket?",
"options": [
{"option_id": "billing", "criteria": "Charges, invoices, refunds"},
{"option_id": "technical", "criteria": "Errors, outages, integrations"}
]
}
],
"evidence": [
{"id": "ticket", "text": "I was charged twice for [REDACTED_CARD_PAN]", "source": "helpdesk"}
],
"require": {"proof": true}
}
Goals must be admitted
This request is illustrative: goal must name a goal that is admitted on your reasoning service, or the call returns 404 GOAL_NOT_ADMITTED. The reasoning service tutorial runs a goal that dispatches today and shows both an answer and an abstention.
Question kind is choice, score or boolean. require can also set min_confidence and max_latency_ms. Authenticate with your API key in the X-API-Key header. The endpoint URL and key come with your hosted API access.
The response#
{
"verdicts": {
"q1": {"verdict": "answer", "value": "billing", "p_correct": 0.97,
"proof": {"...": "..."}, "reason": "..."}
},
"composed": {"verdict": "answer", "rule_ids": [], "band": "..."},
"proof_id": "...",
"latency_ms": 42
}
Each question gets a verdict of answer, abstain or refuse. Act only on answer, and treat abstain as "a person decides". Keep proof_id with your record, as you keep receipt_hash for embedded decisions.
Combining the two#
import os
import httpx
from helixor_runtime import HelixorEngine
ENGINE = HelixorEngine()
API = httpx.Client(
base_url=os.environ["HELIXOR_API_URL"], # your reasoning API endpoint
headers={"X-API-Key": os.environ["HELIXOR_API_KEY"]},
timeout=5.0,
)
def route_ticket(text: str) -> str:
guard = ENGINE.evaluate(text)
if guard.action.startswith("block_") or any(t.severity == "FATAL" for t in guard.triggers):
return "manual_review" # never escalate blocked content
evidence = guard.remedy.clean_text # only redacted text leaves the process
resp = API.post("/v1/decide", json=build_request(evidence))
resp.raise_for_status() # an error is not an answer
verdict = resp.json()["verdicts"]["q1"]
return verdict["value"] if verdict["verdict"] == "answer" else "manual_review"
Status
The request and response shapes above follow the reasoning API's published contract. A typed escalation path built into the runtime, which links an embedded receipt to the hosted proof automatically, is Planned. Until then, call the API yourself as shown and store both identifiers.