Front-desk visitor screening
A registry check at visitor sign-in: what to send from the ID, the Clear, Review and Match states, the staff screen, repeat visitors and the audit trail.
Livehttps://api.offendersearch.appSandboxhttps://sandbox.offendersearch.appFront-desk visitor screening: what to send from the ID
This guide is for teams building a sex offender registry check into a front-desk sign-in: a visitor management product, a school platform, or a district’s own app. It covers the request, how to turn the answer into one of three states a desk can act on, what staff should see, and what to keep. The product side of the same workflow is on the visitor screening API and school visitor screening API pages.
Send three fields from the visitor’s ID: firstName, lastName and dob as YYYY-MM-DD. A driver’s licence barcode carries all three. Add your own ids for the visitor (subjectId) and for the school or district (clientId); the sections on repeat visitors and platforms explain why.
curl -X POST https://api.offendersearch.app/v1/search \
-H "X-API-Key: $OFFENDERSEARCH_KEY" \
-H "Content-Type: application/json" \
-d '{
"query": { "firstName": "James", "lastName": "Smith", "dob": "1984-03-09" },
"subjectId": "visitor-1042",
"clientId": "district-17/school-3"
}'Every field is documented on Search. Omit jurisdictions: the default search covers 58 registries, so a visitor registered in another state is found at the desk as readily as a local one.
Why date of birth matters in visitor screening
A name alone is not an identity. A common name matches many registrants, and none of them can be told apart from the visitor without more information. With the date of birth, every record whose full date of birth disagrees with the ID is left out, and the records that remain say how their date of birth was checked in matchState.
{ "query": { "firstName": "James", "lastName": "Smith" } }
→ every registrant named James Smith, across the 58 registries
"counts": { "records": 40, … } (illustrative)
each record: "matchState": null — nothing was checked{ "query": { "firstName": "James", "lastName": "Smith", "dob": "1984-03-09" } }
→ registrants whose date of birth disagrees are left out
"counts": { "records": 1, … }
"matchState": "dob_match" — the date of birth matchedWith a date of birth, a common name usually returns zero or one person. A few registries publish only a birth year or an age, or no birth information at all; a record from one of them stays in the answer, labelled year_match, age_match or no_dob_age_year, because it cannot be ruled out. Those are the records your desk reviews. The full vocabulary is on Date of birth.
Reading a visitor screening result: Clear, Review, Match
Map every response to one of three states. Take the strongest state any record reaches: one Match record makes the whole check a Match.
| State | Rule | What it means at the desk |
|---|---|---|
| Clear | status is "complete" and counts.records is 0. | No registrant matches the visitor. Continue the sign-in and print the badge. |
| Review | At least one record, and none meets the Match rule. This covers year_match, age_match, no_dob_age_year, a dob_match below 0.90, and every record from a search sent without a dob (matchState: null). | A possible match. A staff member compares the photo and details with the ID before the visitor goes further. |
| Match | At least one record has matchState: "dob_match" and matchConfidence of 0.90 or higher. | A strong match: the visitor’s full date of birth matches a registrant with the same name. Alert an administrator quietly and follow the site’s protocol. |
Why the Match rule needs both fields
matchConfidence measures how much identity evidence the record carries: a full date of birth and both names on file score 0.90, and 1.00 when more than one registry holds the person. A dob_match record is never capped, so it scores 0.90 or higher whenever both names are on file. But an exact-name record with a date of birth on file also scores 0.90 when you sent no dob, and nothing about the visitor was checked. Only matchState says the visitor’s date of birth was compared, so the rule reads both.
| matchState on the record | matchConfidence | Desk state |
|---|---|---|
| dob_match, first and last name on file | 0.90 – 1.00 | Match |
| dob_match, a name missing from the record | 0.70 – 0.80 | Review |
| year_match (the registry publishes only a birth year) | up to 0.85 | Review |
| age_match (the registry publishes only an age) | up to 0.70 | Review |
| no_dob_age_year (no birth information on the record) | up to 0.70 | Review |
| null — the search was sent without a dob | up to 1.00 | Review |
Visitor screening desk UI: what staff see
POST /v1/search with subjectId and clientId.- Print the badge.
- Log the searchId, time and state.
- Visitor waits; staff see the candidates.
- Compare photo, date of birth and details with the ID.
- Not this person: record a clearance, print the badge.
- Cannot rule it out: escalate to an administrator.
- Visitor waits; no badge prints.
- Quiet alert to the designated administrator, with the photo.
- Administrator confirms against the ID and follows the written protocol.
The visitor sees nothing
Never show a result on a screen the visitor or the line can see. For Clear the kiosk prints a badge; for Review and Match it shows the same neutral message, such as “The front office will finish your sign-in”, and alerts staff on their own screen or device.
What the staff screen shows for each candidate
- The photo,
images[0].url, beside the photo on the ID. - Name,
dob(orbirthYearoragewhere that is all the registry publishes), sex, height and weight. - Where the person is registered,
registrationState, and their addresses. - How the record matched, in plain words: “Date of birth matches the ID” for
dob_match, “Only the birth year could be compared” foryear_match, “The registry lists no date of birth” forno_dob_age_year, and so on. - A link to the registry’s own page for the record,
source.recordUrl.
Handling a Review
- Compare the photo and the date of birth (or birth year, or age) with the ID. Check height, weight and other details the registry lists.
- If they plainly differ, the staff member records that the candidate is not this visitor. Your app calls
POST /v1/clearanceswithverdict: "not_this_person", the staff member asreviewerand a shortnote, and the sign-in continues. - If the staff member cannot rule the candidate out, escalate to an administrator, who decides under the site’s written protocol. Do not deny entry on a Review alone.
Handling a Match
No badge prints. The designated administrator gets a quiet alert with the photo and the match details, confirms the person against the ID, and follows the district’s written protocol. State laws differ on when a registrant may be on school grounds, so the response to a confirmed match belongs to district policy and counsel, not to your app.
Repeat visitors: clearances for visitor screening
A parent who visits every week and shares a name with a registrant should be reviewed once, not every week. Send your stable id for the visitor as subjectId on every search. When a reviewer decides a returned person is not that visitor, record it with POST /v1/clearances. From then on, a search with the same subjectId and clientId still runs in full, but the cleared person is removed from records[] and listed in cleared[], and counts.clearedSuppressed says how many were removed. The visitor comes back Clear.
- Exact person only. A clearance names one
personRef, never a name or a date of birth, so a different registrant with the same name still comes through. - Changes bring them back. If the cleared person’s name, date of birth, offenses or registration status change, or a later search matches them more strongly, they return to
records[]withclearance.stale: truefor a new review. clearanceModeis"suppress"by default. Send"annotate"to keep cleared people inrecords[]with aclearanceobject, for a staff screen that shows them greyed out, or"ignore"to answer as if no clearance existed.- Proof of review. The clearance cites the
searchIdthat returned the person, which must be in your 14-day search history. - No extra charge, no expiry until revoked, and
DELETE /v1/clearances/{clearanceId}revokes one.
The full flow, every field and the re-surfacing rules are on Clearances.
Visitor screening for platforms serving many schools and districts
A platform that runs sign-in for many schools uses one OffenderSearch account and sends the school or district with each search:
- One account, one key per environment. Every key on the account can search; create a separate key for each environment or service so any one can be rotated or revoked on its own (see Authentication).
clientIdper school or district. Send your own id for the school or district, up to 128 printable characters, on every search and every clearance. Clearances are scoped to it: one district’s decisions never change another district’s results, even for the same registrant and the same visitor id. It takes effect alongside asubjectId.subjectIdunique across your platform. Use the visitor id your platform already has. Both ids are stored only as per-account hashes, never as the values you sent.- Usage per school or district. Usage and the invoice are per account, in the dashboard. To report or bill each district, count the
searchIds you stored against eachclientIdin your audit log (below).
Pre-registered visitors, such as a field-trip chaperone list, can be screened before the day with Batch & CSV, which takes the same subjectId, clientId and clearanceMode per row.
Visitor screening audit trail and data retention
Store one row per check, Clear ones included:
| Field | Why |
|---|---|
| searchId | Identifies the search. It is what a clearance and a verification report cite. |
| Timestamp | When the check ran, from your own clock at sign-in. |
| Desk state | Clear, Review or Match, as your app computed it. |
| clientId, subjectId | Which school and which visitor, for reporting and for the next visit. |
| Review outcome | For Review and Match: the personRef and recordId of each candidate, who reviewed it, the decision, and the clearance id if one was recorded. |
We keep each search and its results for 14 days. In that window it appears in the dashboard’s search history, a clearance can cite it, and POST /v1/report can turn it into a timestamped PDF for your file. After 14 days the stored results are erased, so keep in your own records whatever your retention policy requires.
Visitor screening speed and registry coverage
A visitor is waiting at the counter, so the check has to finish there. A search is designed to be sub-second, and every response carries elapsedMs so you can measure it in your own flow. Each search covers 58 registries: the 50 states, DC and the US territories. The date of birth narrows the answer to the right person, which also keeps the staff screen short.
Visitor screening and the FCRA
A visitor check decides who is escorted today. Results from POST /v1/search are not a consumer report and may not be used for FCRA-covered decisions. When a result will help decide whether to hire or engage someone, such as a substitute teacher, a coach or a contracted caregiver, that check is a consumer report and runs as an FCRA order through FCRA Partner Access. See the FCRA API.
Visitor screening integration code in Node and Python
A complete minimal integration: search at sign-in, map the answer to Clear, Review or Match, and record a reviewer’s decision as a clearance. It points at the sandbox; set OFFENDERSEARCH_BASE and OFFENDERSEARCH_KEY to go live.
// desk-screening.mjs — Node 18+ (fetch is built in)
const BASE = process.env.OFFENDERSEARCH_BASE ?? 'https://sandbox.offendersearch.app';
const KEY = process.env.OFFENDERSEARCH_KEY;
// A Match is a record whose full date of birth matched the visitor's ID and
// whose first and last name are both on file: matchState "dob_match" scores 0.90+.
const MATCH_MIN_CONFIDENCE = 0.90;
async function call(path, body) {
const res = await fetch(BASE + path, {
method: 'POST',
headers: { 'X-API-Key': KEY, 'Content-Type': 'application/json' },
body: JSON.stringify(body),
});
return res.json();
}
/** Clear | Review | Match for one search response. */
export function deskState(result) {
if (result.status === 'complete' && result.counts.records === 0) return 'clear';
const strong = result.records.some(
(r) => r.matchState === 'dob_match' && r.matchConfidence >= MATCH_MIN_CONFIDENCE,
);
return strong ? 'match' : 'review';Test visitor screening in the API sandbox
Run each of these against https://sandbox.offendersearch.app with a test key and check that your app reaches the desk state shown. Nothing on the sandbox is billed. Test keys, the criminal test names and examples are on Sandbox & test keys.
| Send | What comes back | Desk state |
|---|---|---|
| firstName: Clear · lastName: Visitor | Clear visitor | Clear |
| firstName: Exact · lastName: Match · dob: 1980-01-01 | Exact match, date of birth verified | Match |
| firstName: Exact · lastName: Match | Exact match, no date of birth sent | Review |
| firstName: Exact · lastName: Match · dob: 1990-05-05 | Exact match name, different date of birth | Clear |
| firstName: Name · lastName: Only | Name only — possible matches | Review |
| firstName: Alias · lastName: Match | Matched through an alias | Review |
| firstName: Out · lastName: State · state: TX | Registered in another state | Review |
| firstName: Multi · lastName: State | One person on two registries | Review |
| firstName: Homeless · lastName: Registrant | Homeless registrant | Review |
Related products
Product overview
- Sex offender search API
- Sex offender registry API
- Offender search API
- Background check API
- Bulk sex offender search
- Background check API pricing
- Background check API integration
- Integrations
- Free background check API
- Pay-as-you-go background check API
- Background check API in Python
- Background check API in Node.js
- NSOPW API