Skip to main content

Validate a policy

POST /policies/validate
Implemented in packages/api/src/routes/policies.ts, mounted at /policies in app.ts with the /validate subpath defined on the router. Calls policyRepository.load(policyId, policyVersion) directly — the same FilePolicyRepository.load (packages/policy/src/FilePolicyRepository.ts) used internally when a Business Transaction is executed.
curl -X POST http://localhost:3000/policies/validate \
  -H "Content-Type: application/json" \
  -d '{ "policyId": "vendor-payment", "policyVersion": "1.0.0" }'
Despite the field name, policyId is a policy name (e.g. vendor-payment), not a UUID — FilePolicyRepository resolves it to policies/<name>/<version>/policy.json on disk. The monorepo root policies/ directory ships vendor-payment, wire-transfer, fraud-block, payment-release, autonomous-driving, medical-treatment, multi-agent, and custom-policy, each versioned in its own subdirectory (e.g. policies/vendor-payment/1.0.0/ and policies/vendor-payment/2.0.0/).
“Validate” here means existence and readability, not schema or business-rule validation. FilePolicyRepository.load only checks that policies/<policyId>/<policyVersion>/policy.json exists and parses as JSON — it does not check the policy’s internal structure against a schema. A malformed-but-present policy.json that still parses as valid JSON would report valid: true.
Response 200
{ "valid": true, "errors": [] }
Response 400 — missing policyId or policyVersion
{ "valid": false, "errors": ["policyId is required."] }
Response 404 — policy not found on disk
{ "valid": false, "errors": ["Policy \"vendor-payment\" version \"9.9.9\" not found."] }
This route does not use the shared errorHandler middleware’s error shape at all — it catches Error locally and always responds with { valid, errors }, even for the not-found case that other routes would report as { "error": "..." } with a plain 404. See Error Model for how this compares to other routes.
Specification gap. openapi/openapi.yaml documents GET /policies (list) and GET /policies/{name}/{version} (get one) under the Policies tag — neither exists as an implemented route. Conversely, the OpenAPI spec does not document POST /policies/validate at all, even though it’s the only policy-related endpoint that actually exists in packages/api/src/routes/.

SDK equivalents

const result = await client.validatePolicy({
  policyId: "vendor-payment",
  policyVersion: "1.0.0",
  schemaVersion: "1.0.0",
  signalsSchema: { amount: "number" },
  rules: [],
});
TypeScript’s PolicyApi.validate() (typescript/src/client/PolicyApi.ts) is typed to accept a full Policy object from @parmana/policy{ policyId, policyVersion, schemaVersion, signalsSchema, rules } — and posts it as-is. Note this is a different shape from the PolicyReference ({ name, version, schemaVersion }) used inside a BusinessTransaction’s own policy field (see typescript/src/models/policy.ts). The server route only reads policyId/policyVersion off whatever body it receives and ignores the rest, so in practice only those two fields matter for this call.

Policy

The concept behind what’s being validated.

policy package

Policy loading and evaluation internals.

Error Model

Why this route’s error shape doesn’t match the others.

Execute

Where an unvalidated, missing policy fails instead — inside execution, not up front.