Reentry Resource Extractor

Turns one public reentry-program web page into a structured draft listing. The model is instructed to use only the page’s text, the server keeps a value only when its quoted evidence appears in that text, and a person reviews every field.

Built for managers of transitional housing, halfway houses, and reentry programs who need complete and correct listings assembled from information that already sits on their public websites.

My role
I defined the problem and application workflow, guided AI-assisted implementation, reviewed the results, and refined the interface and human-verification process.
Built with
JavaScript with Vite, a Node.js server, cheerio, and the OpenAI Responses API, deployed on Railway.
Status
Deployed on Railway. Analyzes one page at a time; reviewed values aren’t saved.

Scattered details, real consequences.

Reentry program listings are hard to get right. The details that matter most to someone leaving incarceration (eligibility, cost, length of stay, substance-use and medication policies, how to apply) are usually scattered across a program’s website instead of gathered in one place. Directory listings end up incomplete, inconsistent, or out of date.

The obvious shortcut, asking an AI model to summarize the site, creates a worse problem: invented or misread details presented as fact. For this audience, a wrong policy or phone number has real consequences, so that is the failure the whole design works against. The tool handles the tedious part, finding and organizing the details, and leaves the person accountable for them in charge.

Retrieve, extract, then review.

  1. Retrieve

    The manager enters the address of one public web page. The server fetches it, refuses private and internal addresses, limits the page size and redirects, and reduces the HTML to readable text, which the manager sees before anything else happens.

  2. Extract

    That text goes to the OpenAI Responses API with a strict JSON schema covering 21 listing fields, from program name and contact details to eligibility, cost, length of stay, and how to apply. The model is told to use only that text, to treat it as content rather than commands, and not to guess. Anything it reports as missing, and any value whose evidence quote can’t be found in the page, is shown as Not Found.

  3. Review

    Each field shows the AI result, the quoted evidence, and a separate manager-reviewed value. The manager confirms, edits, or clears each one, and its status moves from Unreviewed to Confirmed or Edited, so it’s always clear what a person has checked.

Start screen of the Reentry Resource Extractor: the title, a short description, and a three-step pathway labelled Retrieve, Extract, and Review, with the form for a program website address beginning below.
The start screen of the deployed application: the three-step pathway and the form that takes a public web address.

Built to check the AI, not to trust it.

Evidence has to match the page

The server accepts a value as found only when the model’s evidence quote appears word for word (ignoring spacing) in the retrieved text; otherwise the field becomes Not Found. This rejects evidence quotes that do not appear in the retrieved text. A matching quote can still accompany an incorrect interpretation, so every field requires human review.

A strict schema instead of free text

Every response must fit a fixed JSON schema with the same three parts for each field: the value, whether it was found, and the source evidence. Incomplete, malformed, or refused responses are rejected with a plain message instead of being shown as results.

Page text is data, not instructions

Retrieved content reaches the model as source material, with instructions to ignore any commands inside it. That lowers the risk from prompt-injection text hidden in a page; it doesn’t eliminate it.

Only public pages, with fetch limits

Before fetching, the server resolves the hostname and blocks loopback, private, and link-local address ranges, to keep the tool from being pointed at internal systems. The page is capped at 5 MB, redirects at three, and the request times out after 8 seconds.

The AI result and the human value stay apart

The AI result and its evidence stay read-only, and each field gets its own manager-reviewed value with an explicit status. What the model said and what the person decided never blur together.

Secrets stay on the server

The OpenAI key lives only in the server’s environment and never reaches the browser. If it’s missing, the app says so plainly instead of failing silently.

The person has the last word.

This is where the design shows. Every field carries two badges: its review status (Unreviewed, Confirmed, or Edited) and whether the AI found it at all. Found values sit beside the quote the model cited, matched by the server in the page text; Not Found fields say so and show no evidence.

The screenshots below use the app’s built-in sample results, a feature for testing the interface without analyzing a website, so they show sample data rather than a real extraction.

The review interface on a desktop screen. Field cards for Program name and Website show badges reading Unreviewed and Found, the AI result, the quoted source evidence, an editable manager-reviewed value, and buttons to mark the value confirmed or clear it. A status message says the development sample results are for interface testing only.
Sample data The review interface on a wide screen, showing the application’s development sample results rather than a live extraction.
The review interface on a phone: the Extraction results heading, the note that AI-generated information is a draft, and the Program name field card with its badges, AI result, source evidence, and manager-reviewed value.
Sample data The same screen on a phone, again with sample data.

What it does, and doesn’t.

What is implemented

  • Retrieval of one public HTML page, with the readable text shown before extraction
  • AI extraction of 21 listing fields through a strict JSON schema, with evidence checked against the page
  • Per-field review with Unreviewed, Confirmed, and Edited states, keeping the AI result separate from the reviewed value
  • Clear messages for configuration, retrieval, size, timeout, refusal, and invalid-response failures
  • Development sample results for checking the interface without an AI call
  • Deployment on Railway

What it does not do

  • It analyzes one page at a time. It does not crawl a whole website or follow links.
  • Nothing is saved, exported, or published. Reviewed values live only in the current browser session.
  • There are no accounts, and the AI step needs an API key configured on the server.
  • Evidence matching only confirms that a quote appears in the retrieved text. It can’t tell whether the extracted value or its interpretation is correct; that remains the reviewer’s job.
  • Testing so far has been manual; there’s no automated test suite yet.

What I contributed.

I defined the problem and application workflow, guided AI-assisted implementation, reviewed the results, and refined the interface and human-verification process.

Built with

  • JavaScript and Vite A framework-free interface written in plain JavaScript and bundled with Vite.
  • Node.js A small HTTP server on Node’s built-in modules, handling retrieval, address checks, and two JSON endpoints.
  • cheerio Strips scripts, styles, and layout from the fetched HTML and keeps the readable text and link targets.
  • OpenAI Responses API Structured outputs against a strict JSON schema, through the official openai library.
  • Railway Production hosting, with the server bound to Railway’s port and serving the built interface.

Let’s talk it through.

Tell me what you need to organize or the workflow you want to simplify, and we’ll work out together whether a tool like this would help.

Start a conversation