Skip to main content
POST
Evaluate
Initial submissions: record.xml (+ recommended record.candidate_info). Resubmissions / JSON-native: record.record_json + required record.candidate_info. Both paths require search_id, search_date, order_id, and order_number on record. The response shape is unchanged for both paths.

Input modes: XML vs JSON

The record object accepts two mutually exclusive input types: For JSON submissions, send sibling fields on record: record_json (mapped criminal data only), candidate_info, plus search_id, search_date, order_id, and order_number. Optional submission_type: "resubmit" marks a resubmission; it must be a top-level field on record — not inside record_json.

Request

string
required
Bearer YOUR_JWT_TOKEN — see Authentication
object
required
The criminal record to evaluate. Provide exactly one of xml (initial submission) or record_json (resubmission), plus search metadata.

Request validation

Common HTTP 400 errors: Common HTTP 422 errors (XML-Transformer — court search ID / offense ID count mismatch):
This validation runs inside XML-Transformer and returns HTTP 422 on /transform-xml. On POST /evaluate, the orchestrator returns HTTP 422 with data.status: "validation_required" and embeds the XT error as XML-Transformer failed: XML-Transformer HTTP error 422: ... in data.errors[].

cases[] count reconciliation (XML-Transformer gate)

When record.cases[] is non-empty, XML-Transformer validates that counts match the parsed record before enrichment or compliance evaluation. Processing stops immediately on mismatch. Matching example — XML has 2 cases and 3 charges:
Offense "2" is included in the array to maintain positional alignment but excluded from the response (is_excluded: true). It still counts for the gate and evaluation; it is stripped from the client-facing decision JSON after decisions are built. Mismatch example — UBS sends 17 distinct court_search_id values and 22 offense_id rows, but XML has 24 cases and 32 offenses → rejected.

Excluding charges from the decision JSON (is_excluded)

When cases[] is non-empty, set is_excluded on every row: Behavior:
  1. All cases[] pairs still flow through transform and compliance
  2. After decisions are built, offenses with is_excluded: true are removed from data.decision (court_decisionscase_decisionsoffenses)
  3. Cases/courts with no remaining offenses are pruned from the decision tree
  4. Top-level / case / court rollups (search_queue, record_decision, case_queue, case_decision, court_queue, court_decision) are not recomputed — they reflect the full (pre-exclusion) set
  5. Client-facing validation filtering:
    • Drop errors/warnings tied to an excluded offense_id
    • If every offense on a case is excluded, also drop that case’s case / case_subject issues and offense_id: "unknown" issues for that case
    • Search / candidate issues are kept
    • If no issues remain, validation is omitted (or status ready)
  6. Routing still uses pre-exclusion validation internally
On POST /evaluate, court search ID / offense ID count mismatch returns HTTP 422 with data.status: "validation_required" (not HTTP 500 / failed). See Example Response — validation_required (court search ID / offense ID count mismatch).
The record_json structure errors above apply when record_json is provided (resubmission path). The sentence block on offenses is optional and is not validated as required on input. Other offense-level fields are validated separately during enrichment.

Response

Operational next steps (queue or decision — one usually enough): Queue & Decision → Next Action. All responses are wrapped in a standard envelope:
boolean
Indicates whether the request was processed. true when evaluation completed (fully or partially). false when the request was rejected due to validation errors or a processing failure.
object
Contains the evaluation results, including the compliance decision, identity match scores, and any data-quality issues found.
object
Metadata about the request itself (not the evaluation result).

HTTP Status Codes


record_json reference

record_json is mapped-tier criminal data — court searches, cases, and charges from a prior pipeline output. It does not include candidate demographics or order metadata; send candidate_info, order_id, and order_number via sibling fields on record. Required top-level key: criminal_search_results (non-empty array) Each criminal_search_results[] item must include required fields (court_search_id, search_date, criminal_records) — values must not be null, missing, or empty (""). search_jurisdiction is optional (ignored for validation when missing or empty; still accepted when provided). Nested criminal_records[] items must include required case fields (see table below); sub_middle_name is optional. Arrays (criminal_records, offenses) must not be empty. Prefer case-level jurisdiction_state (and jurisdiction when available) for location. Forbidden inside record_json: candidate_info, submission_type (including record_json.search.submission_type on input), order_id, order_number (including inside record_json.search), status (including record_json.search.status on input). Optional metadata about the original search. Do not include submission_type, order_id, order_number, or status here — send those as sibling fields on record.

criminal_search_results[]

Each entry represents a court search — a query to a specific court that returned one or more criminal cases.

criminal_records[] (case)

Each entry represents a criminal case — a court proceeding that contains one or more charges (offenses). The subject fields describe the person named in the court record (who may or may not be the candidate).

