curl --request POST \
--url https://cra.pr.snh-ai.com/evaluate \
--header 'Authorization: <authorization>' \
--header 'Content-Type: application/json' \
--data '
{
"record": {
"search_id": "<string>",
"search_date": "<string>",
"order_id": "<string>",
"order_number": "<string>",
"xml": "<string>",
"record_json": {},
"submission_type": "<string>",
"tenant_id": "<string>",
"applicant_state": "<string>",
"customer_state": "<string>",
"cases": [
{
"court_search_id": "<string>",
"offense_id": "<string>",
"is_excluded": true
}
],
"candidate_info": {
"first_name": "<string>",
"middle_name": "<string>",
"last_name": "<string>",
"date_of_birth": "<string>",
"ssn": "<string>",
"address": "<string>",
"source_type": "<string>",
"source_reliability_rating": "<string>",
"verification_status": "<string>"
},
"search_type": "<string>"
}
}
'import requests
url = "https://cra.pr.snh-ai.com/evaluate"
payload = { "record": {
"search_id": "<string>",
"search_date": "<string>",
"order_id": "<string>",
"order_number": "<string>",
"xml": "<string>",
"record_json": {},
"submission_type": "<string>",
"tenant_id": "<string>",
"applicant_state": "<string>",
"customer_state": "<string>",
"cases": [
{
"court_search_id": "<string>",
"offense_id": "<string>",
"is_excluded": True
}
],
"candidate_info": {
"first_name": "<string>",
"middle_name": "<string>",
"last_name": "<string>",
"date_of_birth": "<string>",
"ssn": "<string>",
"address": "<string>",
"source_type": "<string>",
"source_reliability_rating": "<string>",
"verification_status": "<string>"
},
"search_type": "<string>"
} }
headers = {
"Authorization": "<authorization>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: '<authorization>', 'Content-Type': 'application/json'},
body: JSON.stringify({
record: {
search_id: '<string>',
search_date: '<string>',
order_id: '<string>',
order_number: '<string>',
xml: '<string>',
record_json: {},
submission_type: '<string>',
tenant_id: '<string>',
applicant_state: '<string>',
customer_state: '<string>',
cases: [{court_search_id: '<string>', offense_id: '<string>', is_excluded: true}],
candidate_info: {
first_name: '<string>',
middle_name: '<string>',
last_name: '<string>',
date_of_birth: '<string>',
ssn: '<string>',
address: '<string>',
source_type: '<string>',
source_reliability_rating: '<string>',
verification_status: '<string>'
},
search_type: '<string>'
}
})
};
fetch('https://cra.pr.snh-ai.com/evaluate', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://cra.pr.snh-ai.com/evaluate",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'record' => [
'search_id' => '<string>',
'search_date' => '<string>',
'order_id' => '<string>',
'order_number' => '<string>',
'xml' => '<string>',
'record_json' => [
],
'submission_type' => '<string>',
'tenant_id' => '<string>',
'applicant_state' => '<string>',
'customer_state' => '<string>',
'cases' => [
[
'court_search_id' => '<string>',
'offense_id' => '<string>',
'is_excluded' => true
]
],
'candidate_info' => [
'first_name' => '<string>',
'middle_name' => '<string>',
'last_name' => '<string>',
'date_of_birth' => '<string>',
'ssn' => '<string>',
'address' => '<string>',
'source_type' => '<string>',
'source_reliability_rating' => '<string>',
'verification_status' => '<string>'
],
'search_type' => '<string>'
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: <authorization>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://cra.pr.snh-ai.com/evaluate"
payload := strings.NewReader("{\n \"record\": {\n \"search_id\": \"<string>\",\n \"search_date\": \"<string>\",\n \"order_id\": \"<string>\",\n \"order_number\": \"<string>\",\n \"xml\": \"<string>\",\n \"record_json\": {},\n \"submission_type\": \"<string>\",\n \"tenant_id\": \"<string>\",\n \"applicant_state\": \"<string>\",\n \"customer_state\": \"<string>\",\n \"cases\": [\n {\n \"court_search_id\": \"<string>\",\n \"offense_id\": \"<string>\",\n \"is_excluded\": true\n }\n ],\n \"candidate_info\": {\n \"first_name\": \"<string>\",\n \"middle_name\": \"<string>\",\n \"last_name\": \"<string>\",\n \"date_of_birth\": \"<string>\",\n \"ssn\": \"<string>\",\n \"address\": \"<string>\",\n \"source_type\": \"<string>\",\n \"source_reliability_rating\": \"<string>\",\n \"verification_status\": \"<string>\"\n },\n \"search_type\": \"<string>\"\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "<authorization>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://cra.pr.snh-ai.com/evaluate")
.header("Authorization", "<authorization>")
.header("Content-Type", "application/json")
.body("{\n \"record\": {\n \"search_id\": \"<string>\",\n \"search_date\": \"<string>\",\n \"order_id\": \"<string>\",\n \"order_number\": \"<string>\",\n \"xml\": \"<string>\",\n \"record_json\": {},\n \"submission_type\": \"<string>\",\n \"tenant_id\": \"<string>\",\n \"applicant_state\": \"<string>\",\n \"customer_state\": \"<string>\",\n \"cases\": [\n {\n \"court_search_id\": \"<string>\",\n \"offense_id\": \"<string>\",\n \"is_excluded\": true\n }\n ],\n \"candidate_info\": {\n \"first_name\": \"<string>\",\n \"middle_name\": \"<string>\",\n \"last_name\": \"<string>\",\n \"date_of_birth\": \"<string>\",\n \"ssn\": \"<string>\",\n \"address\": \"<string>\",\n \"source_type\": \"<string>\",\n \"source_reliability_rating\": \"<string>\",\n \"verification_status\": \"<string>\"\n },\n \"search_type\": \"<string>\"\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://cra.pr.snh-ai.com/evaluate")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = '<authorization>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"record\": {\n \"search_id\": \"<string>\",\n \"search_date\": \"<string>\",\n \"order_id\": \"<string>\",\n \"order_number\": \"<string>\",\n \"xml\": \"<string>\",\n \"record_json\": {},\n \"submission_type\": \"<string>\",\n \"tenant_id\": \"<string>\",\n \"applicant_state\": \"<string>\",\n \"customer_state\": \"<string>\",\n \"cases\": [\n {\n \"court_search_id\": \"<string>\",\n \"offense_id\": \"<string>\",\n \"is_excluded\": true\n }\n ],\n \"candidate_info\": {\n \"first_name\": \"<string>\",\n \"middle_name\": \"<string>\",\n \"last_name\": \"<string>\",\n \"date_of_birth\": \"<string>\",\n \"ssn\": \"<string>\",\n \"address\": \"<string>\",\n \"source_type\": \"<string>\",\n \"source_reliability_rating\": \"<string>\",\n \"verification_status\": \"<string>\"\n },\n \"search_type\": \"<string>\"\n }\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"data": {
"search_id": "<string>",
"correlation_id": "<string>",
"status": "<string>",
"errors": [
{}
],
"validation": {
"status": "<string>",
"issues": {
"errors": [
{
"level": "<string>",
"field": "<string>",
"issue": "<string>",
"description": "<string>",
"case_id": "<string>",
"case_number": "<string>",
"offense_id": "<string>"
}
],
"warnings": [
{}
]
}
},
"decision": {
"search_id": "<string>",
"decision_date": "<string>",
"record_decision": "<string>",
"search_queue": "<string>",
"order_id": "<string>",
"order_number": "<string>",
"ubs_search_type": "<string>",
"court_decisions": [
{
"court_search_id": "<string>",
"court_decision": "<string>",
"court_queue": "<string>",
"case_decisions": [
{
"case_number": "<string>",
"case_decision": "<string>",
"case_queue": "<string>",
"jurisdiction_state": "<string>",
"id_match": {
"is_match": true,
"match_score": 123,
"name_score": 123,
"dob_score": 123,
"ssn_score": 123,
"address_score": 123,
"details": "<string>"
},
"offenses": [
{
"offense_id": "<string>",
"charge": "<string>",
"charge_decision": "<string>",
"type": "<string>",
"disposition": "<string>",
"rationale": "<string>",
"needs_human_review": true,
"cited_rules": [
{}
],
"citations": [
{}
],
"routing": {
"queue": "<string>",
"reportability": "<string>",
"is_automatable": true,
"identity_level": "<string>",
"identity_score": 123,
"identity_insufficient": true,
"reportability_insufficient": true,
"third_id_required": true
}
}
]
}
]
}
]
},
"timing": {},
"degradation": {}
},
"meta": {
"api_version": "<string>",
"process_time_ms": 123,
"correlation_id": "<string>"
}
}Evaluate
Evaluate a single criminal record for compliance and reportability
curl --request POST \
--url https://cra.pr.snh-ai.com/evaluate \
--header 'Authorization: <authorization>' \
--header 'Content-Type: application/json' \
--data '
{
"record": {
"search_id": "<string>",
"search_date": "<string>",
"order_id": "<string>",
"order_number": "<string>",
"xml": "<string>",
"record_json": {},
"submission_type": "<string>",
"tenant_id": "<string>",
"applicant_state": "<string>",
"customer_state": "<string>",
"cases": [
{
"court_search_id": "<string>",
"offense_id": "<string>",
"is_excluded": true
}
],
"candidate_info": {
"first_name": "<string>",
"middle_name": "<string>",
"last_name": "<string>",
"date_of_birth": "<string>",
"ssn": "<string>",
"address": "<string>",
"source_type": "<string>",
"source_reliability_rating": "<string>",
"verification_status": "<string>"
},
"search_type": "<string>"
}
}
'import requests
url = "https://cra.pr.snh-ai.com/evaluate"
payload = { "record": {
"search_id": "<string>",
"search_date": "<string>",
"order_id": "<string>",
"order_number": "<string>",
"xml": "<string>",
"record_json": {},
"submission_type": "<string>",
"tenant_id": "<string>",
"applicant_state": "<string>",
"customer_state": "<string>",
"cases": [
{
"court_search_id": "<string>",
"offense_id": "<string>",
"is_excluded": True
}
],
"candidate_info": {
"first_name": "<string>",
"middle_name": "<string>",
"last_name": "<string>",
"date_of_birth": "<string>",
"ssn": "<string>",
"address": "<string>",
"source_type": "<string>",
"source_reliability_rating": "<string>",
"verification_status": "<string>"
},
"search_type": "<string>"
} }
headers = {
"Authorization": "<authorization>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: '<authorization>', 'Content-Type': 'application/json'},
body: JSON.stringify({
record: {
search_id: '<string>',
search_date: '<string>',
order_id: '<string>',
order_number: '<string>',
xml: '<string>',
record_json: {},
submission_type: '<string>',
tenant_id: '<string>',
applicant_state: '<string>',
customer_state: '<string>',
cases: [{court_search_id: '<string>', offense_id: '<string>', is_excluded: true}],
candidate_info: {
first_name: '<string>',
middle_name: '<string>',
last_name: '<string>',
date_of_birth: '<string>',
ssn: '<string>',
address: '<string>',
source_type: '<string>',
source_reliability_rating: '<string>',
verification_status: '<string>'
},
search_type: '<string>'
}
})
};
fetch('https://cra.pr.snh-ai.com/evaluate', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://cra.pr.snh-ai.com/evaluate",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'record' => [
'search_id' => '<string>',
'search_date' => '<string>',
'order_id' => '<string>',
'order_number' => '<string>',
'xml' => '<string>',
'record_json' => [
],
'submission_type' => '<string>',
'tenant_id' => '<string>',
'applicant_state' => '<string>',
'customer_state' => '<string>',
'cases' => [
[
'court_search_id' => '<string>',
'offense_id' => '<string>',
'is_excluded' => true
]
],
'candidate_info' => [
'first_name' => '<string>',
'middle_name' => '<string>',
'last_name' => '<string>',
'date_of_birth' => '<string>',
'ssn' => '<string>',
'address' => '<string>',
'source_type' => '<string>',
'source_reliability_rating' => '<string>',
'verification_status' => '<string>'
],
'search_type' => '<string>'
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: <authorization>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://cra.pr.snh-ai.com/evaluate"
payload := strings.NewReader("{\n \"record\": {\n \"search_id\": \"<string>\",\n \"search_date\": \"<string>\",\n \"order_id\": \"<string>\",\n \"order_number\": \"<string>\",\n \"xml\": \"<string>\",\n \"record_json\": {},\n \"submission_type\": \"<string>\",\n \"tenant_id\": \"<string>\",\n \"applicant_state\": \"<string>\",\n \"customer_state\": \"<string>\",\n \"cases\": [\n {\n \"court_search_id\": \"<string>\",\n \"offense_id\": \"<string>\",\n \"is_excluded\": true\n }\n ],\n \"candidate_info\": {\n \"first_name\": \"<string>\",\n \"middle_name\": \"<string>\",\n \"last_name\": \"<string>\",\n \"date_of_birth\": \"<string>\",\n \"ssn\": \"<string>\",\n \"address\": \"<string>\",\n \"source_type\": \"<string>\",\n \"source_reliability_rating\": \"<string>\",\n \"verification_status\": \"<string>\"\n },\n \"search_type\": \"<string>\"\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "<authorization>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://cra.pr.snh-ai.com/evaluate")
.header("Authorization", "<authorization>")
.header("Content-Type", "application/json")
.body("{\n \"record\": {\n \"search_id\": \"<string>\",\n \"search_date\": \"<string>\",\n \"order_id\": \"<string>\",\n \"order_number\": \"<string>\",\n \"xml\": \"<string>\",\n \"record_json\": {},\n \"submission_type\": \"<string>\",\n \"tenant_id\": \"<string>\",\n \"applicant_state\": \"<string>\",\n \"customer_state\": \"<string>\",\n \"cases\": [\n {\n \"court_search_id\": \"<string>\",\n \"offense_id\": \"<string>\",\n \"is_excluded\": true\n }\n ],\n \"candidate_info\": {\n \"first_name\": \"<string>\",\n \"middle_name\": \"<string>\",\n \"last_name\": \"<string>\",\n \"date_of_birth\": \"<string>\",\n \"ssn\": \"<string>\",\n \"address\": \"<string>\",\n \"source_type\": \"<string>\",\n \"source_reliability_rating\": \"<string>\",\n \"verification_status\": \"<string>\"\n },\n \"search_type\": \"<string>\"\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://cra.pr.snh-ai.com/evaluate")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = '<authorization>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"record\": {\n \"search_id\": \"<string>\",\n \"search_date\": \"<string>\",\n \"order_id\": \"<string>\",\n \"order_number\": \"<string>\",\n \"xml\": \"<string>\",\n \"record_json\": {},\n \"submission_type\": \"<string>\",\n \"tenant_id\": \"<string>\",\n \"applicant_state\": \"<string>\",\n \"customer_state\": \"<string>\",\n \"cases\": [\n {\n \"court_search_id\": \"<string>\",\n \"offense_id\": \"<string>\",\n \"is_excluded\": true\n }\n ],\n \"candidate_info\": {\n \"first_name\": \"<string>\",\n \"middle_name\": \"<string>\",\n \"last_name\": \"<string>\",\n \"date_of_birth\": \"<string>\",\n \"ssn\": \"<string>\",\n \"address\": \"<string>\",\n \"source_type\": \"<string>\",\n \"source_reliability_rating\": \"<string>\",\n \"verification_status\": \"<string>\"\n },\n \"search_type\": \"<string>\"\n }\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"data": {
"search_id": "<string>",
"correlation_id": "<string>",
"status": "<string>",
"errors": [
{}
],
"validation": {
"status": "<string>",
"issues": {
"errors": [
{
"level": "<string>",
"field": "<string>",
"issue": "<string>",
"description": "<string>",
"case_id": "<string>",
"case_number": "<string>",
"offense_id": "<string>"
}
],
"warnings": [
{}
]
}
},
"decision": {
"search_id": "<string>",
"decision_date": "<string>",
"record_decision": "<string>",
"search_queue": "<string>",
"order_id": "<string>",
"order_number": "<string>",
"ubs_search_type": "<string>",
"court_decisions": [
{
"court_search_id": "<string>",
"court_decision": "<string>",
"court_queue": "<string>",
"case_decisions": [
{
"case_number": "<string>",
"case_decision": "<string>",
"case_queue": "<string>",
"jurisdiction_state": "<string>",
"id_match": {
"is_match": true,
"match_score": 123,
"name_score": 123,
"dob_score": 123,
"ssn_score": 123,
"address_score": 123,
"details": "<string>"
},
"offenses": [
{
"offense_id": "<string>",
"charge": "<string>",
"charge_decision": "<string>",
"type": "<string>",
"disposition": "<string>",
"rationale": "<string>",
"needs_human_review": true,
"cited_rules": [
{}
],
"citations": [
{}
],
"routing": {
"queue": "<string>",
"reportability": "<string>",
"is_automatable": true,
"identity_level": "<string>",
"identity_score": 123,
"identity_insufficient": true,
"reportability_insufficient": true,
"third_id_required": true
}
}
]
}
]
}
]
},
"timing": {},
"degradation": {}
},
"meta": {
"api_version": "<string>",
"process_time_ms": 123,
"correlation_id": "<string>"
}
}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
Therecord object accepts two mutually exclusive input types:
| Input | Use case |
|---|---|
record.xml (+ recommended candidate_info) | Initial submission — raw vendor XML |
record.record_json + record.candidate_info | Resubmission or JSON-native mapped criminal data |
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
Bearer YOUR_JWT_TOKEN — see Authenticationxml (initial submission) or record_json (resubmission), plus search metadata.Show Record Properties
Show Record Properties
"76190bc0-e58c-494f-92c5-0c9a47389528"YYYY-MM-DD format. Used for lookback period calculations and compliance rule evaluation. Example: "2025-04-30"order_id across every search on that order. Top-level on record only (not inside record_json). Echoed in the response. Example: "41115779"orderID.subOrderID form. Identifies which search under the order this call covers. One /evaluate per order_number. Top-level on record only. Example: "41115779.1"<ScreeningResults>...</ScreeningResults> tags. Use this for initial submissions when you have the original vendor output. Mutually exclusive with record_json — provide one or the other, not both.criminal_search_results array. Requires submission_type: "resubmit" on record. Mutually exclusive with xml. See record_json reference for the full structure."resubmit" when using the JSON resubmission path (record_json). Omit this field entirely on initial XML submissions. Must be a top-level field on record."CA", "TX"). Used with state-specific compliance rules in addition to federal rules."CA", "TX"). Used for state-specific rule matching (distinct from applicant_state). Optional.[] to evaluate all charges without explicit mapping. When provided, you must include an entry for every charge in the record — partial subsets are rejected (HTTP 422). Use is_excluded: true on charges you want to exclude from the response instead of omitting them.cases[] (you are supplying your own court_search_id / offense_id values). Entries are mapped to charges in the vendor XML by sequential position — include a row for every charge, in the same order, even for charges you will exclude (is_excluded: true). Sending fewer rows than XML charges causes positional drift. If you omit cases or send cases: [], this sequential mapping does not apply.Show Case Mapping Properties
Show Case Mapping Properties
"CS40929433A"). Multiple charges can share the same court search ID when they come from the same court. Must be non-empty."34789896"). The total number of entries must match the total number of charges in the record. Must be non-empty.false to include it. Set to true to process it internally (identity matching and case/court/search rollups still include it) but strip it from the client decision JSON. Always required on each cases[] row — omitting returns HTTP 400.record_json (must include non-empty first_name, last_name, date_of_birth). Recommended for XML submissions — needed for identity matching quality. Must be at the top level of record (not inside record_json). Send a flat object — do not send aliases[] or addresses[].Show Candidate Info Properties
Show Candidate Info Properties
"John""A""Doe"YYYY-MM-DD format. Used for identity matching and age-based compliance rules. Example: "2000-01-01""123-45-6789""123 Main St, Austin, TX 78701"ubs_search_type. Examples: "Statewide_criminal", "County_criminal"Request validation
| Rule | Detail |
|---|---|
| Exactly one input | Must provide exactly one of xml or record_json (not both, not neither) |
record_json shape | Must contain top-level criminal_search_results — non-empty array; each item and nested criminal_records[] entry must include all required fields (not null, missing, or "") — see record_json reference |
submission_type with record_json | record_json requires submission_type: "resubmit" on record |
cases[] | Optional — omit or send [] to evaluate all offenses (gate not applied). When non-empty: full set required (count gate), each row requires court_search_id, offense_id, and is_excluded (boolean). Mark exclusions with is_excluded: true — do not drop those rows. Partial subsets rejected (HTTP 422) |
candidate_info | Required with record_json — non-empty object with first_name, last_name, date_of_birth. Recommended for XML (needed for id matching). Must not be inside record_json |
order_id / order_number | Required on record — non-empty strings (not null, missing, or ""); must not be inside record_json or record_json.search |
| Message | Cause |
|---|---|
Either xml or record_json is required | Neither input field was provided |
Provide exactly one of xml and record_json | Both fields were provided |
record_json must contain criminal_search_results | record_json is missing the required top-level key |
criminal_search_results must not be empty | criminal_search_results is null, missing, or [] |
court_search_id must not be empty | court_search_id is null, missing, or "" in record_json.criminal_search_results[] |
search_date must not be empty | search_date is null, missing, or "" in record_json.criminal_search_results[] |
criminal_records must not be empty | criminal_records is null, missing, or [] in record_json.criminal_search_results[] |
case_number must not be empty | case_number is null, missing, or "" in record_json.criminal_search_results[].criminal_records[] |
jurisdiction_state must not be empty | jurisdiction_state is null, missing, or "" in record_json.criminal_search_results[].criminal_records[] |
file_date must not be empty | file_date is null, missing, or "" in record_json.criminal_search_results[].criminal_records[] |
subject_name must not be empty | subject_name is null, missing, or "" in record_json.criminal_search_results[].criminal_records[] |
sub_first_name must not be empty | sub_first_name is null, missing, or "" in record_json.criminal_search_results[].criminal_records[] |
sub_last_name must not be empty | sub_last_name is null, missing, or "" in record_json.criminal_search_results[].criminal_records[] |
subject_dob must not be empty | subject_dob is null, missing, or "" in record_json.criminal_search_results[].criminal_records[] |
subject_ssn must not be empty | subject_ssn is null, missing, or "" in record_json.criminal_search_results[].criminal_records[] |
subject_address must not be empty | subject_address is null, missing, or "" in record_json.criminal_search_results[].criminal_records[] |
offenses must not be empty | offenses is null, missing, or [] in record_json.criminal_search_results[].criminal_records[] |
submission_type must be 'resubmit' when record_json is provided | record_json sent without submission_type: "resubmit" |
submission_type 'resubmit' requires record_json, not xml | submission_type: "resubmit" sent with xml instead of record_json |
submission_type must not be inside record_json; provide it as a separate field | submission_type nested inside record_json |
submission_type must not be inside record_json.search; provide it as a separate field | submission_type nested inside record_json.search |
candidate_info must not be inside record_json; provide it as a separate field | candidate_info was nested inside record_json |
candidate_info must not be empty | candidate_info is null, missing, or {} |
candidate_info requires at minimum: first_name, last_name, and date_of_birth | Required candidate_info fields are missing or empty ("") |
Field required [type=bool] (missing is_excluded) | A cases[] row is missing is_excluded (must be boolean true or false) |
order_id must not be inside record_json; provide it as a separate field | order_id nested inside record_json or record_json.search |
order_number must not be inside record_json; provide it as a separate field | order_number nested inside record_json or record_json.search |
order_id must not be empty | order_id is null, missing, or "" |
order_number must not be empty | order_number is null, missing, or "" |
| Message | Cause |
|---|---|
Court search ID and offense ID counts do not match the XML: {n} court search ID(s) in the request but {m} case(s) in the XML; {p} offense ID(s) in the request but {q} offense(s) in the XML. Each court search ID must map to exactly one XML case and each offense ID to exactly one XML offense; resubmit with the complete set. | Non-empty cases[] — distinct court search ID count or total offense ID row count does not match parsed XML/record_json (error_code: CASE_OFFENSE_COUNT_MISMATCH) |
/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.
| Check | Rule |
|---|---|
| Distinct courts | Number of distinct court_search_id values in cases[] = number of XML/record_json cases that have at least one offense |
| Total offenses | Number of offense_id entries in cases[] = total number of XML/record_json offenses/charges |
| One court, many offenses | Multiple cases[] rows may share the same court_search_id (1 court : N offenses) |
| Exclusions | Do not omit excluded charges — send them with is_excluded: true (they still count toward the gate) |
| Partial subsets | Rejected — you cannot send a filtered subset of courts or offenses (HTTP 422) |
| Omit or empty | Omit cases[] or send cases: [] — evaluate all offenses; gate not applied |
[
{ "court_search_id": "A", "offense_id": "1", "is_excluded": false },
{ "court_search_id": "A", "offense_id": "2", "is_excluded": true },
{ "court_search_id": "B", "offense_id": "3", "is_excluded": false }
]
"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:
| Value | Effect |
|---|---|
false | Offense appears in data.decision |
true | Offense is evaluated (ID mapping, count gate, rollups) but removed from client-facing data.decision |
- All
cases[]pairs still flow through transform and compliance - After decisions are built, offenses with
is_excluded: trueare removed fromdata.decision(court_decisions→case_decisions→offenses) - Cases/courts with no remaining offenses are pruned from the decision tree
- 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 - 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_subjectissues andoffense_id: "unknown"issues for that case - Search / candidate issues are kept
- If no issues remain,
validationis omitted (or status ready)
- Drop errors/warnings tied to an excluded
- Routing still uses pre-exclusion validation internally
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).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:true when evaluation completed (fully or partially). false when the request was rejected due to validation errors or a processing failure.Show Data Properties
Show Data Properties
success means all stages completed normally. partial means some stages failed but results were still produced. validation_required means the request was rejected due to a data mismatch (HTTP 422). failed means processing could not complete.[] when everything succeeded. These are infrastructure-level issues — for data-quality problems in the record itself, see validation.Insufficient Data.Show Validation Properties
Show Validation Properties
needs_manual_review means at least one data-quality issue requires human attention before the record can be fully automated.errors (blocking) and warnings (informational).Show Issues Properties
Show Issues Properties
Insufficient Data queue.Show Error Item Properties
Show Error Item Properties
candidate (about the person being screened), case_subject (about the person named in the record), case (about the court case), or offense (about a specific charge).subject_ssn (missing SSN), disposition (missing case outcome), disposition_class (ambiguous charge classification)."SSN is missing or could not be determined.""SSN is required to confirm identity at the highest tier."errors[].REPORTABLE, NOT_REPORTABLE, or MANUAL_REVIEW) and a routing queue (Automation, Auditor, or Insufficient Data). When charges are excluded via is_excluded: true, they are removed from this output after processing.Show Decision Properties
Show Decision Properties
"2026-05-19T14:22:10Z"REPORTABLE means at least one charge can be included in a background report. NOT_REPORTABLE means no charges are reportable. MANUAL_REVIEW means insufficient data to make an automated determination. Derived from search_queue.Automation means no human review needed. Auditor means at least one charge requires manual review. Insufficient Data means at least one charge lacks enough information for a determination. The highest-priority queue from any charge wins: Insufficient Data > Auditor > Automation.Show Court Properties
Show Court Properties
cases[] is provided, this matches the request court_search_id. May show "TBD" only when no court mapping was supplied.REPORTABLE, NOT_REPORTABLE, or MANUAL_REVIEW — rolled up from the cases within this court.Automation, Auditor, or Insufficient Data — rolled up from all cases. A single charge needing review is enough to escalate the entire court.Show Case Properties
Show Case Properties
"2009D 008764", "2024-CF-00312"REPORTABLE, NOT_REPORTABLE, or MANUAL_REVIEW — rolled up from the individual charges.Automation, Auditor, or Insufficient Data — rolled up from all charges in the case."TX", "NC", "CA"Show Identity Match Properties
Show Identity Match Properties
true if the candidate is considered the same person as the record subject (score meets the qualifying threshold). false if the identity could not be confirmed.0.85 or above typically qualifies as a positive match.1 = match, 0 = no match, -1 = address was not available for comparison.Show Offense Properties
Show Offense Properties
"NO LIABILITY INSURANCE", "THEFT OF PROPERTY >= $2,500"REPORTABLE means the charge can legally be reported. NOT_REPORTABLE means compliance rules prevent reporting (e.g., beyond lookback period, traffic violation). MANUAL_REVIEW means a human must decide."Misdemeanor", "Felony", "FELONY", "M", "D""Guilty", "Dismissed", "Pending", "Deferred", "No Information"true when the charge requires a human to make the reporting decision (i.e., charge_decision is MANUAL_REVIEW). false when the decision was fully automated."GENERAL_TRAFFIC_NON_REPORT" (traffic violations not reportable), "CA_NONCONV_7YR_LIMIT" (California 7-year lookback for non-convictions)."15 U.S.C. § 1681c(a)(5)". May be empty when rules don’t reference specific statutes.Show Routing Properties
Show Routing Properties
Automation = no human review needed. Auditor = human review recommended (optional). Insufficient Data = human review required.Reportable — can appear in a background report. Not Reportable — cannot be reported. Not Enough Info — insufficient data to determine.true when the charge can be fully processed without human review (queue is Automation). false otherwise.High (score ≥ 0.9) — strong match. Medium (0.7–0.89) — probable match. Not Enough Info — insufficient data to score. Not Matching (score ≤ −1) — likely a different person.true when there is not enough candidate or record data (e.g., missing SSN, missing address) to calculate a reliable identity match.true when there is not enough charge data (e.g., missing disposition, unclear charge type) to make an automated reportability decision.true when identity match could not be confirmed with name and DOB alone and requires a third factor (SSN or address) that was not available or did not match.total_ms for the overall processing time and per-component breakdowns (e.g., xml_transformer_ms, compliance_ms). Useful for performance monitoring.partial, validation_required, or failed). Shows the health of each processing component: available (working normally), degraded (partially working), unavailable (could not be reached), failed (returned an error), or not_called (was not invoked because a prior stage failed).Show Meta Properties
Show Meta Properties
"2.0.0"data payload, provided here for convenience.HTTP Status Codes
| Status | Meaning |
|---|---|
| 200 | success — all processing completed |
| 206 | partial — some components failed, results may be incomplete |
| 400 | Invalid request — missing or conflicting xml / record_json, empty required fields, missing is_excluded on a cases[] row |
| 422 | Validation error — payload does not reconcile with the record. On /evaluate, court search ID / offense ID count mismatch returns 422 with data.status: "validation_required" (do not retry) |
| 500 | failed — processing failed entirely (engine/service failures) |
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).
| Field | Required | Description | Example values |
|---|---|---|---|
criminal_search_results | Yes | Array of court searches — each represents a separate court search result containing one or more criminal cases | Non-empty array of court search objects |
search | No | Optional metadata about the original search request. See record_json.search | See below |
tenant_id | No | Your organization identifier for data isolation | "UBS", "T001" |
record_json.search
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.
| Field | Description | Example values |
|---|---|---|
search_id | The search identifier — should match the outer search_id on record | "99908723" |
vendor_source | The name of the data vendor that produced this record | "DataDivers" |
search_type | The type of criminal search that was performed | "USA Criminal Offender" |
criminal_search_results[]
Each entry represents a court search — a query to a specific court that returned one or more criminal cases.
| Field | Required | Description | Example values |
|---|---|---|---|
court_search_id | Yes | Unique identifier for this court search, used to map results back to the source | "CS40929433A", "78901234" |
search_date | Yes | When the court search was performed, in YYYY-MM-DD format | "2026-06-16" |
criminal_records | Yes | Array of criminal cases found in this court search. Each case contains one or more charges. | Non-empty array of case objects |
search_jurisdiction | No | The jurisdiction (state and county) where the court search was performed. Prefer using case-level jurisdiction_state for compliance rules. | "NC-MECKLENBURG", "TX-HARRIS" |
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).
| Field | Required | Description | Example values |
|---|---|---|---|
case_number | Yes | The court case number assigned by the court system | "2009D 008764", "2024-CF-00312" |
jurisdiction_state | Yes | The US state where the case was filed (two-letter code). Determines state-specific compliance rules. | "NC", "TX", "CA" |
file_date | Yes | The date the case was filed with the court, in YYYY-MM-DD format | "2009-02-10" |
subject_name | Yes | The full name of the person named in the court record | "JOHN A DOE" |
sub_first_name | Yes | The first name of the record subject, used for identity matching | "John" |
sub_middle_name | No | Optional middle name or initial. May be omitted or "". | "A", "" |
sub_last_name | Yes | The last name of the record subject | "Doe" |
subject_dob | Yes | The date of birth of the record subject, in YYYY-MM-DD format. Used for identity matching. | "2000-01-01" |
subject_ssn | Yes | Non-empty SSN of the record subject (identity matching). Use a real value or a sentinel such as UNKNOWN / NONE when unavailable — "" is rejected. | "123-45-6789", "UNKNOWN" |
subject_address | Yes | The address of the record subject. Used for identity matching. | "123 Main St, Charlotte, NC 28202" |
offenses | Yes | Array of individual charges within this case. Each charge is evaluated independently for compliance. | Non-empty array of offense objects |
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.
| Field | Description | Example values |
|---|---|---|
offense_id | Unique identifier for this charge, required for mapping decisions back to your system | "48870", "4628347" |
charge | The formal charge description as it appears in the court record | "Speeding", "THEFT OF PROPERTY >= $2,500" |
disposition | How the charge was resolved — the court’s final outcome. Drives reportability rules (e.g., convictions vs. dismissals have different lookback periods). | "Guilty", "Dismissed", "Pending", "Deferred", "No Information" |
type | The severity classification of the charge. Affects which compliance rules apply. | "Misdemeanor", "Felony", "M", "FELONY", "D" |
charge_date | When the charge was filed, in YYYY-MM-DD format. Used for lookback period calculations. | "2009-02-10", null |
disposition_date | When the disposition was entered, in YYYY-MM-DD format. Used for lookback period calculations. | "2009-02-10", null |
arrest_date | When the arrest occurred, in YYYY-MM-DD format | "2009-02-08", null |
pending_court_date | The next scheduled court date, when the charge is still pending. In YYYY-MM-DD format. | "2026-07-15", null |
sentence | Sentencing details, if applicable. Optional — omit when sentence data is not available. See sentence. | See below |
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.
| Field | Description | Example values |
|---|---|---|
incarceration_date | When the subject began serving jail or prison time, in YYYY-MM-DD format | "2009-02-15", null |
release_date | When the subject was released from custody, in YYYY-MM-DD format | "2010-01-15", null |
is_serving | Whether the subject is currently incarcerated for this sentence. Relevant for compliance rules around active sentences. | true, false |
probation | The probation term imposed as part of the sentence | "12 months", null |
jail_time | The jail sentence imposed (shorter-term local incarceration) | "30 days", null |
prison_time | The prison sentence imposed (longer-term state/federal incarceration) | "2 years", null |
suspended | Any portion of the sentence that was suspended (not served unless conditions are violated) | "6 months suspended", null |
restitution | The amount the subject was ordered to pay to the victim | "500.00", null |
community_service | Community service hours ordered by the court | "40 hours", null |
fines | Monetary fines imposed as part of the sentence | "250.00", null |
court_costs | Administrative court costs assessed to the defendant | "100.00", null |
sentence_comments | Free-text notes or additional context about the sentence | "Credit for time served", null |
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
Resubmission — mapped JSON (record_json)
Resubmission — mapped JSON (record_json)
{
"record": {
"search_id": "99908723",
"search_date": "2026-06-16",
"order_id": "789",
"order_number": "789.1",
"submission_type": "resubmit",
"cases": [
{ "court_search_id": "CS40929433A", "offense_id": "34789896", "is_excluded": false }
],
"record_json": {
"criminal_search_results": [{
"court_search_id": "CS40929433A",
"search_date": "2026-06-16",
"criminal_records": [{
"case_number": "2009D 008764",
"jurisdiction_state": "NC",
"file_date": "2009-02-10",
"subject_name": "JOHN A DOE",
"sub_first_name": "John",
"sub_middle_name": "A",
"sub_last_name": "Doe",
"subject_dob": "2000-01-01",
"subject_ssn": "123-45-6789",
"subject_address": "123 Main St, Charlotte, NC 28202",
"offenses": [{
"offense_id": "34789896",
"type": "Misdemeanor",
"charge": "Speeding",
"disposition": "Guilty",
"charge_date": "2009-02-10",
"disposition_date": "2009-02-10",
"pending_court_date": null,
"sentence": {
"incarceration_date": "2024-06-01",
"release_date": "2026-06-01",
"is_serving": true,
"probation": null,
"jail_time": "30 days",
"prison_time": null,
"fines": "250.00",
"court_costs": "100.00"
}
}]
}]
}]
},
"candidate_info": {
"first_name": "John",
"last_name": "Doe",
"date_of_birth": "2000-01-01",
"address": "123 Main St, Austin, TX 78701"
}
}
}
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.Resubmission — pending charge (offense snippet)
Resubmission — pending charge (offense snippet)
Pending disposition and a future pending_court_date:{
"offense_id": "48871",
"type": "Misdemeanor",
"charge": "Driving While License Revoked",
"disposition": "Pending",
"charge_date": "2024-03-15",
"disposition_date": null,
"pending_court_date": "2026-07-15",
"sentence": {
"incarceration_date": null,
"release_date": null,
"is_serving": false,
"probation": null,
"jail_time": null,
"prison_time": null,
"suspended": null,
"restitution": null,
"community_service": null,
"fines": null,
"court_costs": null,
"sentence_comments": null
}
}
Initial submission — raw XML
Initial submission — raw XML
xml instead of record_json + submission_type. Omit submission_type on initial submissions:{
"record": {
"search_id": "99908723",
"search_date": "2026-06-16",
"order_id": "789",
"order_number": "789.1",
"xml": "<ScreeningResults>...</ScreeningResults>",
"cases": [
{ "court_search_id": "CS40929433A", "offense_id": "34789896", "is_excluded": false }
],
"candidate_info": {
"first_name": "John",
"last_name": "Doe",
"date_of_birth": "2000-01-01",
"address": "123 Main St, Austin, TX 78701"
}
}
}
Full Request Body — XML with candidate overrides
Full Request Body — XML with candidate overrides
{
"record": {
"xml": "<ScreeningResults>...</ScreeningResults>",
"search_id": "76190bc0-e58c-494f-92c5-0c9a47389528",
"search_date": "2025-04-30",
"order_id": "41115779",
"order_number": "41115779.1",
"applicant_state": "CA",
"candidate_info": {
"first_name": "John",
"middle_name": "A",
"last_name": "Doe",
"date_of_birth": "1990-05-15",
"ssn": "123-45-6789",
"address": "123 Main St, Los Angeles, CA 90001"
},
"cases": [
{ "court_search_id": "CS40929433A", "offense_id": "34789896", "is_excluded": false }
],
"search_type": "Statewide_criminal"
}
}
Example Request (cURL / Python / JavaScript)
Example Request (cURL / Python / JavaScript)
curl -X POST "https://cra.pr.snh-ai.com/evaluate" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"record": {
"xml": "<ScreeningResults><login><account>05022022</account><username>test</username></login><postResults order=\"12345\"><case><case_number>MJ-05003-TR-0006023-2020</case_number><offense_date>20200310</offense_date><jurisdiction_state>PA</jurisdiction_state><chargeinfo><charge>DRIVING WHILE OPERATOR PRIVILEGES SUSPENDED</charge><disposition>GUILTY</disposition></chargeinfo></case></postResults></ScreeningResults>",
"search_id": "76190bc0-e58c-494f-92c5-0c9a47389528",
"search_date": "2025-04-30",
"order_id": "41115779",
"order_number": "41115779.1",
"applicant_state": "CA",
"candidate_info": {
"first_name": "John",
"middle_name": "A",
"last_name": "Doe",
"date_of_birth": "1990-05-15",
"ssn": "123-45-6789",
"address": "123 Main St, Los Angeles, CA 90001"
},
"cases": [
{ "court_search_id": "CS40929433A", "offense_id": "34789896", "is_excluded": false }
],
"search_type": "Statewide_criminal"
}
}'
import requests
response = requests.post(
"https://cra.pr.snh-ai.com/evaluate",
headers={
"Authorization": "Bearer YOUR_JWT_TOKEN",
"Content-Type": "application/json"
},
json={
"record": {
"xml": "<ScreeningResults>...</ScreeningResults>",
"search_id": "76190bc0-e58c-494f-92c5-0c9a47389528",
"search_date": "2025-04-30",
"order_id": "41115779",
"order_number": "41115779.1",
"applicant_state": "CA",
"candidate_info": {
"first_name": "John",
"middle_name": "A",
"last_name": "Doe",
"date_of_birth": "1990-05-15",
"ssn": "123-45-6789",
"address": "123 Main St, Los Angeles, CA 90001"
},
"cases": [
{ "court_search_id": "CS40929433A", "offense_id": "34789896", "is_excluded": false }
],
"search_type": "Statewide_criminal"
}
}
)
result = response.json()
decision = result["data"]["decision"]
print(f"Search queue: {decision['search_queue']}")
for court in decision["court_decisions"]:
for case in court["case_decisions"]:
for offense in case["offenses"]:
print(offense["offense_id"], offense["charge_decision"], offense["routing"]["queue"])
const response = await fetch("https://cra.pr.snh-ai.com/evaluate", {
method: "POST",
headers: {
"Authorization": "Bearer YOUR_JWT_TOKEN",
"Content-Type": "application/json"
},
body: JSON.stringify({
record: {
xml: "<ScreeningResults>...</ScreeningResults>",
search_id: "76190bc0-e58c-494f-92c5-0c9a47389528",
search_date: "2025-04-30",
order_id: "41115779",
order_number: "41115779.1",
applicant_state: "CA",
candidate_info: {
first_name: "John",
middle_name: "A",
last_name: "Doe",
date_of_birth: "1990-05-15",
ssn: "123-45-6789",
address: "123 Main St, Los Angeles, CA 90001"
},
cases: [
{ court_search_id: "CS40929433A", offense_id: "34789896", is_excluded: false }
],
search_type: "Statewide_criminal"
}
})
});
const result = await response.json();
const decision = result.data.decision;
console.log(`Search queue: ${decision.search_queue}`);
for (const court of decision.court_decisions) {
for (const c of court.case_decisions) {
for (const offense of c.offenses) {
console.log(offense.offense_id, offense.charge_decision, offense.routing.queue);
}
}
}
Example Response — Success (200)
Example Response — Success (200)
{
"success": true,
"data": {
"search_id": "42198501",
"correlation_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "success",
"errors": [],
"decision": {
"search_id": "42198501",
"decision_date": "2026-05-19T14:22:10Z",
"record_decision": "REPORTABLE",
"search_queue": "Auditor",
"order_id": "41115779",
"order_number": "41115779.1",
"ubs_search_type": "Statewide_criminal",
"court_decisions": [
{
"court_search_id": "CS40929433A",
"court_decision": "REPORTABLE",
"court_queue": "Auditor",
"case_decisions": [
{
"case_number": "2024-CF-00312",
"case_decision": "REPORTABLE",
"case_queue": "Auditor",
"jurisdiction_state": "TX",
"id_match": {
"is_match": true,
"match_score": 0.925,
"name_score": 1,
"dob_score": 1,
"ssn_score": 0,
"address_score": 1,
"details": "Name/Alias Score: 100 % (Weight: 45 %)\nDOB Score: 100 % (Weight: 40 %)\nSSN Score: 0 % (Weight: 10 %)\nAddress Score: 100 % (Weight: 5 %)\nOverall Score: 92.5 % (Qualify: 85 %)"
},
"offenses": [
{
"offense_id": "34789896",
"charge": "THEFT OF PROPERTY >= $2,500",
"charge_decision": "REPORTABLE",
"rationale": "Conviction within 7-year lookback period. Felony conviction is reportable under federal and Texas state law.",
"type": "FELONY",
"disposition": "GUILTY",
"needs_human_review": false,
"cited_rules": [
"FCRA_CONV_NO_FEDERAL_LIMIT",
"TX_CONV_NO_STATE_LIMIT"
],
"citations": [
"15 U.S.C. § 1681c(a)(2) - No federal time limit on criminal convictions",
"Tex. Bus. & Com. Code § 20.05 - Texas has no state-specific conviction limit"
],
"routing": {
"queue": "Auditor",
"reportability": "Reportable",
"is_automatable": false,
"identity_level": "High",
"identity_score": 0.925,
"identity_insufficient": false,
"reportability_insufficient": false
}
}
]
}
]
}
]
},
"timing": {
"xml_transformer_ms": 1200.5,
"compliance_ms": 330.7,
"total_ms": 1850.3
}
},
"meta": {
"api_version": "2.0.0",
"process_time_ms": 1855,
"correlation_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
}
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.Example Response — With Validation Issues (200)
Example Response — With Validation Issues (200)
validation object alongside the decision. The validation field is only present when issues exist:{
"success": true,
"data": {
"search_id": "40851153",
"correlation_id": "966f69b2-2855-4a67-b4f3-cd8b3da053c7",
"status": "success",
"errors": [],
"validation": {
"status": "needs_manual_review",
"issues": {
"errors": [
{
"level": "candidate",
"field": "subject_ssn",
"issue": "SSN is missing or could not be determined.",
"description": "SSN is required to confirm identity at the highest tier.",
"case_id": "",
"case_number": ""
},
{
"level": "offense",
"field": "disposition",
"issue": "Disposition is missing or could not be determined.",
"description": "Disposition drives reportability, so missing values force the charge to Insufficient Data.",
"case_id": "case-1",
"case_number": "J-1401-TR-200304452",
"offense_id": "34714104"
}
],
"warnings": [
{
"level": "offense",
"field": "type_class",
"issue": "Type class mismatch: rule='MISDEMEANOR', llm/cache='Other'",
"description": "The rule-engine classification takes precedence; this is informational.",
"case_id": "case-1",
"case_number": "J-1401-TR-200304452",
"offense_id": "34714104"
}
]
}
},
"decision": {
"search_id": "40851153",
"decision_date": "2026-03-31T20:22:36Z",
"search_queue": "Insufficient Data",
"order_id": "",
"court_decisions": [ "..." ]
},
"timing": {
"xml_transformer_ms": 22095.3,
"compliance_ms": 330.7,
"total_ms": 30016.1
}
},
"meta": {
"api_version": "2.0.0",
"process_time_ms": 30020,
"correlation_id": "966f69b2-2855-4a67-b4f3-cd8b3da053c7"
}
}
issues.errors— Hard data-quality problems (missing SSN, missing disposition, indeterminate charge classification). These typically force affected charges into theInsufficient Dataqueue, which then propagates up throughcase_queue,court_queue, andsearch_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.
Example Response — Partial (206)
Example Response — Partial (206)
status is partial and degradation is included. The errors array carries the engine-level failure messages:{
"success": true,
"data": {
"search_id": "42198503",
"correlation_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"status": "partial",
"errors": [
"Compliance-Engine failed: read timeout after 30s"
],
"decision": {
"search_id": "42198503",
"decision_date": "2026-05-22T01:00:00Z",
"search_queue": "Auditor",
"order_id": "",
"court_decisions": [ "..." ]
},
"degradation": {
"xml_transformer": { "status": "available", "error": null },
"compliance_engine": { "status": "degraded", "error": "read timeout after 30s" }
},
"timing": {
"xml_transformer_ms": 1500.2,
"compliance_ms": 400.1,
"total_ms": 2100.5
}
},
"meta": {
"api_version": "2.0.0",
"process_time_ms": 2105,
"correlation_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901"
}
}
Example Response — validation_required (court search ID / offense ID count mismatch)
Example Response — validation_required (court search ID / offense ID count mismatch)
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:{
"success": false,
"data": {
"search_id": "76190bc0-e58c-494f-92c5-0c9a47389528",
"correlation_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "validation_required",
"errors": [
"XML-Transformer failed: XML-Transformer HTTP error 422: Court search ID and offense ID counts do not match the XML: 17 court search ID(s) in the request but 24 case(s) in the XML; 22 offense ID(s) in the request but 32 offense(s) in the XML. Each court search ID must map to exactly one XML case and each offense ID to exactly one XML offense; resubmit with the complete set."
],
"degradation": {
"xml_transformer": {
"status": "failed",
"error": "Court search ID and offense ID counts do not match the XML: 17 court search ID(s) in the request but 24 case(s) in the XML; 22 offense ID(s) in the request but 32 offense(s) in the XML. Each court search ID must map to exactly one XML case and each offense ID to exactly one XML offense; resubmit with the complete set."
},
"compliance_engine": { "status": "not_called", "error": null }
},
"timing": {
"xml_transformer_ms": 120.5,
"compliance_ms": 0,
"total_ms": 125.0
}
},
"meta": {
"api_version": "2.0.0",
"process_time_ms": 130,
"correlation_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
}
/transform-xml (HTTP 422):{
"status": "error",
"error_code": "CASE_OFFENSE_COUNT_MISMATCH",
"message": "Court search ID and offense ID counts do not match the XML: 1 court search ID(s) in the request but 2 case(s) in the XML; 1 offense ID(s) in the request but 3 offense(s) in the XML. Each court search ID must map to exactly one XML case and each offense ID to exactly one XML offense; resubmit with the complete set.",
"search_id": "99916916",
"details": {
"court_search_id_count": 1,
"xml_case_count": 2,
"offense_id_count": 1,
"xml_offense_count": 3,
"court_search_id_matches_cases": false,
"offense_id_matches_offenses": false
}
}
| Field | Example |
|---|---|
error_code | CASE_OFFENSE_COUNT_MISMATCH |
http_status | 422 |
details.court_search_id_count | 17 (evaluate example) / 1 (transform-xml example) |
details.xml_case_count | 24 / 2 |
details.offense_id_count | 22 / 1 |
details.xml_offense_count | 32 / 3 |
details.court_search_id_matches_cases | false |
details.offense_id_matches_offenses | false |
Validation
Thevalidation 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:
{
"validation": {
"status": "needs_manual_review",
"issues": {
"errors": [ { "level": "...", "field": "...", "issue": "...", "description": "...", "case_id": "...", "case_number": "...", "offense_id": "..." } ],
"warnings": [ { "level": "...", "field": "...", "issue": "...", "description": "...", "case_id": "...", "case_number": "...", "offense_id": "..." } ]
}
}
}
| Field | Type | Description |
|---|---|---|
level | string | Where the issue was found: candidate, case_subject, case, or offense |
field | string | The specific field that failed. Examples: subject_ssn, disposition, disposition_class, type_class |
issue | string | Short human-readable description |
description | string | Longer explanation of why the field matters |
case_id | string | Internal case identifier (empty for candidate-level issues) |
case_number | string | Associated case number (empty for candidate-level issues) |
offense_id | string | Offense identifier (only on offense-level items) |
errors vs warnings:errorsare hard data-quality problems — they typically force affected charges into theInsufficient Dataqueue, which then bubbles up throughcase_queue→court_queue→search_queue.warningsare 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
| Field | Description | Example values | Notes |
|---|---|---|---|
search_id | Your unique identifier for this background check search | "99908723", "76190bc0-..." | Required |
search_date | When the search was initiated | "2026-06-16" | Required — YYYY-MM-DD |
order_id | Your order identifier for grouping searches | "789", "41115779" | Required — echoed in response |
order_number | Sub-order number within an order | "789.1", "41115779.1" | Required — format orderID.subOrderID |
xml | Raw screening XML from the vendor | "<ScreeningResults>...</ScreeningResults>" | Initial submission — mutually exclusive with record_json |
submission_type | Flag indicating a resubmission | "resubmit" | Required when using record_json |
record_json | Pre-processed criminal data for resubmission | { "criminal_search_results": [...] } | Must not contain candidate_info, submission_type, order_id, or order_number |
candidate_info | The person being screened — used for identity matching | { "first_name": "John", ... } | Required for record_json; recommended for XML |
candidate_info.first_name | Candidate’s first name | "John" | Required when candidate_info is sent / with record_json |
candidate_info.last_name | Candidate’s last name | "Doe" | Required when candidate_info is sent / with record_json |
candidate_info.date_of_birth | Candidate’s date of birth | "2000-01-01" | Required when candidate_info is sent / with record_json — YYYY-MM-DD |
candidate_info.ssn | Candidate’s Social Security Number — improves identity match confidence | "123-45-6789" | Any format accepted |
candidate_info.address | Candidate’s current address — used for identity matching | "123 Main St, Austin, TX 78701" | Single-line string |
tenant_id | Your organization identifier | "UBS", "T001" | Optional |
applicant_state | US state where the candidate will work — triggers state-specific rules | "CA", "TX" | Optional |
customer_state | Client HQ / work state for state-specific rule matching | "CA", "TX" | Optional — distinct from applicant_state |
cases | Maps each charge to a court/offense ID and controls response inclusion | See matching example below | Optional — omit or [] to evaluate all |
cases[].court_search_id | Which court search this charge belongs to | "CS40929433A", "A" | Same ID repeats when a court has multiple charges |
cases[].offense_id | Unique identifier for the charge | "34789896", "1" | Total rows must equal total charges |
cases[].is_excluded | Whether to exclude this charge from the response | true, false | Required boolean — omitting returns HTTP 400 |
search_type | Type of criminal search performed | "Statewide_criminal", "County_criminal" | Optional — echoed as ubs_search_type |
Response — envelope
| Field | Description | Example values | Notes |
|---|---|---|---|
success | Whether the request was processed | true, false | true for HTTP 200/206; false for validation errors or failures |
data.status | Overall processing outcome | "success", "partial", "validation_required", "failed" | Determines what data is available in the response |
data.errors | Processing errors (engine timeouts, network failures, count mismatches) | [], ["Compliance-Engine failed: ..."] | Empty when no errors; distinct from data-quality validation issues |
data.validation.status | Data-quality assessment of the submitted record | "needs_manual_review" | Omitted when source data is clean |
data.timing.total_ms | Total processing time | 2121, 1850.3 | Milliseconds |
data.degradation.*.status | Health of each processing component | "available", "degraded", "failed", "not_called" | Only present when something went wrong |
meta.api_version | API version that processed this request | "2.0.0" | |
meta.correlation_id | Unique trace ID for this request — share with support | "a1b2c3d4-..." | Same as data.correlation_id |
200 success · 206 partial · 400 bad input · 422 validation_required · 500 failed
Response — decision labels (*_decision)
| Field | Description | Values | Notes |
|---|---|---|---|
offenses[].charge_decision | Whether this specific charge can be included in a background report | REPORTABLE, NOT_REPORTABLE, MANUAL_REVIEW | From the compliance engine |
case_decisions[].case_decision | Reportability decision rolled up from all charges in this case | REPORTABLE, NOT_REPORTABLE, MANUAL_REVIEW | Derived from case_queue |
court_decisions[].court_decision | Reportability decision rolled up from all cases in this court | REPORTABLE, NOT_REPORTABLE, MANUAL_REVIEW | Derived from court_queue |
record_decision | Overall reportability decision for the entire record | REPORTABLE, NOT_REPORTABLE, MANUAL_REVIEW | Derived from search_queue |
*_queue value | *_decision value |
|---|---|
Automation | NOT_REPORTABLE |
Auditor | REPORTABLE |
Insufficient Data | MANUAL_REVIEW |
Response — routing queues (*_queue)
| Field | Description | Values | Rollup precedence |
|---|---|---|---|
search_queue | Where the entire record should be routed | Automation, Auditor, Insufficient Data | Highest-priority child queue wins |
court_queue | Where all cases in this court should be routed | Automation, Auditor, Insufficient Data | Insufficient Data > Auditor > Automation |
case_queue | Where all charges in this case should be routed | Automation, Auditor, Insufficient Data | |
routing.queue | Where this individual charge should be routed | Automation, Auditor, Insufficient Data | Per-charge (not rolled up) |
Response — per-offense routing
| Field | Description | Example values |
|---|---|---|
routing.reportability | Human-readable summary of whether this charge can be reported | Reportable, Not Reportable, Not Enough Info |
routing.identity_level | Confidence that the candidate matches the record subject | High (≥ 0.9), Medium (0.7–0.89), Not Enough Info, Not Matching (≤ −1) |
routing.identity_score | Numeric identity match score (0–1), higher is better | 0.925, 0.85, 0.40 |
routing.is_automatable | Whether this charge can be processed without human review | true (only when queue is Automation), false |
routing.identity_insufficient | Whether there is not enough candidate/record data for a reliable identity match | true, false |
routing.reportability_insufficient | Whether there is not enough charge data (missing disposition, unclear type) for an automated decision | true, false |
routing.third_id_required | Whether a third identity factor (SSN or address) is needed but was not available | true, false |
needs_human_review | Whether this charge requires a human to make the reporting decision | true (when MANUAL_REVIEW), false |
Response — id_match (per case)
| Field | Description | Example values |
|---|---|---|
is_match | Whether the candidate is considered the same person as the record subject | true, false |
match_score | Overall identity confidence as a weighted score (0 = no match, 1 = perfect match) | 0.85, 0.925, 0.40 |
name_score | How closely the names match (0–1), accounting for aliases | 0–1 |
dob_score | How closely the dates of birth match (0–1) | 0–1 |
ssn_score | How closely the SSNs match (0 if not available, 1 = exact match) | 0–1 |
address_score | How closely the addresses match. -1 means address was not available for comparison. | 1, 0, -1 |
details | Human-readable breakdown of each component’s score, weight, and qualifying threshold | "Name/Alias Score: 100 % ... Qualify: 70 %" |
Response — other offense fields
| Field | Description | Example values | |
|---|---|---|---|
offense_id | Unique identifier for this charge, from the source screening data | "48870", "40112987" | |
charge | The charge description as it appears in the criminal record | "Speeding", "THEFT OF PROPERTY >= $2,500" | |
type | Severity classification of the charge (echoed from input) | "Misdemeanor", "Felony" | |
disposition | How the charge was resolved (echoed from input) | "Guilty", "Dismissed", "Pending" | |
cited_rules | Compliance rule IDs that determined this charge’s decision | ["GENERAL_TRAFFIC_NON_REPORT", "TX_CONV_NO_STATE_LIMIT"] | |
citations | Legal statute references supporting the decision | ["15 U.S.C. § 1681c(a)(5)"], [] | |
court_search_id | Court search identifier from the vendor / cases[] mapping | "CS40929433A", "111111" | "TBD" only when no mapping was supplied |
case_number | The court case number this charge belongs to | "2009D 008764" |
Request — offense sentence (optional)
Omit sentence entirely when not applicable. When included, all fields below are optional.
| Field | Description | Example values |
|---|---|---|
incarceration_date | When the subject began serving time | "2009-02-15", null |
release_date | When the subject was released from custody | "2010-01-15", null |
is_serving | Whether the subject is currently incarcerated for this sentence | true, false |
Response — validation (when issues exist)
| Field | Description | Example values |
|---|---|---|
validation.status | Overall data-quality assessment — omitted when no issues found | "needs_manual_review" |
issues.errors[].level | Scope of the issue: who or what has the problem | candidate, case_subject, case, offense |
issues.errors[].field | The specific data field with the problem | subject_ssn, disposition, disposition_class, type_class |
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):
record_decision + search_queue (record level)
└── court_decisions[]
├── court_decision + court_queue (court level)
└── case_decisions[]
├── case_decision + case_queue (case level)
├── id_match (identity match scores)
└── offenses[]
├── charge_decision (REPORTABLE | NOT_REPORTABLE | MANUAL_REVIEW)
├── rationale
├── cited_rules
├── citations
└── routing (queue, reportability, identity_level, ...)
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:
*_queue | *_decision |
|---|---|
Automation | NOT_REPORTABLE |
Auditor | REPORTABLE |
Insufficient Data | MANUAL_REVIEW |
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:
Insufficient Data(highest) — if any child queue isInsufficient DataAuditor— if any child isAuditor(and none areInsufficient Data)Automation(lowest) — every child isAutomation
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".Related
- Charge-Level Routing — Queue categories, routing matrix, and overrides
- Authentication — How to get JWT tokens
- Glossary — Decision values and compliance terminology
