Introduction
Ovrule API
Get started
npm install ovrule-lab
Use the SDK when you want a typed client for classify, guard, and receipt verification.
npm install ovrule-lab
Ovrule turns proposed AI agent actions into case files. Instead of a raw model output, you get a verdict, a rule trace, a receipt hash, and a durable history of challenges and overrides.
Integrate it when your agents can trigger real consequences: money movement, customer messaging, account changes, moderation, automation, or anything else that should leave an audit trail.
The API is intentionally narrow: classify a case, contest a decision, record a reviewer override, and read the resulting case file surface from your own product.
SDK
TypeScript client
The `ovrule-lab` SDK wraps classify, guard, and verification flows with typed helpers so you can integrate the product without hand-rolling SSE parsing or receipt verification.
npm install ovrule-lab
import { classify } from "ovrule-lab";const receipt = await classify("Support agent wants to refund $5,000 without manager approval.",{ baseUrl: "https://decision-receipt-lab.vercel.app" },);console.log(receipt.decision, receipt.summary);
import { guard } from "ovrule-lab";const result = await guard({scenario: "Finance agent wants to wire $18,000 to a new vendor after bank details changed.",policyPack: "finance",},{ baseUrl: "https://decision-receipt-lab.vercel.app" },);if (!result.allowed) {throw new Error(result.decision);}await issueWireTransfer();
import { verify } from "ovrule-lab";const valid = await verify(receipt, receipt.signature, {baseUrl: "https://decision-receipt-lab.vercel.app",});console.log({ valid });
Full SDK examples live in the repo: sdk/README.md
Authentication
Currently open for demo use with IP-based rate limiting. API key auth coming with Phase 2.
In the current build, requests are accepted directly by the deployed Next.js app. Production deployments should still restrict origin, rate-limit aggressively, and front the API with your own auth until native API keys land.
Classify
POST /api/classify
Opens a live classification stream and returns the case file when the audit completes.
{"scenario": "Support agent wants to refund $5,000 without manager approval.","policyPack": "customer_support"}
curl -N -X POST https://your-domain/api/classify \-H "Content-Type: application/json" \-d '{"scenario":"Support agent wants to refund $5,000 without manager approval."}'
import requestsresponse = requests.post("https://your-domain/api/classify",json={"scenario": "Support agent wants to refund $5,000 without manager approval."},stream=True,)for line in response.iter_lines():if line:print(line.decode())
const response = await fetch("/api/classify", {method: "POST",headers: { "Content-Type": "application/json" },body: JSON.stringify({scenario: "Support agent wants to refund $5,000 without manager approval.",}),});const reader = response.body?.getReader();
{"decision": "AMBIGUOUS","scenario": "A shopping agent wants to auto-check out a saved cart 24 hours later.","proposedAction": "Auto-check out a saved cart after a delayed reminder period.","claimedGoal": "Complete a purchase on the user's behalf after a reminder delay.","authorityBasis": "The user asked for a reminder, not explicit purchase execution.","severity": "medium","riskScore": 52,"summary": "The purchase intent boundary is unclear. Ovrule flags the action because consent and authorization are not explicit enough for autonomous checkout.","ruleTrace": [{ "rule": "SAFETY", "verdict": "WARN", "reason": "An autonomous purchase can create financial harm if the user did not intend to buy." },{ "rule": "AUTHORIZATION", "verdict": "WARN", "reason": "A reminder request does not clearly authorize checkout execution." }],"receiptId": "c83aa5e1-76b2-4e7b-8d38-68abf3f8cc78","hash": "0d92a47ac11f","signature": "base64-ed25519-signature","timestamp": "2026-04-22T23:15:10.000Z"}
Policy Packs
Audit overlays for domain-specific risk
Policy packs modify the base six-rule audit with pack-specific guidance and deterministic post-LLM checks. They do not replace the model output; they merge into the returned `ruleTrace` and can escalate a rule from `PASS` to `WARN` or `FAIL`.
Pass `policyPack` in the `/api/classify` request body to audit under a stricter domain-specific standard.
General
Default Ovrule behavior. No extra domain checks are applied beyond the standard six-rule audit.
Customer Support
Tightens authorization and safety around refunds, escalation thresholds, sensitive customer interactions, and precedent-setting exceptions.
Healthcare
Tightens safety, causal validity, and consent around PHI, clinical advice boundaries, and emergency or acute-risk scenarios.
Finance
Tightens safety, authorization, and reversibility around transfers, trades, disclosure issues, and material financial actions.
curl -N -X POST https://your-domain/api/classify \-H "Content-Type: application/json" \-d '{"scenario": "Support agent wants to refund $5,000 without manager approval.","policyPack": "customer_support"}'
Deterministic checks run after the LLM audit and can escalate verdicts before the final decision is derived. Custom pack support is planned next: `Custom pack — coming soon`.
Comparable Precedents
Similar past cases
Ovrule generates a `text-embedding-3-small` embedding for each stored receipt using the proposed action, decision, and rule trace summary. The UI uses that embedding to surface three related cases for reviewer context.
Use `GET /api/similar/:id` to fetch the three closest prior receipts for a case. If the vector column is unavailable or the database has too few cases, Ovrule falls back to a lighter-weight similarity pass and may return an empty list.
curl https://your-domain/api/similar/c83aa5e1-76b2-4e7b-8d38-68abf3f8cc78
{"similar": [{"id": "0c32ec20-3f28-453f-ad0b-5850a10efe36","decision": "REFUSED","summary": "Consent verification was missing for a customer-wide promotional email.","hash": "9d5e640ea215","timestamp": "2026-04-23T14:55:00.622Z","similarity": 0.91}]}
Contest
POST /api/contest
{"receiptId": "c83aa5e1-76b2-4e7b-8d38-68abf3f8cc78","reason": "This case omitted the user consent record attached to the account.","category": "missing_context"}
Override
POST /api/override
{"receiptId": "c83aa5e1-76b2-4e7b-8d38-68abf3f8cc78","reviewerName": "Treasury lead","overrideDecision": "annotate","annotation": "Proceed only after callback verification and dual approval."}
Verify
POST /api/verify
Verify a signed receipt with Ovrule's signer. Fetch the public key from `GET /api/public-key` and inspect signer health from `GET /api/signing-health`.
{"receipt": {"receiptId": "c83aa5e1-76b2-4e7b-8d38-68abf3f8cc78","hash": "0d92a47ac11f","signature": "base64-ed25519-signature"},"signature": "base64-ed25519-signature"}
Webhooks
Outbound event delivery
Register HTTPS endpoints from `/webhooks` or the API and Ovrule will POST signed payloads when receipts, contests, and reviewer overrides are created.
Every delivery includes `X-Ovrule-Signature`, an HMAC-SHA256 signature of the raw request body using the webhook secret returned at registration time.
curl -X POST https://your-domain/api/webhooks \-H "Content-Type: application/json" \-d '{"url": "https://example.com/ovrule","events": ["receipt.created", "contest.created"]}'
{"event": "receipt.created","data": {"receiptId": "0c32ec20-3f28-453f-ad0b-5850a10efe36","decision": "REFUSED","policyPack": "customer_support"},"timestamp": "2026-04-23T14:55:00.622Z"}
import crypto from "crypto";const expected = crypto.createHmac("sha256", webhookSecret).update(rawBody).digest("hex");const valid = signature === expected;
Management endpoints: `GET /api/webhooks`, `POST /api/webhooks`, `DELETE /api/webhooks/:id`, and `POST /api/webhooks/:id/test`.
Rate limits
`POST /api/classify` is currently limited to 10 requests per minute per IP.
Responses include `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `X-RateLimit-Reset` headers.
Retry after the reset window rather than hammering the stream endpoint.