Developer docs · v1.0.0

Skilled nursing facilities

Resident admission screening, staff exclusion checks, visitor screening at the desk, and a census that stays watched — mapped to the API with a runnable integration.

How screening works in skilled nursing today

A skilled nursing facility (SNF) admits most residents from a hospital. A discharge planner sends the referral — often through a referral-management platform, with a packet that can run to hundreds of pages — and the facility's admissions coordinator or director of admissions decides, frequently the same day, whether to offer a bed. Industry reporting puts the average at more than six referrals per patient with roughly a third accepted, and describes rapid-response admission teams built around that pace. Screening therefore happens at referral review, before a bed is offered, on the identifiers the packet carries: legal name, date of birth, prior addresses.

Residents

No federal Condition of Participation requires a registry or criminal check of a prospective resident. State law does in places, and the duties differ in kind. Illinois' Nursing Home Care Act (210 ILCS 45/2-201.5) requires a facility to request a criminal history background check within 24 hours after admission for every person 18 or older seeking admission; a resident who is an "identified offender" (210 ILCS 45/1-114.01 — which includes a registered sex offender) triggers a fingerprint-based inquiry, a State Police criminal history report and an Identified Offender Report and Recommendation that the facility must incorporate into the care plan, with a private room required for a registered sex offender (210 ILCS 45/2-201.6). Oklahoma's Long-Term Care Security Act (63 O.S. § 1-1946) and its rule (OAC 310:675-7-21) require a long-term care facility to determine, before admission or employment and no later than three business days after acceptance, whether an applicant, resident or employee is a registered offender, to post any law-enforcement notice conspicuously, and to notify the State Department of Health in writing. Minnesota (Minn. Stat. § 243.166 subd. 4b) puts the disclosure duty on the offender and the notice on law enforcement, whose fact sheet the facility must distribute to all residents when it carries a risk level. Virginia requires the facility to tell each resident they may wish to consult the registry and to assist on request (Va. Code § 32.1-138(A)(16)), and lets it ask the State Police for automatic notice when an offender registers nearby (Va. Code § 9.1-914).

Staff and contractors

Here the federal rules are explicit. 42 CFR 483.12(a)(3) says a facility must not employ or otherwise engage anyone found guilty by a court of abuse, neglect, exploitation, misappropriation or mistreatment, anyone with such a finding on the State nurse aide registry, or anyone whose professional licence is under disciplinary action for the same reasons; "or otherwise engage" is the hook that reaches contractors. 42 CFR 483.35(d)(4) requires registry verification before an individual serves as a nurse aide. Section 6201 of the Affordable Care Act (42 U.S.C. § 1320a-7l) funded the National Background Check Program for "direct patient access employees" — a definition that covers contract staff with one-on-one resident contact — in 29 states at various times between 2010 and 2024. State statutes carry the day-to-day requirements: Illinois' Health Care Worker Background Check Act (225 ILCS 46), Florida's Level 2 screening through the Care Provider Background Screening Clearinghouse (Fla. Stat. §§ 408.809, 435.04, 435.12; rescreen every five years), Texas' Employee Misconduct Registry and nurse aide registry checks before employment with no direct consumer contact while a check is pending (Tex. Health & Safety Code § 250.003).

Exclusion lists are a separate obligation with a separate cadence. Under 42 CFR 1001.1901, no federal health care program pays for items or services furnished by an excluded individual or entity when the provider knew or should have known. The OIG's Special Advisory Bulletin on the Effect of Exclusion (May 2013) notes that providers are not required by statute to check the List of Excluded Individuals/Entities (LEIE), that the list is updated monthly, and that screening employees and contractors each month "best minimizes" overpayment and penalty exposure; CMS' State Medicaid Director Letter 09-001 recommended that states require providers to screen monthly, and many facilities also check SAM.gov (2 CFR 180.510) and their state Medicaid exclusion list. In practice this is a monthly batch run by compliance or HR against the whole roster, including agency nurses, with hits routed to a named reviewer.

Visitors

42 CFR 483.10(f)(4) gives every resident the right to receive visitors of their choosing at the time of their choosing, subject to the resident's consent and to "reasonable clinical and safety restrictions" set out in written visitation policies (483.10(f)(4)(v)). No federal rule requires a visitor background check, and most facilities do not run one on every visitor. Where a facility does screen — visitors to a resident with a guardian, overnight visitors, a name staff recognise — it is a front-desk name check with a supervisor path, written into that policy.

