> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pr.snh-ai.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Integration Testing Scenarios

> Test scenarios for validating your integration — decision paths, error handling, identity matching, and routing logic

These scenarios cover the primary decision paths, error conditions, and edge cases your integration should handle. Each happy-path scenario includes a real sample response you can use as a reference for field mapping and assertion logic.

## Scenario Summary

| # | Scenario                                 | Search ID | `record_decision` | `search_queue`    | Key Validation                         |
| - | ---------------------------------------- | --------- | ----------------- | ----------------- | -------------------------------------- |
| 1 | Single offense, traffic, dismissed       | 40929433  | NOT\_REPORTABLE   | Automation        | 1 charge auto-cleared by rules         |
| 2 | Multiple offenses, all not reportable    | 40832032  | NOT\_REPORTABLE   | Automation        | 3 charges across 3 courts, all cleared |
| 3 | Dismissed traffic charges, older records | 41537337  | NOT\_REPORTABLE   | Automation        | 4 offenses, low identity match (40%)   |
| 4 | Mixed: some reportable, some not         | 41991032  | REPORTABLE        | Auditor           | 1 REPORTABLE + 4 NOT\_REPORTABLE cases |
| 5 | Reportable with multiple convictions     | 41274791  | REPORTABLE        | Auditor           | 3 guilty charges, state-specific rules |
| 6 | Insufficient data, medium identity       | 41002798  | MANUAL\_REVIEW    | Insufficient Data | Identity 80%, mixed offense decisions  |
| 7 | Insufficient reportability data          | 40697757  | REPORTABLE        | Auditor           | High identity but missing offense data |

***

## Scenario 1 — Single Offense, Not Reportable

**Search ID:** `40929433` | **Candidate:** Ebony Ferrell | **State:** TX | **Type:** USA\_CRIMINAL\_PLUS

A single speeding charge auto-cleared by traffic exclusion rules. Simplest possible path.

**What to Assert:**

* `decision.record_decision` = `NOT_REPORTABLE`
* `decision.search_queue` = `Automation`
* 1 court, 1 case, 1 offense
* `case_decision` = `NOT_REPORTABLE`, `case_queue` = `Automation`
* `charge_decision` = `NOT_REPORTABLE`, `needs_human_review` = `false`
* `routing.queue` = `Automation`, `routing.is_automatable` = `true`
* `cited_rules` includes `GENERAL_TRAFFIC_NON_REPORT`, `EXCLUSION_FILTER_CHARGE`
* `id_match.is_match` = `true`, `match_score` = `0.85`

<Accordion title="Sample Response (condensed)">
  ```json theme={null}
  {
    "decision": {
      "search_id": "40929433",
      "record_decision": "NOT_REPORTABLE",
      "search_queue": "Automation",
      "court_decisions": [{
        "court_queue": "Automation",
        "court_decision": "NOT_REPORTABLE",
        "case_decisions": [{
          "case_number": "",
          "case_queue": "Automation",
          "case_decision": "NOT_REPORTABLE",
          "jurisdiction_state": "TX",
          "id_match": { "is_match": true, "match_score": 0.85 },
          "offenses": [{
            "offense_id": "34789896",
            "charge": "SPEEDING 15MPH OR MORE (ST RD) (SERIOUSTRAF VIOL) 60 MPH IN A 40 MPH ZONE",
            "charge_decision": "NOT_REPORTABLE",
            "rationale": "The traffic violations are not reportable, unless it's a felony.\nGeneral traffic charges are not reportable.\nExclusion rule filters common non-reportable cases considering charge.\n",
            "cited_rules": ["GENERAL_TRAFFIC_NON_REPORT", "GENERAL_EX_RULES_TRAFFIC", "EXCLUSION_FILTER_CHARGE"],
            "routing": { "queue": "Automation", "reportability": "Not Reportable", "is_automatable": true, "identity_level": "Medium", "identity_score": 0.85 }
          }]
        }]
      }]
    }
  }
  ```
</Accordion>

***

## Scenario 2 — Multiple Offenses, All Not Reportable

**Search ID:** `40832032` | **Candidate:** Erline Lacombe | **State:** FL | **Type:** USA\_CRIMINAL\_OFFENDER

