Skip to content

Quickstart

This guide takes you from install to a gated write in a few minutes, in either language. Pick a tab; the choice follows you down the page.

1. Install

Both SDKs are open source (Apache-2.0).

Terminal window
pip install novarch # SDK
pip install "novarch[langchain]" # + LangChain adapter
pip install "novarch[langgraph]" # + LangGraph adapter
pip install "novarch[adk]" # + Claude Agent SDK adapter

2. Get your deployment

The SDK is a client. It talks to a Novarch deployment that runs the Novarch engine, your plain-English rules, and the operator triage queue. For pilots and sandboxes, that deployment is managed by Novarch. You’ll receive a server URL and an SDK key, which the SDK reads from two environment variables:

Terminal window
export NOVARCH_SERVER_URL=https://<your-deployment>
export NOVARCH_SDK_KEY=<your-sdk-key>

VPC and self-hosted deployments are available for design partners. See Configuration for the full list of environment variables and per-session options.

3. Instrument your agent

Mark your read tools, your write tool, and the session. Read tools buffer as evidence; the write tool is gated on the Novarch engine’s verdict.

This quickstart uses the tool surface directly, which works in any agent. If yours runs on LangChain (create_agent / createAgent), LangGraph, or the Claude Agent SDK, you can register one gate at the framework’s tool seam instead and skip wrapping tools entirely — see that adapter’s page. Everything below about sessions, verdicts, and NovarchKillError applies either way.

from novarch import augur, NovarchKillError
from novarch.verticals.ap import APContext
@augur.tool(kind="read")
def lookup_vendor(vendor_id: str) -> dict:
return vendor_db.lookup(vendor_id)
@augur.tool(
kind="write",
action_type="process_invoice",
context_builder=lambda invoice: APContext(
vendor_id=invoice["vendor_id"],
vendor_name=invoice["vendor_name"],
invoice_id=invoice["invoice_id"],
amount=invoice["amount"],
payment_method="ach",
approval_threshold=25000.0,
vendor_in_master_db=True,
domain_similarity_to_canonical=1.0,
off_cycle=False,
agent_confidence=0.95,
),
)
def submit_payment(invoice: dict) -> str:
return payments_api.submit(invoice)
@augur.session(
tenant_id="enterprise-ap-ops",
team_id="accounts-payable",
agent_id="ap-agent-014",
)
def run(invoice):
lookup_vendor(invoice["vendor_id"]) # buffers a read
augur.set_reasoning("Vendor known; bank verified.")
submit_payment(invoice) # gated write; the engine fires here

The decorators find the session on a ContextVar, so tools declared at module scope pick up whichever session is open when they run.

4. What happens on a write

  • Read tools execute immediately and buffer a ToolCall entry into the pending action.
  • The staged reasoning travels with the action. An agent that produces none declares that on the session, and its actions say which silence this was. See Declaring that your agent has no reasoning.
  • The first write tool drains the buffer, materializes an Action, submits it to your deployment, and waits for the Novarch engine’s verdict.
  • pass → the wrapped function runs.
  • hold (PAUSED) → the SDK blocks while a human operator approves or denies in the triage dashboard; approve runs the function, deny / timeout raises NovarchKillError (pre-v0.1.x servers may also report skip, legacy).
  • kill (BLOCKED) → raises NovarchKillError immediately; the write never executes. Any uncertainty (judge error, timeout, server restart) fails closed.

5. Handle the kill

Catch NovarchKillError in your loop if you want to keep processing the rest of the queue when one item is rejected. The error carries the rule that fired and the rationale so the agent loop and the audit row can reconstruct what happened:

for invoice in invoice_queue:
try:
run(invoice)
except NovarchKillError as e:
print(f"Blocked by rule {e.rule_id_cited} ({e.verdict}): {e.rationale}")
continue # a kill is terminal for this write; do not retry

Useful fields on NovarchKillError:

  • rule_id_cited: the rule that fired (e.g. AP-1), or __FAIL_CLOSED__ when the server fail-closed on an error.
  • verdict: the trigger source: "kill" (from the Novarch engine), or "deny" / "skip" / "timeout" (from the operator polling path).
  • rationale: the human-readable explanation, prefixed with [fail_closed_*] when uncertainty forced the kill.

Treat NovarchKillError as terminal for the current write. The block is an enforcement decision, not a transient failure.

6. Watch the queue

When an action is held, it appears in your deployment’s operator triage queue at NOVARCH_SERVER_URL/app (operators sign in with their own per-operator account). PAUSED cards show the cited rule and the agent’s reasoning; an operator clicks Approve to resume the blocked write or Deny to terminate it. The verdict is recorded to the audit log under that operator’s identity, and the blocked SDK call unblocks the moment the operator decides.