What the rules require is counsel's call. The statutes above are named so you can find them, not applied to your facility. Which apply, what they require of a given admission, hire or visit, and how a positive result may be used are decisions for your compliance officer and counsel. The API returns public records with their provenance; it does not make a facility compliant with any of them.

Where the API fits

Workflow stepEndpointFields that matter
Referral review — registry check on the prospective residentPOST /v1/search (all 58 US registries; never scope to one state — registrants move)query.firstName, query.lastName, query.dob; subjectId = your resident id, clientId = facility; read status, records[].matchState, matchBasis, registrationState, addressStates, sources[].recordUrl
Referral review — criminal records in the states the person lived inPOST /v1/criminal/search, cached (no live block), one call per statequery.lastName (required), firstName, dob, state; read records[].recordCategory, cases[].charges[], currentStatus, normalized; counts.sourcesPending
State-mandated check after a positive registry result (IL, OK)Same searchId, plus POST /v1/report and, where the registry's own page is wanted, live.evidence: "pdf"searchId; evidence[].downloadUrl; X-Report-Id
Monthly exclusion run on staff and agency nursesPOST /v1/criminal/search with include: ["exclusions"]; POST /v1/criminal/batch for the whole rosterrecordType: "healthcare_exclusion", exclusion.program (medicare_medicaid, federal_procurement, state_medicaid), exclusion.authority, convictionBased, excludedOn, reinstatedOn; counts.exclusionsIncluded
Visitor at the deskPOST /v1/search with age when no date of birth is collectedquery.age; subjectId = visitor id; status (partial → "could not complete", never "clear")
Reviewer decides a candidate is someone elsePOST /v1/clearancesproduct: "sex_offender" or "criminal", subjectId, clientId, personRef, searchId, verdict: "not_this_person", reviewer, note
Census and roster stay watchedPOST /v1/monitors — one per dataset, product + type: "person"person.firstName/lastName/dob, minConfidence, label, channels.webhookUrl or emails[]
Discharge, terminationDELETE /v1/monitors/{monitorId}Evaluation stops now; not charged on any invoice prepared after the cancel

Never send a Medicaid id, MRN or SSN: the APIs match on name, date of birth and place only, and refuse unknown keys with 422. The referral's identifiers stay on your side of the mapping, keyed by the subjectId you choose.

Reference integration

One module, three functions, one outcome vocabulary. registry_check serves admissions and the front desk; criminal_check serves admissions; exclusion_check serves the monthly staff run. Every call carries subjectId and clientId, so a reviewer's clearance follows the person. Run it as written against the sandbox with an os_test_ key; the test names and expected outcomes are in the comments.

"""Skilled nursing screening — OffenderSearch reference integration (Python 3.11+, requests).

Three jobs one facility runs, in one module:
  registry_check    resident admission or visitor — Sex Offender Search, all 58 US registries
  criminal_check    resident admission — Criminal Records, cached search
  exclusion_check   staff and contractors — the same criminal search with include: ["exclusions"]

Runs against the sandbox with its fixed test names. Swap the host and key for production.
  OFFENDERSEARCH_API_KEY   os_test_… (sandbox) or os_live_… (production)
  OFFENDERSEARCH_BASE      https://sandbox.offendersearch.app (default here)
  FACILITY_ID              your id for this building — sent as clientId so clearances stay per facility
"""
from __future__ import annotations

import os
import time
from dataclasses import dataclass, field

import requests

API_KEY = os.environ["OFFENDERSEARCH_API_KEY"]
BASE = os.environ.get("OFFENDERSEARCH_BASE", "https://sandbox.offendersearch.app")
FACILITY_ID = os.environ.get("FACILITY_ID", "facility-07")
HEADERS = {"X-API-Key": API_KEY, "Content-Type": "application/json"}

The outcome logic