offenses[] (charge)

Each entry represents a single criminal charge within a case. This is the most granular level — each charge receives its own reportability decision.

sentence (on offense)

Optional — omit the entire sentence block when sentence data is not available. When provided, all fields within are also optional — include only what is known.
Send candidate_info as a flat block with a single-line address string and submission_type as a top-level field on record. The API handles normalization internally — you do not need to send structured addresses[] or nest these fields inside record_json.

Code Examples

Primary example for JSON resubmission:
Do not nest candidate_info, submission_type, order_id, or order_number inside record_json. Send submission_type: "resubmit" as a top-level field on record whenever record_json is provided.
Example offense with Pending disposition and a future pending_court_date:
Same envelope as resubmission — use xml instead of record_json + submission_type. Omit submission_type on initial submissions:
Copy and modify this JSON payload for initial XML submissions:
When the source data is clean, the validation field is omitted entirely from the response (not returned as an empty object). It only appears when there are real data-quality issues — see the next example.
When the source data has quality issues, the response includes a structured validation object alongside the decision. The validation field is only present when issues exist:
Validation issue types:
  • issues.errors — Hard data-quality problems (missing SSN, missing disposition, indeterminate charge classification). These typically force affected charges into the Insufficient Data queue, which then propagates up through case_queue, court_queue, and search_queue.
  • issues.warnings — Soft mismatches (e.g., type-class or disposition-class disagreements between the rule engine and the ML model). The rule-engine classification takes precedence — these are informational only and do not change routing on their own.
When some components fail, status is partial and degradation is included. The errors array carries the engine-level failure messages:
When court search ID / offense ID counts in cases[] do not match the parsed XML/record_json, XML-Transformer rejects the request (error_code: CASE_OFFENSE_COUNT_MISMATCH). On POST /evaluate, the orchestrator returns HTTP 422 with data.status: "validation_required" and no compliance evaluation:
XML-Transformer response on /transform-xml (HTTP 422):

Validation

The validation object surfaces data-quality issues in the source record. It is omitted entirely from the response when the source data is clean — your client should treat the absence of validation as “no issues”. When any cases[] row has is_excluded: true, client-facing validation is filtered after evaluation: issues for excluded offense_ids are dropped; if every offense on a case is excluded, that case’s case / case_subject (and offense_id: "unknown") issues are dropped too. Search / candidate issues remain. If nothing remains, validation is omitted. When present, it has this shape:
Each item:
errors vs warnings:
  • errors are hard data-quality problems — they typically force affected charges into the Insufficient Data queue, which then bubbles up through case_queuecourt_queuesearch_queue.
  • warnings are soft mismatches (e.g., the rule engine and ML model disagree on type class). The rule-engine classification wins; these are informational only.
errors here is not the same as the top-level data.errors array. data.errors carries service-level failures (engine timeouts, network errors). validation.issues.errors carries data-level problems in the input record.

JSON Field Reference

Field values below are sourced from the live API contract. See also Glossary and Routing.

Request — outer record

Response — envelope

HTTP status: 200 success · 206 partial · 400 bad input · 422 validation_required · 500 failed

Response — decision labels (*_decision)

Queue → decision mapping (aggregated levels only):

Response — routing queues (*_queue)

Response — per-offense routing

Response — id_match (per case)

Response — other offense fields

Request — offense sentence (optional)

Omit sentence entirely when not applicable. When included, all fields below are optional.

Response — validation (when issues exist)

record_decision vs search_queue: These are linked at aggregated levels — e.g. search_queue: "Automation" always pairs with record_decision: "NOT_REPORTABLE". Per-offense charge_decision comes from the compliance engine independently; aggregated *_decision labels mirror the queue mapping above.

Decision Hierarchy

The evaluation result (data.decision) is structured hierarchically: Record → Court → Case → Offense Each level carries both a reportability label (*_decision) and a routing queue (*_queue):

Aggregated decision labels (*_decision)

At case, court, and record levels, *_decision is derived from the matching *_queue, not rolled up from child charge_decision values: charge_decision on each offense is computed independently by the compliance engine (REPORTABLE | NOT_REPORTABLE | MANUAL_REVIEW).

Queue rollup (*_queue)

case_queue, court_queue, and search_queue are aggregated from per-charge routing.queue values:
  1. Insufficient Data (highest) — if any child queue is Insufficient Data
  2. Auditor — if any child is Auditor (and none are Insufficient Data)
  3. Automation (lowest) — every child is Automation
Use per-offense charge_decision and routing.queue for charge-level handling. At aggregated levels, *_decision mirrors *_queue via the mapping above — e.g. one reportable charge routed to Auditor yields record_decision: "REPORTABLE" and search_queue: "Auditor".