Three charges across three court searches — a traffic violation (guilty but old), and two battery charges with "No Information" dispositions. All cleared by rules.

**What to Assert:**

* `decision.record_decision` = `NOT_REPORTABLE`
* `decision.search_queue` = `Automation`
* 3 courts, 3 cases, 3 offenses — all `NOT_REPORTABLE` / `Automation`
* Different rules applied per charge: traffic rules vs. disposition-based rules
* `id_match.match_score` = `0.85` on all cases

<Accordion title="Sample Response (condensed)">
  ```json theme={null}
  {
    "decision": {
      "record_decision": "NOT_REPORTABLE",
      "search_queue": "Automation",
      "court_decisions": [
        {
          "court_decision": "NOT_REPORTABLE",
          "case_decisions": [{
            "case_number": "13081231TI40A",
            "case_decision": "NOT_REPORTABLE",
            "offenses": [{
              "offense_id": "34696557",
              "charge": "Red Light Camera Violation",
              "charge_decision": "NOT_REPORTABLE",
              "cited_rules": ["FL_CONV_NO_STATE_LIMIT", "GENERAL_TRAFFIC_NON_REPORT", "GENERAL_NON_REPORT_OVER_7YR", "GENERAL_EX_RULES_INFRACTION", "GENERAL_EX_RULES_TRAFFIC", "EXCLUSION_FILTER_CASE_NUMBER"],
              "routing": { "queue": "Automation", "reportability": "Not Reportable" }
            }]
          }]
        },
        {
          "court_decision": "NOT_REPORTABLE",
          "case_decisions": [{
            "case_number": "23007248MM10A",
            "case_decision": "NOT_REPORTABLE",
            "offenses": [{
              "offense_id": "34696558",
              "charge": "BATTERY",
              "charge_decision": "NOT_REPORTABLE",
              "cited_rules": ["GENERAL_DSIP_NON_REPORT", "GENERAL_EX_RULES_INFRACTION", "EXCLUSION_FILTER_DISPOSITION"],
              "routing": { "queue": "Automation", "reportability": "Not Reportable" }
            }]
          }]
        },
        {
          "court_decision": "NOT_REPORTABLE",
          "case_decisions": [{
            "case_number": "23007248MM10A",
            "case_decision": "NOT_REPORTABLE",
            "offenses": [{
              "offense_id": "34696559",
              "charge": "Battery",
              "charge_decision": "NOT_REPORTABLE",
              "cited_rules": ["GENERAL_DSIP_NON_REPORT", "GENERAL_EX_RULES_INFRACTION", "EXCLUSION_FILTER_DISPOSITION"],
              "routing": { "queue": "Automation", "reportability": "Not Reportable" }
            }]
          }]
        }
      ]
    }
  }
  ```
</Accordion>

***

## Scenario 3 — Dismissed Traffic Charges, Low Identity

**Search ID:** `41537337` | **Candidate:** Brian Briley | **State:** KS | **Type:** USA\_CRIMINAL\_OFFENDER

Four dismissed traffic charges across two court searches. Identity match is **Not Enough Info** (40%) — only DOB matched, name did not. All charges still routed to Automation because they are Not Reportable regardless of identity level.

**What to Assert:**

* `decision.record_decision` = `NOT_REPORTABLE`
* `decision.search_queue` = `Automation`
* `id_match.is_match` = `false`, `match_score` = `0.40`
* `routing.identity_level` = `Not Enough Info` on all offenses
* All 4 offenses: `charge_decision` = `NOT_REPORTABLE`, `routing.queue` = `Automation`

<Accordion title="Sample Response (condensed)">
  ```json theme={null}
  {
    "decision": {
      "record_decision": "NOT_REPORTABLE",
      "search_queue": "Automation",
      "court_decisions": [{
        "case_decisions": [{
          "case_number": "01TC08508",
          "case_decision": "NOT_REPORTABLE",
          "id_match": { "is_match": false, "match_score": 0.4, "name_score": 0, "dob_score": 0.4 },
          "offenses": [
            { "offense_id": "35386283", "charge": "NO LIABILITY INSURANCE", "charge_decision": "NOT_REPORTABLE", "routing": { "queue": "Automation", "identity_level": "Not Enough Info", "identity_score": 0.4 } },
            { "offense_id": "35386284", "charge": "DRIVE WHILE SUSPENDED/1ST OFF", "charge_decision": "NOT_REPORTABLE", "routing": { "queue": "Automation", "identity_level": "Not Enough Info", "identity_score": 0.4 } }
          ]
        }]
      }]
    }
  }
  ```