OutcomeRuleWhat the reviewer sees
clearrecords empty and status: "complete" (criminal: and counts.sourcesPending is 0)The admission or the monthly run proceeds on its own merits. The searchId is filed with the referral or the roster run.
reviewRecords exist, none with matchState: "dob_match" — the registry or court published no birth date (no_dob_age_year, unverified: true), or only the year or an age agreedA card per candidate: name.full, aliases, dob/age, matchBasis (why it matched), matchedName.type (legal or alias), registrationState, addressStates, offense.crime, the photo, and sources[].recordUrl to open the registry's own page. For criminal records: recordCategory, cases[].charges[] with severity and disposition, currentStatus.
matchAt least one record with matchState: "dob_match"The same card, flagged. Still a candidate — a person confirms identity against the photo and details, then facility policy decides. Never auto-decline on the API answer.
incompleterecords empty but status: "partial" (a registry did not finish: sourceStatus[].incomplete, incompleteReason) or counts.sourcesPending > 0"Could not complete" with the registries named. Retry, or re-check those registries with live.jurisdictions. Never record it as clear.
refused / failed422 (read detail on the registry search, error.code + error.message on the criminal search), 402 (billing), other non-2xxFix the request or the account; nothing was searched and nothing billed on 429 or 5xx.
Note. A candidate's matchConfidence is a ceiling on the evidence held, not a probability that this is the resident. Tier the queue on matchState and matchDetail.strategies, explain the match with matchBasis, and let a person decide. The results guide and false positives cover the edge cases.

Ongoing monitoring

An admission check is a snapshot. A resident may be added to a registry after admission; a licensed nurse may be excluded mid-year; a registrant may move into the ZIP code a Virginia facility asked to be notified about. Monitoring turns the census into standing watches: one monitor per person per dataset (product: "sex-offender" or "criminal", type: "person"), evaluated once a day, alerting by email or webhook with eventType new, changed or removed. A criminal person monitor also alerts on exclusion-list records: the alert's detail.recordType is healthcare_exclusion and detail.exclusion carries the list's own facts.

Create a person monitor for an admitted resident
curl -X POST https://sandbox.offendersearch.app/v1/monitors \
  -H "X-API-Key: $OFFENDERSEARCH_API_KEY" -H "Content-Type: application/json" \
  -d '{
    "product": "sex-offender",
    "type": "person",
    "person": { "firstName": "Exact", "lastName": "Match", "dob": "1980-01-01" },
    "minConfidence": "year_match",
    "label": "resident-48213 · unit 2B",
    "channels": { "webhookUrl": "https://your-app.example.com/hooks/offendersearch?token=REPLACE_WITH_A_SECRET" }
  }'
201 Created
{
  "id": "mon_7f2a1c9e0b4d",
  "product": "sex-offender",
  "type": "person",
  "minConfidence": "year_match",
  "label": "resident-48213 · unit 2B",
  "status": "active",
  "channels": { "emails": [], "webhookUrl": "https://your-app.example.com/hooks/offendersearch?token=REPLACE_WITH_A_SECRET" },
  "createdAt": "2026-10-25T14:02:11+00:00",
  "canceledAt": null,
  "person": { "firstName": "Exact", "lastName": "Match", "dob": "1980-01-01" },
  "recentAlerts": [],
  "sandbox": true
}
  • Create the two monitors when the admission is confirmed, and the criminal monitor for each staff member when they are added to the roster; cancel on discharge or termination with DELETE /v1/monitors/{monitorId}.
  • Put the resident or employee id in label so the alert routes itself; the alert never carries your id otherwise.
  • Every delivery is signed with the monitor's webhookSecret (X-Offendersearch-Signature: t=…,v1=… over <t>.<raw body>): verify it, then answer 2xx quickly. A first evaluation reports every current match as new, so expect alerts immediately for anyone who already has a record.
  • A location monitor (type: "location", location.address or lat/lng + radiusMiles) around the building is the API's version of the ZIP-code notice some states offer facilities.

Details: person monitoring, alerts, delivery, and the webhooks and lifecycle guide.

When this is FCRA

Resident admission and visitor screening are facility-safety processes governed by your state's rules and your policy; they are not consumer reports, and every self-service response carries a legal.notice saying so. Staff are different. Deciding whether to hire, keep or assign an employee or an agency nurse is an employment purpose under the Fair Credit Reporting Act, and a report procured from a third party for that purpose is a consumer report. That path is the FCRA Partner API: an end user with a certified permissible purpose (employment), a consent attestation on every order, the subject's name, date of birth and addresses, and a criminal search whose exclusions mode (conviction_only by default, all, none) returns the same healthcare_exclusion records inside a regulated report with disputes, a consumer portal and signed webhooks. It is available to approved partners; other accounts receive 403 fcra_not_enabled.

