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 requests
response = 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.