Person monitoring
A person monitor follows an identity, not a listing. Get the criteria right and the alert stream stays quiet until something real happens.
What you’ll build
A watch on one named person in one dataset — the sex-offender registries or the criminal records the platform holds — that alerts when a matching record appears (new), when a fact about a matched person changes (changed), or when the person is no longer listed in that registry or source (removed). You will create the monitor with a confidence floor, read the alerts it files, and learn which field to key your own records on.
Typical operators: a compliance team watching a roster of caregivers, a housing platform watching admitted residents, a marketplace watching its providers. Each person on the list is one monitor per dataset.
Before you start
- A test key against
https://sandbox.offendersearch.app. Sandbox people for this guide: Exact Match (1980-01-01, onedob_matchalert inOH), Multi State (1972-03-14, two alerts —FLandGA), Name Only (threename_matchalerts), Felony Record (1975-06-15, a criminal monitor alert withexternalIdSBX-CR-2015-000123). - The
personobject:lastNameis required;firstNameanddob(YYYY-MM-DD) are optional but adobis what keeps the monitor precise. - Decide which dataset you are watching.
product: "sex-offender"andproduct: "criminal"are two monitors, even for the same person.
person.dob must match YYYY-MM-DD or the call is a 422. It is not checked as a calendar date, so 1988-13-45 is accepted and the monitor then runs as if no date had been sent. Validate the date yourself before you send it.Step by step
- Pick the floor.
minConfidenceis the weakest match the monitor alerts on:dob_match(full date agrees),year_match(birth year agrees — the default with adob),age_match(a published age is consistent),name_match(name alone — the default without adob).anymeans "the default". - Create the monitor.
POST /v1/monitorswithproduct,type: "person",person,minConfidenceandchannels. - Read the alerts.
GET /v1/monitors/{monitorId}/alerts. Each alert is one person in one registry;record.personRefis the key. - Store the key. Save
record.personRefwithrecord.jurisdictionagainst your own person record. Treatrecord.recordIdas a pointer to the listing at that moment, not as the person’s identity.
Create a person monitor with a dob_match floor
curl -X POST https://sandbox.offendersearch.app/v1/monitors \
-H "X-API-Key: $OFFENDERSEARCH_TEST_KEY" \
-H "Content-Type: application/json" \
-d '{
"product": "sex-offender",
"type": "person",
"person": { "firstName": "Multi", "lastName": "State", "dob": "1972-03-14" },
"minConfidence": "dob_match",
"label": "roster-00412",
"channels": { "email": "alerts@your-app.example.com" }
}'{
"alerts": [
{
"id": "hit_0d3e9b7c51a2f684",
"eventType": "new",
"matchLabel": "dob_match",
"record": {
"name": "MULTI STATE", "jurisdiction": "GA", "locality": "Sampleville",
"matchLabel": "dob_match", "matchConfidence": 1.0,
"personRef": "prs_9a1b2c3d4e5f60718293", "recordId": "rec_7f8e9d0c1b2a39485766"
},
"firstSeenAt": "2026-10-25T14:10:02.118930+00:00",
"occurredAt": "2026-10-25T14:10:02.118930+00:00",
"notifiedAt": null
},
{
"id": "hit_6c2a8f1e94d7b035",
"eventType": "new",
"matchLabel": "dob_match",
"record": {
"name": "MULTI STATE", "jurisdiction": "FL", "locality": "Sampleville",
"matchLabel": "dob_match", "matchConfidence": 1.0,
"personRef": "prs_4e5f60718293a1b2c3d4", "recordId": "rec_1b2a39485766f7e8d9c0"
},
"firstSeenAt": "2026-10-25T14:10:02.118930+00:00",
"occurredAt": "2026-10-25T14:10:02.118930+00:00",
"notifiedAt": null
}
],
"sandbox": true
}Two alerts, two ids, two personRefs: the person is listed in two registries, and each registry is its own thread. notifiedAt is null here because the sandbox sends no email and this monitor has no webhook — the alerts are still filed and readable.
A criminal person monitor
Same request with product: "criminal". The alert record then carries externalId (the record’s own id at its source, the same value the Criminal Records API returns), recordType (court_case, doc_inmate, jail_booking, healthcare_exclusion, …) and, for an exclusion-list record, an exclusion block; recordId is null.
{
"name": "FELONY RECORD",
"jurisdiction": "OH",
"locality": "Sampleville",
"matchLabel": "dob_match",
"matchConfidence": 0.98,
"personRef": "prs_6d0c2a9e4b1f7e3a8c5d",
"recordId": null,
"recordType": "court_case",
"externalId": "SBX-CR-2015-000123"
}Reading the result
| Field | Values | What it means for a person watch |
|---|---|---|
eventType | new | The person newly matches: they were not matching at the previous evaluation. Everyone who already matches is new on the first evaluation. |
eventType | changed | A fact about an already-matched person changed — an address, an offense field, a status, or on a criminal monitor a new booking. A registry republishing the listing under a new number is not a change. |
eventType | removed | The person is no longer listed in that registry or source. Reported once; matchLabel is null and record carries only event, jurisdiction and personRef. |
matchLabel | dob_match, year_match, age_match, name_match | Which strength actually fired. Under any floor you can tell a full-date confirmation from a year-only or age-only one. |
record.personRef | prs_… | The person within the publishing registry or source — the same value the matching record carries in search results. Stable across renumbering. Your reconciliation key. |
record.recordId | rec_… or null | Sex-offender monitors only: the matching record at the time of the event, for GET /v1/records/{recordId}. null on a criminal monitor. |
record.externalId | source id or null | Criminal monitors: the record’s id at its source. null for registry records. |
Edge cases and pitfalls
- Common names without a `dob` flood the stream: every same-named record alerts at
name_match. Supply adob; if you truly cannot, plan to triage the volume. - A contradicting `dob` is silent — the monitor is created and never alerts on that person. Check the
dobyou hold before blaming coverage. - `minConfidence: "dob_match"` hides year-only records. A registry that publishes only a birth year can never reach
dob_match; useyear_match(the default) if that matters to you. - One person, several registries is several alerts with different
personRefs. Reconcile on (jurisdiction,personRef), not on the name. - A renumbered listing is not an event. No
new, nochanged, noremoved; the same alertidandpersonRefcontinue. Do not key onrecordId. - Exclusion lists on a criminal monitor: only conviction-based exclusions alert, and list entries that publish no date of birth (most SAM.gov and state entries) never alert, because a person monitor is keyed on identity.
- `minConfidence: "any"` is stored and echoed but behaves as "unset"; an unknown value is a
422.
Production checklist
- Validate
dobas a real calendar date before sending; always send it when you have it. - Create one monitor per dataset you need —
sex-offender,criminal, or both — and store eachmon_id. - Persist
record.personRef+record.jurisdictionper alert; look uprecordIdonly to show the listing. - Route
removedevents to a human: disappearance is data, and the alert record is deliberately sparse. - Alerts are not a consumer report and may not be used for employment, tenant, credit or insurance decisions.