Warning. The exclusion_check function above shows the data and its fields. Whether a particular exclusion-list screen of your workforce is a consumer-report use is a question for your counsel; where it is, run it as the criminal search of an FCRA order, not through the self-service endpoint.

Evidence and audit

A surveyor or an Illinois Department of Public Health reviewer will ask what was checked, when, and what it showed. Keep four things with every referral, roster run and desk check: the searchId, the status with counts and sourceStatus[], the outcome your software derived, and the reviewer's decision with their name and note. Searches stay in the account's history for 14 days; GET /v1/searches/{searchId}/results re-reads one in that window, and the dashboard's history lists both products.

Verification report for the chart
curl -X POST https://sandbox.offendersearch.app/v1/report \
  -H "X-API-Key: $OFFENDERSEARCH_API_KEY" -H "Content-Type: application/json" \
  -d '{ "searchId": "srch_9f2c1a7b", "viewerName": "Admissions desk" }' \
  --output admission-48213.pdf      # application/pdf; the report id is in the X-Report-Id header
  • Verification report. POST /v1/report with the searchId renders a timestamped PDF of every matching record and every field, with a source citation on each record and the legal notice printed; metered at +$0.02. The criminal twin is POST /v1/criminal/report. Render it at decision time, not weeks later. Reports.
  • The registry's own page. Where a state process wants the registry record itself, send live: { "jurisdictions": ["OH"], "evidence": "pdf" } on the search: it answers 202 with a pollUrl; the finished job carries evidence[] with a downloadUrl per registry, readable for 30 days, 25¢ per document. Live evidence.
  • Clearances as audit trail. A not_this_person verdict must cite the searchId that showed the person, carries reviewer and note, and stays readable after revocation (includeRevoked=true). It is the record of who decided what about a look-alike. Clearances.
Record a reviewer's verdict
curl -X POST https://sandbox.offendersearch.app/v1/clearances \
  -H "X-API-Key: $OFFENDERSEARCH_API_KEY" -H "Content-Type: application/json" \
  -d '{
    "product": "sex_offender",
    "subjectId": "resident-48214",
    "clientId": "facility-07",
    "personRef": "prs_3c9e1f0a7b2d4e6f8a1b",
    "recordId": "rec_6d066763131ebe8c77ea",
    "searchId": "srch_9f2c1a7b",
    "verdict": "not_this_person",
    "reviewer": "admissions-director",
    "note": "Registry photo and address do not match the resident; age differs by 14 years."
  }'

Referral data is protected health information: call the API from your backend over TLS, send only name, date of birth and place, and execute a Business Associate Agreement from Dashboard → BAA where your counsel requires one.

Rollout plan

  1. Sandbox first. Create an account, mint an os_test_ key under Dashboard → API keys and run the module above against https://sandbox.offendersearch.app. Confirm each test name lands in the expected outcome, including the review states — those are the ones your reviewer screen has to handle.
  2. Map the referral. Decide which packet fields become firstName, lastName, dob and the states for the criminal calls; decide the subjectId scheme (your resident id, never the MRN) and the clientId per building. Write down what "clear" means in your admissions policy with counsel.
  3. Build the reviewer screen. Show the candidate card from the table above, the registry's own page via sources[].recordUrl, and two buttons: "this person" and "not this person". Wire the second to POST /v1/clearances.
  4. Pilot one building. Switch that building's key to os_live_, keep the same code, and run live referrals alongside the current manual process for a few weeks. Compare outcomes, measure review volume, tune nothing in the API — tune the policy and the screen.
  5. Add the monthly exclusion run. Export the roster, run exclusion_check per person (or POST /v1/criminal/batch for the list), route every review/match to compliance, and file the searchIds with the run. Decide with counsel which workforce checks belong on the FCRA Partner API.
  6. Turn on monitoring, then roll out. Create the person monitors for the current census and roster, verify the webhook receives and acknowledges alerts, then add the remaining buildings — one clientId each, the same key or one per building where you need separate revocation.