</Accordion>

***

## Scenario 4 — Mixed Cases: Reportable + Not Reportable

**Search ID:** `41991032` | **Candidate:** Michealah Hartman | **State:** IA | **Type:** County\_criminal

Five cases under one court search. One DUI conviction is REPORTABLE (Auditor queue), four traffic charges are NOT\_REPORTABLE (Automation). The single reportable case escalates the entire search to `REPORTABLE` / `Auditor`.

**What to Assert:**

* `decision.record_decision` = `REPORTABLE` (escalated by 1 reportable case)
* `decision.search_queue` = `Auditor` (Auditor > Automation in rollup)
* Case `08291 OWIN027732`: `case_decision` = `REPORTABLE`, `case_queue` = `Auditor`
* Other 4 cases: `case_decision` = `NOT_REPORTABLE`, `case_queue` = `Automation`
* Reportable offense: `cited_rules` includes `GENERAL_DUI_MISD_REPORTABLE`

<Accordion title="Sample Response (condensed)">
  ```json theme={null}
  {
    "decision": {
      "record_decision": "REPORTABLE",
      "search_queue": "Auditor",
      "court_decisions": [{
        "court_decision": "REPORTABLE",
        "case_decisions": [
          { "case_number": "08291 NTA0064479", "case_decision": "NOT_REPORTABLE", "case_queue": "Automation",
            "offenses": [{ "charge": "DUS - DRIVING WHILE LICENSE DENIED...", "charge_decision": "NOT_REPORTABLE", "routing": { "queue": "Automation" } }] },
          { "case_number": "08291 OWIN027732", "case_decision": "REPORTABLE", "case_queue": "Auditor",
            "offenses": [{ "charge": "OPERATING WHILE UNDER THE INFLUENCE 1ST OFFENSE", "charge_decision": "REPORTABLE", "routing": { "queue": "Auditor", "reportability": "Reportable" } }] },
          { "case_number": "08291 SMSM046064", "case_decision": "NOT_REPORTABLE", "case_queue": "Automation" },
          { "case_number": "08291 SRIN027850", "case_decision": "NOT_REPORTABLE", "case_queue": "Automation" },
          { "case_number": "08291 STA0045633", "case_decision": "NOT_REPORTABLE", "case_queue": "Automation" }
        ]
      }]
    }
  }
  ```
</Accordion>

***

## Scenario 5 — Reportable with Multiple Convictions

**Search ID:** `41274791` | **Candidate:** Vanessa Stanley | **State:** CA | **Type:** County\_criminal

Three guilty charges in Colorado — Disturbing the Peace, Assault, and Public Fighting. All are reportable under state-specific rules with legal citations.

**What to Assert:**

* `decision.record_decision` = `REPORTABLE`
* `decision.search_queue` = `Auditor`
* All 3 offenses: `charge_decision` = `REPORTABLE`
* `cited_rules` includes state-specific `CO_CONV_NO_STATE_LIMIT`
* `citations` populated with Colorado statute references
* `routing.reportability` = `Not Enough Info` (insufficient reportability data despite conviction)

<Accordion title="Sample Response (condensed)">
  ```json theme={null}
  {
    "decision": {
      "record_decision": "REPORTABLE",
      "search_queue": "Auditor",
      "court_decisions": [{
        "case_decisions": [{
          "case_number": "25GS006878",
          "case_decision": "REPORTABLE",
          "jurisdiction_state": "CO",
          "offenses": [
            { "offense_id": "35117601", "charge": "DISTURBING THE PEACE", "charge_decision": "REPORTABLE",
              "cited_rules": ["CO_CONV_NO_STATE_LIMIT"], "citations": ["Colo. Rev. Stat. § 12-14.3-105"],
              "routing": { "queue": "Auditor", "reportability": "Not Enough Info" } },
            { "offense_id": "35117602", "charge": "ASSAULT", "charge_decision": "REPORTABLE",
              "cited_rules": ["CO_CONV_NO_STATE_LIMIT", "GENERAL_CRIMINAL_REPORT_UNDER_7YR"],
              "routing": { "queue": "Auditor" } },
            { "offense_id": "35117603", "charge": "PUBLIC FIGHTING", "charge_decision": "REPORTABLE",
              "cited_rules": ["CO_CONV_NO_STATE_LIMIT"],
              "routing": { "queue": "Auditor" } }
          ]
        }]
      }]
    }
  }
  ```
