FCRA Partner API · v1.0.0

Order statuses and retrieving results

A search ends in one of three results, and every record it did not furnish has a reason code. Read the status, then the result, then the withheld counts.

What you’ll build

The result reader: code that walks an order’s searches, classifies each as clear, records_found or incomplete, reads the furnished records from GET /v1/fcra/orders/{order_id}/searches/{search_id}, and explains every withheld record by its reason code. You will use the sandbox subjects Records, Withheld and Unavailable to see all three outcomes.

Furnished records are the product’s own record object plus FCRA fields; the controls that decide furnishing versus withholding are documented in full in Controls & reason codes — this guide shows you how to read their output.

Before you start

  • Three completed sandbox orders (see Creating screening orders): Pat Records with both searches, Pat Withheld with a criminal search, Pat Unavailable with a sex-offender search.
  • Records appear only on GET /v1/fcra/orders/{order_id}/searches/{search_id} and in the report — never on the order object.
  • Order statuses: received, in_progress, partial, complete, cancelled, error. Search statuses: queued, in_progress, complete, unavailable, error, cancelled, superseded. Search results: clear, records_found, incomplete (null until finished and on error).
`clear` with withheld counts is still `clear`. A search that found candidates and withheld all of them — because their date of birth did not match exactly, or they were no longer current at the source — reports result: "clear", recordCount: 0 and non-empty withheld counts. The counts explain; they are not records and are never shown to the consumer.

Step by step

  1. Read the order. GET /v1/fcra/orders/{order_id}. Stop when status is complete, error or cancelled; partial means keep waiting.
  2. Classify each search. status first: complete and unavailable are finished; error has no result. Then result: records_found → read records; clear → check withheld; incomplete → read incompleteReasons and coverage.
  3. Read the records. GET /v1/fcra/orders/{order_id}/searches/{search_id} returns records[]: each is the product record plus recordId, type, matchBasis, retrievedAt, verifiedAt, and on criminal records dispositionStatus, per-charge fcraDisposition and fcraRedactions.
  4. Read the coverage. coverage[] has one row per source: mode (live/snapshot), status (answered/unavailable/not_searched), relevant, restricted, candidates, furnished, withheld, captureIds[].
curl https://sandbox.offendersearch.app/v1/fcra/orders/fo_ee0b34c5b52fd35b/searches/fs_9c03d7e1a4b6f258 \
  -H "X-API-Key: $OFFENDERSEARCH_TEST_KEY"
200 — the criminal search for Pat Records
{
  "id": "fs_9c03d7e1a4b6f258",
  "type": "criminal",
  "status": "complete",
  "result": "records_found",
  "recordCount": 1,
  "withheld": {},
  "createdAt": "2026-10-25T14:01:07.318402+00:00",
  "startedAt": "2026-10-25T14:01:07.402118+00:00",
  "completedAt": "2026-10-25T14:01:07.455930+00:00",
  "supersededBy": null,
  "coverage": [],
  "incompleteReasons": [],
  "records": [
    {
      "recordId": "sandbox:criminal:records-1980-01-01",
      "type": "criminal",
      "matchBasis": { "state": "dob_match", "identifiers": ["name", "dob"] },
      "retrievedAt": "2026-10-25T14:01:07.455930+00:00",
      "verifiedAt": "2026-10-25T14:01:07.455930+00:00",
      "sandbox": true,
      "name": { "first": "Pat", "last": "Records", "full": "Pat Records" },
      "dob": "1980-01-01",
      "sources": [{ "code": "ZZ-SANDBOX", "level": "county", "live": true }],
      "state": "ZZ",
      "dispositionStatus": "conviction",
      "cases": [
        { "caseNumber": "SBX-0001", "court": "Sandbox County Court", "filedDate": "2021-03-04",
          "charges": [
            { "description": "Sandbox charge (test data)", "level": "misdemeanor", "disposition": "Guilty",
              "dispositionDate": "2021-09-10", "fcraDisposition": "conviction" }
          ] }
      ]
    }
  ]
}
Pat Withheld — clear, with the reasons
{
  "id": "fs_1e7b4c9a3d5f2086",
  "type": "criminal",
  "status": "complete",
  "result": "clear",
  "recordCount": 0,
  "withheld": { "insufficient_identifiers": 2, "not_current_at_source": 1 },
  "incompleteReasons": [],
  "records": []
}
Pat Unavailable — the sources could not be reached
{
  "id": "fs_7a2d9e4c1b6f3058",
  "type": "sex_offender",
  "status": "unavailable",
  "result": "incomplete",
  "recordCount": 0,
  "withheld": {},
  "incompleteReasons": ["live_unavailable"],
  "records": []
}

Sandbox searches have empty coverage because they make no live calls; a production search lists every source consulted. The production coverage shape is in Results & records.

Reading the result

FieldValuesWhat it decides
resultrecords_foundAt least one record passed every control and was re-verified. Read records[].
resultclearNothing was furnished. withheld may be non-empty — candidates existed but failed a control.
resultincompleteA relevant source could not be searched live or a record could not be re-verified; never clear. incompleteReasons ⊆ source_unanswered, live_unavailable, verification_unavailable.
withheldreason → countinsufficient_identifiers (no exact date-of-birth match), jurisdiction_restricted, disputed_suppressed, not_current_at_source, verification_unavailable; criminal adds obsolete_non_conviction, obsolescence_undeterminable, time_limited.
records[].matchBasis.statedob_matchAlways: only exact-date-of-birth records are furnished.
records[].dispositionStatusconviction, non_conviction, sanction, mixed, unknownCriminal only, across furnished charges. sanction is an administrative exclusion-list record, furnished only with exclusions: "all".
records[].fcraRedactionsreason → countCharges removed from a furnished record by the time rules.
controlVersion (order)fcra-controls-2026-10-19.1The rule set the order ran under; the evidence record pins a hash of the exact rules.

Edge cases and pitfalls

  • `partial` is not final. One search finished, another is running. Keep waiting for order.completed.
  • `status: "unavailable"` is finished. Its result is incomplete; the order still completes. Decide what an incomplete check means for your workflow before go-live.
  • `error` has `result: null` and is reported by search.completed with status: "error" and no result. Read it; do not infer clear.
  • Superseded searches (after a re-verify) stay on the order with status: "superseded" and supersededBy; skip them and read the new search.
  • Exclusion-list records are criminal records with recordType: "healthcare_exclusion"; a conviction-based one reads dispositionStatus: "conviction", an administrative one "sanction" and appears only under exclusions: "all". A list entry with no date of birth is withheld as insufficient_identifiers.
  • `corrections[]` appears on a record a resolved dispute modified; show it with the record.
  • `restricted: true` in a coverage row means a registry whose records are withheld for the order’s purpose; it was not searched live and does not make the result incomplete.

Production checklist

  • Branch on status before result; treat incomplete and error as their own outcomes, never as clear.
  • Show reviewers the withheld reasons and fcraRedactions; show the consumer only furnished records (the portal does this).
  • Store controlVersion with every order you adjudicate on.
  • Skip superseded searches; read supersededBy.
  • Adjudication and any notices to the consumer stay with you; the API furnishes records and the reasons it did not.

Product overview