SecaniDocumentation
OSCAL

Validation API

Validate OSCAL 1.2.2 JSON documents over HTTP — stateless, anonymous, powered by the same engine as the browser validator.

The Secani OSCAL Validation API validates OSCAL 1.2.2 JSON documents over HTTP. POST the document itself as the request body — no envelope, no account — and receive a machine-readable report from the same engine that powers the browser validator. It is built for machines in flight: CI jobs, integration scripts, and other tools that produce or transform OSCAL documents.

Privacy contract. Your document is processed transiently in memory on EU servers (Frankfurt) and discarded when the response is sent. Nothing is persisted, logged, or used for anything besides producing the response; telemetry is aggregate only (model type, size bucket, outcome, duration). The browser validator remains fully browser-local — your document never leaves the browser there.

Quickstart

curl -sS -X POST https://secani.com/api/oscal/v1/validate \
  -H "content-type: application/json" \
  --data-binary @system-security-plan.json

Large artifacts (full catalogs can exceed the 4 MB raw body limit) compress extremely well — send them gzipped:

gzip -c catalog.json | curl -sS -X POST https://secani.com/api/oscal/v1/validate \
  -H "content-type: application/json" \
  -H "content-encoding: gzip" \
  --data-binary @-

Response

A completed validation always returns HTTP 200 — "valid": false is a successful validation of an invalid document. Branch on the valid field, not the status code.

{
  "valid": false,
  "model": "system-security-plan",
  "oscalVersion": "1.2.2",
  "level": "schema",
  "issues": [
    {
      "path": "/system-security-plan/metadata/version",
      "message": "must have required property 'version'",
      "keyword": "required",
      "severity": "error"
    }
  ],
  "issueCount": 37,
  "truncated": false,
  "meta": {
    "engine": "@secani/oscal",
    "schemaSource": "NIST OSCAL v1.2.2 release JSON schemas",
    "patches": ["profile-combine-method"],
    "durationMs": 84
  }
}
FieldMeaning
validWhether the document passed schema validation.
modelDetected OSCAL model (one of the eight OSCAL 1.2.2 models).
oscalVersionThe oscal-version declared in the document's metadata; unknown if absent.
levelValidation level that ran. Always schema in v1.
issues[].pathRFC 6901 JSON Pointer into the submitted document.
issues[].severityAlways error in v1; warning is reserved for future interpretive rules.
issueCountTrue total number of issues, even when issues is truncated.
truncatedtrue when more than 200 issues were found and the list was capped.
meta.patchesDocumented deviations from the raw NIST release schemas.

Errors

Errors use RFC 9457 problem details (application/problem+json) with a stable machine-readable code:

StatusCodeMeaning
400ERR_INVALID_JSONThe body is not well-formed JSON.
400ERR_UNKNOWN_MODELJSON, but no recognizable OSCAL root model.
400ERR_INVALID_CONTENT_ENCODINGContent-Encoding: gzip declared but the body is not valid gzip.
400ERR_UNSUPPORTED_PARAMETERA reserved query parameter carries a not-yet-supported value.
405ERR_METHOD_NOT_ALLOWEDOnly POST (and OPTIONS) are accepted.
413ERR_PAYLOAD_TOO_LARGERaw body over 4 MB or decompressed body over 24 MB.
415ERR_UNSUPPORTED_MEDIA_TYPEContent type other than application/json, or an unsupported encoding.
429ERR_RATE_LIMITEDRate limit exceeded; see Retry-After.
500ERR_INTERNALUnexpected failure; no internals are disclosed.

Limits

  • Request body: 4 MB raw, 24 MB decompressed (Content-Encoding: gzip is supported and recommended for large documents).
  • One document per request.
  • Anonymous rate limits: 10 requests per minute and 300 per day per client, surfaced through RateLimit-Limit, RateLimit-Remaining, and RateLimit-Reset response headers, with Retry-After on 429 responses. Need more? Contact hello@secani.com.

Reserved parameters

The following query parameters are part of the contract but rejected until their functionality ships. Sending an unsupported value returns ERR_UNSUPPORTED_PARAMETER with an explanation:

  • level — only schema today. A deeper constraint- and reference-checking level is planned on top of Secani's extended validation registry.
  • rules — reserved for opting into individual interpretive rules alongside the deeper level.
  • oscal-version — only 1.2.2 today.

API keys with higher, durable rate limits are planned; the anonymous tier stays.

OpenAPI

The machine-readable contract is served at /api/oscal/v1/openapi.json. CORS is open (*), so the API can be called directly from browser-based tools.

See also: the browser validator for interactive use and the TypeScript toolkit behind both.