</Accordion>

***

## Scenario 6 — Insufficient Data, Medium Identity

**Search ID:** `41002798` | **Candidate:** TIM TAYLOR | **State:** KS | **Type:** County\_criminal

Identity match is **Medium** (80%, below the 85% threshold) with `identity_insufficient: true`. Mixed offense decisions — some reportable (guilty pleas), some dismissed. The insufficient identity data escalates the reportable charges to `Insufficient Data` queue.

**What to Assert:**

* `decision.record_decision` = `MANUAL_REVIEW`
* `decision.search_queue` = `Insufficient Data`
* `id_match.is_match` = `false`, `match_score` = `0.80`, `identity_insufficient` = `true` on reportable charges
* Reportable charges → `routing.queue` = `Insufficient Data` (because identity is insufficient)
* Dismissed charges → `routing.queue` = `Automation` (Not Reportable bypasses identity concern)
* `routing.identity_level` = `Not Enough Info` on reportable charges, `Medium` on dismissed charges

<Accordion title="Sample Response (condensed)">
  ```json theme={null}
  {
    "decision": {
      "record_decision": "MANUAL_REVIEW",
      "search_queue": "Insufficient Data",
      "court_decisions": [
        {
          "case_decisions": [{
            "case_number": "2019-CR-000130",
            "case_decision": "MANUAL_REVIEW",
            "case_queue": "Insufficient Data",
            "id_match": { "is_match": false, "match_score": 0.8 },
            "offenses": [
              { "offense_id": "34868564", "charge": "Domestic battery...", "charge_decision": "REPORTABLE",
                "routing": { "queue": "Insufficient Data", "identity_level": "Not Enough Info", "identity_insufficient": true } },
              { "offense_id": "34868565", "charge": "Aggravated battery...", "charge_decision": "NOT_REPORTABLE",
                "routing": { "queue": "Automation", "identity_level": "Medium", "identity_insufficient": false } },
              { "offense_id": "34868566", "charge": "Criminal damage to property...", "charge_decision": "NOT_REPORTABLE",
                "routing": { "queue": "Automation", "identity_level": "Medium" } }
            ]
          }]
        },
        {
          "case_decisions": [{
            "case_number": "2020-TR-001261",
            "case_decision": "MANUAL_REVIEW",
            "case_queue": "Insufficient Data",
            "offenses": [
              { "offense_id": "34868567", "charge": "Driving under influence; 3rd conv...", "charge_decision": "REPORTABLE",
                "routing": { "queue": "Insufficient Data", "identity_level": "Not Enough Info", "identity_insufficient": true } },
              { "offense_id": "34868568", "charge_decision": "NOT_REPORTABLE", "routing": { "queue": "Automation" } },
              { "offense_id": "34868569", "charge_decision": "NOT_REPORTABLE", "routing": { "queue": "Automation" } }
            ]
          }]
        }
      ]
    }
  }
  ```
</Accordion>

***

## Scenario 7 — Insufficient Reportability Data

**Search ID:** `40697757` | **Candidate:** Bryanna Carr | **State:** NC | **Type:** USA\_CRIMINAL

Identity qualifies as a match (85%) but offense data is too sparse — charges are "Not Specified" with missing dispositions. The engine cannot determine reportability, so charges go to `MANUAL_REVIEW` and route to `Auditor`.

**What to Assert:**

* `decision.record_decision` = `REPORTABLE`
* `decision.search_queue` = `Auditor`
* `id_match.is_match` = `true`, `match_score` = `0.85`
* `charge_decision` = `MANUAL_REVIEW` (not enough info to auto-decide)
* `routing.reportability` = `Not Enough Info`
* `routing.reportability_insufficient` = `true`
* `cited_rules` = `[]` (no rules could be applied)
* `validation.issues.errors` count = 16 (extensive missing data)

<Accordion title="Sample Response (condensed)">
  ```json theme={null}
  {
    "decision": {
      "record_decision": "REPORTABLE",
      "search_queue": "Auditor",
      "court_decisions": [
        {
          "case_decisions": [{
            "case_number": "2022D  706536",
            "case_decision": "REPORTABLE",
            "case_queue": "Auditor",
            "id_match": { "is_match": true, "match_score": 0.85, "name_score": 0.45, "dob_score": 0.4 },
            "offenses": [{
              "offense_id": "34569352",
              "charge": "Not Specified",
              "charge_decision": "MANUAL_REVIEW",
              "rationale": "Manual review required, no rules applicable",
              "cited_rules": [],
              "routing": { "queue": "Auditor", "reportability": "Not Enough Info", "reportability_insufficient": true, "identity_level": "Medium" }
            }]
          }]
        },
        {
          "case_decisions": [{
            "case_number": "2023D  713316",
            "case_decision": "REPORTABLE",
            "case_queue": "Auditor",
            "offenses": [{
              "offense_id": "34569353",
              "charge": "Not Specified",
              "charge_decision": "MANUAL_REVIEW",
              "routing": { "queue": "Auditor", "reportability": "Not Enough Info", "reportability_insufficient": true }
            }]
          }]
        }
      ]
    }
  }
  ```
</Accordion>

***

## Error and Edge-Case Scenarios

### Input Validation Errors (HTTP 422)

| Scenario                 | Trigger                                                                                                | Expected Status | Expected Response                                                                                                |
| ------------------------ | ------------------------------------------------------------------------------------------------------ | --------------- | ---------------------------------------------------------------------------------------------------------------- |
| Missing `search_id`      | Omit `record.search_id`                                                                                | 422             | `{"detail": [{"loc": ["body", "record", "search_id"], "msg": "field required", "type": "value_error.missing"}]}` |
| Missing `xml`            | Omit `record.xml`                                                                                      | 422             | Validation error for missing XML field                                                                           |
| Missing `candidate_info` | Omit `record.candidate_info`                                                                           | 400             | `candidate_info must not be empty`                                                                               |
| Empty `candidate_info`   | Send `candidate_info: {}` or `null`                                                                    | 400             | `candidate_info must not be empty`                                                                               |
| Invalid `candidate_info` | Provide `candidate_info` without `first_name`, `last_name`, or `date_of_birth` (or with empty strings) | 400             | `candidate_info requires at minimum: first_name, last_name, and date_of_birth`                                   |
| Malformed XML            | Send truncated or garbled XML                                                                          | 500             | XML parsing error from the transformer service                                                                   |

### `cases[]` Behavior

| Scenario        | Trigger          | Expected Status | Expected Response                                     |
| --------------- | ---------------- | --------------- | ----------------------------------------------------- |
| Missing `cases` | Omit `cases`     | 200 (success)   | All offenses in the record are evaluated              |
| Empty `cases`   | Send `cases: []` | 200 (success)   | Same as omitting `cases` — all offenses are evaluated |

### Processing Failures — Court Search ID / Offense ID Count Mismatch (HTTP 500 on `/evaluate`)

| Scenario                         | Trigger                                                                                                                           | Expected Status | Expected Response                                                                                                                                                                                                                                                                                                                                                                   |
| -------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Partial `cases[]` count mismatch | Send fewer distinct court search IDs or fewer offense ID rows than XML/record\_json (e.g., 17 / 22 vs XML 24 cases / 32 offenses) | 500 (failed)    | `success: false`, `data.status: "failed"`, `data.errors[]` includes `XML-Transformer HTTP error 422: Court search ID and offense ID counts do not match the XML: ...`, `data.degradation.xml_transformer.status: "failed"`, `data.degradation.compliance_engine.status: "not_called"`. XML-Transformer returns HTTP **422** internally (`error_code: CASE_OFFENSE_COUNT_MISMATCH`). |

### Authentication Errors (HTTP 401/403)

| Scenario        | Trigger                                       | Expected Status | Expected Response                              |
| --------------- | --------------------------------------------- | --------------- | ---------------------------------------------- |
| Missing API key | Call `/auth/token` without `X-API-Key` header | 401             | `{"detail": "API key is required"}`            |
| Invalid API key | Call `/auth/token` with wrong key             | 401             | `{"detail": "Invalid API key"}`                |
| Expired JWT     | Call `/evaluate` with expired Bearer token    | 401             | `{"detail": "Token has expired"}`              |
| Tampered JWT    | Call `/evaluate` with modified Bearer token   | 403             | `{"detail": "Could not validate credentials"}` |

### Rate Limiting (HTTP 429)

| Scenario            | Trigger                               | Expected Status | Expected Response                                     |
| ------------------- | ------------------------------------- | --------------- | ----------------------------------------------------- |
| Rate limit exceeded | Exceed configured requests per minute | 429             | `{"detail": "Rate limit exceeded. Try again later."}` |

<Note>
  Rate limits are enforced per API key. Include retry logic with exponential backoff for 429 responses. Contact the AI Team for your account's specific limit.
</Note>

***

## Identity Match and Routing Impact

Identity matching determines **who** the record belongs to. The identity score directly controls which routing queue a charge is assigned to.

### Identity-to-Routing Matrix

Routing is driven by whether the identity **matches** the candidate (`is_match`) and the charge's **reportability**:

| Identity                            | Reportable Charge | Not Reportable Charge | Reportability Not Enough Info |
| ----------------------------------- | ----------------- | --------------------- | ----------------------------- |
| **Matches** (`is_match: true`)      | Auditor           | Automation            | Auditor                       |
| **Does not match** (`Not Matching`) | Automation        | Automation            | Automation                    |
| **Insufficient identity data**      | Insufficient Data | Automation            | Insufficient Data             |

### How Identity Score is Calculated

| Field       | Weight | Notes                                             |
| ----------- | ------ | ------------------------------------------------- |
| **Name**    | 45%    | First + last name match. Middle name is optional. |
| **DOB**     | 40%    | Full date match required. Year-only = partial.    |
| **SSN**     | 10%    | All 9 digits must match exactly.                  |
| **Address** | 5%     | Standardized comparison. No partial match.        |

### Common Score Scenarios

| Available Data                | Max Score | Identity Level  | `is_match`                |
| ----------------------------- | --------- | --------------- | ------------------------- |
| Name + DOB + SSN + Address    | **100%**  | High            | true                      |
| Name + DOB + Address (no SSN) | **90%**   | High            | true                      |
| Name + DOB only               | **85%**   | Medium          | true (is\_match boundary) |
| Partial name + DOB            | **80%**   | Medium          | false                     |
| DOB only                      | **40%**   | Not Enough Info | false                     |

### Identity Impact in Our Samples

| Search ID | `match_score` | `identity_level`         | Impact on Routing                                                 |
| --------- | ------------- | ------------------------ | ----------------------------------------------------------------- |
| 40929433  | 0.85          | Medium                   | Not Reportable = **Automation**                                   |
| 41002798  | 0.80          | Medium / Not Enough Info | Reportable + Insufficient Identity = **Insufficient Data**        |
| 41537337  | 0.40          | Not Enough Info          | Not Reportable = still **Automation** (exclusion filter override) |
| 40697757  | 0.85          | Medium                   | Reportability Not Enough Info + match = **Auditor**               |

### Search Type Differences

The `ubs_search_type` field in the response reflects the type of search that was requested. The response format is identical across all types.

| Search Type             | Description                                                    |
| ----------------------- | -------------------------------------------------------------- |
| `USA_CRIMINAL`          | Standard criminal record search                                |
| `USA_CRIMINAL_OFFENDER` | Offender registry search (may include sex offender registries) |
| `USA_CRIMINAL_PLUS`     | Extended criminal search with additional data sources          |
| `County_criminal`       | County-level court records search                              |

***

## Rationale Structure Guide

Each offense in the response includes a `rationale` field (human-readable text) and a `cited_rules` array (machine-readable rule identifiers). Your back-office can aggregate these to build case-level summaries.

### Per-Offense Fields

| Field                           | Type      | Description                                                     |
| ------------------------------- | --------- | --------------------------------------------------------------- |
| `offenses[].rationale`          | string    | Multi-line text explaining why the charge was decided as it was |
| `offenses[].cited_rules`        | string\[] | Rule IDs that fired (e.g., `GENERAL_DSIP_NON_REPORT`)           |
| `offenses[].citations`          | string\[] | Legal statute references when applicable                        |
| `offenses[].charge_decision`    | string    | `REPORTABLE`, `NOT_REPORTABLE`, or `MANUAL_REVIEW`              |
| `offenses[].needs_human_review` | boolean   | `true` when no rules could fully resolve the charge             |

### Building a Case-Level Rationale

To build the format:

```
Case Number XXXXX, ChargeID YYYYY (CHARGE_DESCRIPTION)
  cleared based on Rule(s): RULE_ID_1, RULE_ID_2
  Rationale: [rationale text]
CaseID XXXXX cleared. Exclusion Rule(s) have cleared N of M charges on this case.
```

Map from these response fields:

| Your Format         | Response Field                   |
| ------------------- | -------------------------------- |
| Case Number         | `case_decisions[].case_number`   |
| ChargeID            | `offenses[].offense_id`          |
| Charge description  | `offenses[].charge`              |
| Rule IDs            | `offenses[].cited_rules[]`       |
| Rationale text      | `offenses[].rationale`           |
| Case-level decision | `case_decisions[].case_decision` |

### Example — Scenario 4 (Mixed Decisions)

From search `41991032`, case `08291 OWIN027732`:

```
Case Number 08291 OWIN027732, ChargeID 35829708 (OPERATING WHILE UNDER THE INFLUENCE 1ST OFFENSE)
  Decision: REPORTABLE
  Rules: GENERAL_DUI_MISD_REPORTABLE, GENERAL_CRIMINAL_REPORT_UNDER_7YR
  Rationale: DUI violations that are convicted are reportable, within the scope of 7 years.
             Criminal charges are reportable within the scope of 7 years unless they're infractions or minor.

Case 08291 OWIN027732: REPORTABLE. 1 of 1 charges are reportable.
```

From the same search, case `08291 NTA0064479`:

```
Case Number 08291 NTA0064479, ChargeID 35829707 (DUS - DRIVING WHILE LICENSE DENIED...)
  Decision: NOT_REPORTABLE
  Rules: GENERAL_TRAFFIC_NON_REPORT, GENERAL_EX_RULES_TRAFFIC, EXCLUSION_FILTER_CHARGE
  Rationale: The traffic violations are not reportable, unless it's a felony.

Case 08291 NTA0064479: NOT_REPORTABLE. Exclusion Rule(s) have cleared 1 of 1 charges.
```

***

## Queue Rollup Logic

Offense-level queues roll up to case, court, and search level using a fixed precedence.

### Precedence (highest wins)

1. **Insufficient Data** — if any child queue is Insufficient Data, the parent is Insufficient Data
2. **Auditor** — if any child queue is Auditor, the parent is Auditor
3. **Automation** — parent is Automation only when every child is Automation

### Rollup Examples from Our Samples

**Scenario 4 — One reportable escalates the whole search:**

```
Offense 35829707: Automation (NOT_REPORTABLE, traffic)
Offense 35829708: Auditor   (REPORTABLE, DUI conviction)
Offense 35829709: Automation (NOT_REPORTABLE, traffic)
Offense 35829710: Automation (NOT_REPORTABLE, dismissed)
Offense 35829711: Automation (NOT_REPORTABLE, traffic)

→ case OWIN027732: Auditor (has one Auditor child)
→ other cases: Automation (all children Automation)
→ court_queue: Auditor (Auditor > Automation)
→ search_queue: Auditor
→ record_decision: REPORTABLE
```

**Scenario 6 — Insufficient data overrides everything:**

```
Offense 34868564: Insufficient Data (REPORTABLE + identity insufficient)
Offense 34868565: Automation (NOT_REPORTABLE)
Offense 34868566: Automation (NOT_REPORTABLE)

→ case 2019-CR-000130: Insufficient Data (one child is Insufficient Data)
→ search_queue: Insufficient Data
→ record_decision: MANUAL_REVIEW
```

**Scenario 2 — Clean automation:**

```
Offense 34696557: Automation
Offense 34696558: Automation
Offense 34696559: Automation

→ All cases: Automation
→ search_queue: Automation
→ record_decision: NOT_REPORTABLE
```

***

## Field Reference

### Response Envelope

| Field                  | Description                       |
| ---------------------- | --------------------------------- |
| `success`              | `true` on 200, `false` on error   |
| `data`                 | Full response payload (see below) |
| `meta.api_version`     | API version (currently `2.0.0`)   |
| `meta.process_time_ms` | Total server processing time      |
| `meta.correlation_id`  | Unique ID for request tracing     |

### Decision Fields

| Field Path                                                       | Values                                          | Description                             |
| ---------------------------------------------------------------- | ----------------------------------------------- | --------------------------------------- |
| `data.decision.record_decision`                                  | `REPORTABLE`, `NOT_REPORTABLE`, `MANUAL_REVIEW` | Top-level outcome for the entire search |
| `data.decision.search_queue`                                     | `Automation`, `Auditor`, `Insufficient Data`    | Top-level routing queue                 |
| `data.decision.court_decisions[].court_decision`                 | same as record\_decision                        | Per-court outcome                       |
| `data.decision.court_decisions[].court_queue`                    | same as search\_queue                           | Per-court routing                       |
| `data.decision.court_decisions[].case_decisions[].case_decision` | same as record\_decision                        | Per-case outcome                        |
| `data.decision.court_decisions[].case_decisions[].case_queue`    | same as search\_queue                           | Per-case routing                        |

### Offense Fields

| Field Path                          | Description                                            |
| ----------------------------------- | ------------------------------------------------------ |
| `offenses[].offense_id`             | Unique identifier for the charge                       |
| `offenses[].charge`                 | Charge description text                                |
| `offenses[].charge_decision`        | `REPORTABLE`, `NOT_REPORTABLE`, or `MANUAL_REVIEW`     |
| `offenses[].rationale`              | Human-readable reasoning                               |
| `offenses[].cited_rules`            | Machine-readable rule IDs                              |
| `offenses[].citations`              | Legal statute references                               |
| `offenses[].needs_human_review`     | `true` if no rules fully resolved the charge           |
| `offenses[].routing.queue`          | `Automation`, `Auditor`, or `Insufficient Data`        |
| `offenses[].routing.reportability`  | `Reportable`, `Not Reportable`, or `Not Enough Info`   |
| `offenses[].routing.is_automatable` | `true` only when queue is `Automation`                 |
| `offenses[].routing.identity_level` | `High`, `Medium`, `Not Enough Info`, or `Not Matching` |
| `offenses[].routing.identity_score` | Numeric score (0–1)                                    |

### Validation Fields

| Field Path                          | Description                                                |
| ----------------------------------- | ---------------------------------------------------------- |
| `data.validation.status`            | `clean` or `needs_manual_review`                           |
| `data.validation.issues.errors[]`   | Data quality issues (missing fields, indeterminate values) |
| `data.validation.issues.warnings[]` | Lower-severity issues (low confidence, date fallbacks)     |

***

## Decision Flow

```mermaid theme={null}
flowchart TD
    Request["Client Request"] --> Auth["Authentication\n/auth/token"]
    Auth -->|"401/403"| AuthError["Auth Error Response"]
    Auth -->|"JWT Token"| Validate["Input Validation"]
    Validate -->|"422"| ValError["Validation Error Response"]
    Validate -->|"Valid"| XMLTransform["Normalize + enrich record"]
    XMLTransform --> ComplianceEngine["Apply compliance rules per offense"]
    ComplianceEngine --> PerOffense["Per-Offense Decision\nREPORTABLE / NOT_REPORTABLE / MANUAL_REVIEW"]
    PerOffense --> RoutingMatrix["Routing Matrix\nIdentity Level x Reportability"]
    RoutingMatrix --> QueueAssign["Queue Assignment\nAutomation / Auditor / Insufficient Data"]
    QueueAssign --> Rollup["Hierarchical Rollup\nOffense → Case → Court → Search"]
    Rollup --> Response["API Response\nrecord_decision + search_queue"]
```

***

## Related

* [Evaluate Endpoint](/api-reference/endpoints/evaluate) — Full API reference for `/evaluate`
* [Routing & Queues](/api-reference/routing) — Detailed routing matrix and rollup logic
* [Identity Matching Logic](/api-reference/identity-matching-logic) — Field-level matching rules and scoring
* [Authentication](/authentication) — API key and JWT setup
* [Error Handling](/error-handling) — Error response formats
