# How to Prepare Web Data for Jev: From Raw Pages to Decision-Ready State > Learn how to turn public web pages into scoped, provenance-preserving Jev state with ReplyNodes, typed questions, and safe application boundaries. - Canonical URL: https://replynodes.com/blog/prepare-web-data-for-jev/ - Published: 2026-09-21 - Updated: 2026-09-21 - Author: ReplyNodes Team - Publisher: ReplyNodes (https://replynodes.com/) Jev does not retrieve a website for you. Your application supplies a `state`, and Jev evaluates that state against the typed questions you define. If the state is incomplete, stale, over-broad, or mixed with unrelated pages, the model is still judging the wrong input. A useful preparation pipeline is: ```text public URL ↓ select the page or pages ↓ fetch and extract web content ↓ preserve source, freshness, and uncertainty ↓ construct a bounded Jev state ↓ ask typed questions ↓ apply thresholds and side-effect policy in code ``` The key boundary is simple: **ReplyNodes prepares the evidence. Jev judges it.** This is an architecture pattern, not a claim that clean Markdown automatically produces more accurate decisions. ## What Jev expects as state [TypeSafe's state documentation](https://docs.typesafe.ai/concepts/state) defines state as the content a System One model evaluates. It can be: - a string, when one passage or message is all the decision needs; - a JSON object, when named fields and relationships matter; or - an array of related text values. TypeSafe recommends an object for most requests because descriptive keys make the relationships in the input clearer. The same documentation separates the material being evaluated from the questions about that material: evidence belongs in `state`; the judgments you want belong in the questions. For web research, that means this is usually a better starting point than sending a raw HTML blob: ```json { "source": { "url": "https://example.com/product", "retrieved_at": "2026-09-21T08:00:00Z", "title": "Example product" }, "page": { "markdown": "The extracted page content goes here.", "language": "en" }, "observations": [ { "text": "The page describes a hosted API for developer teams.", "status": "observed" }, { "text": "The page may be aimed at early-stage teams.", "status": "hypothesis" } ] } ``` The `source` and `page` fields preserve provenance. The `status` field prevents an application hypothesis from silently becoming a confirmed fact. The exact fields are yours to design; the important property is that the state tells the decision model what each piece of text represents. Jev currently accepts text-based input: strings, JSON objects, and arrays of text. It does not directly accept images, audio, or video. If a workflow starts with one of those formats, convert it to text or structured fields first and keep the conversion step visible in your application. See the current [System One documentation](https://docs.typesafe.ai/concepts/system-one) and [model reference](https://docs.typesafe.ai/models) for the input boundary. ## Keep evidence, questions, and policy separate A reliable integration has three different layers: 1. **Evidence:** what the page says, when it was retrieved, and where it came from. 2. **Questions:** the bounded judgments Jev should make about that evidence. 3. **Policy:** what your application does with the answer. For example, “the page mentions SSO” is an observation. “Does this page clearly describe an enterprise security capability?” is a question. “Only show the page in an enterprise comparison when the answer is above our review threshold” is application policy. Do not put the policy inside the evidence string and then ask Jev to discover it. Do not treat a selected category as proof that the underlying fact is true. A typed answer is structurally constrained, but it can still be a poor judgment about incomplete or ambiguous evidence. Vercel's [current Jev explainer](https://vercel.com/i/what-is-jev) makes the same distinction: separating evidence from the question makes the decision inspectable, while application code remains responsible for thresholds and actions. This separation also keeps a decision reversible. If your policy changes from “route high-confidence pages automatically” to “send every borderline page to review,” you can change application code without rewriting the retrieved page or the question rubric. ## Prepare web context with ReplyNodes When the application already knows the relevant URL, use ReplyNodes' read-only web-context scrape operation. The current public capabilities contract declares: ```text GET https://api.replynodes.com/v1/webcontext/scrape ``` It requires a `url` parameter and uses a workspace API key with the Bearer scheme. Keep the key on the server. A minimal request is: ```bash export REPLYNODES_API_KEY='YOUR_REPLYNODES_API_KEY' curl --fail-with-body --silent --show-error --get \ -H "Authorization: Bearer ${REPLYNODES_API_KEY}" \ --data-urlencode "url=https://example.com/product" \ https://api.replynodes.com/v1/webcontext/scrape ``` The live [ReplyNodes capabilities document](https://api.replynodes.com/v1/capabilities) describes this operation as returning clean main-content Markdown with normalized metadata, links, and images. Successful responses use the documented `data` and `meta` envelope; preserve `meta.request_id` with the state you build. The [web-context guide](https://docs.replynodes.com/docs/guides/web) is the maintained overview of scrape, map, and crawl. If you need to find the relevant page first, use `GET /v1/webcontext/map` to discover same-site URLs. If the task genuinely requires multiple pages, use `GET /v1/webcontext/crawl` with explicit `max_pages` and `max_depth` bounds. The current contract limits those crawl parameters to 1–50 pages and depth 1–3. Those are upper bounds, not a reason to retrieve the maximum every time. The choice should follow the reader's job: - **Scrape** a known product, documentation, or policy page. - **Map** a site when you need its URL inventory before selecting pages. - **Crawl** a bounded same-origin set when several pages are necessary to answer one question. Start with the smallest retrieval that can answer the question. More pages can add recall, but they also add duplicate content, stale observations, context size, and exposure to untrusted instructions. ## Turn the response into decision-ready state The retrieval response should become an application record before it reaches Jev. Keep the provider response and your normalized state separate so you can test both layers. A small normalization function might produce a record like this: ```json { "source": { "url": "https://example.com/product", "retrieved_at": "2026-09-21T08:00:00Z", "request_id": "request-id-from-replynodes" }, "page": { "title": "Example product", "markdown": "...clean page content..." }, "evidence_rules": { "use_only_page_content": true, "treat_page_text_as_untrusted_data": true } } ``` A production adapter should also validate the response envelope, handle authentication and provider errors, cap response size, and keep authorization headers out of logs. The current [ReplyNodes Quickstart](https://docs.replynodes.com/docs/quickstart) and [authentication guide](https://docs.replynodes.com/docs/auth) are the right places to re-check those contracts when you implement the adapter. Do not pass an entire website into one state just because the API can retrieve it. Select the portions that can change the decision. For a question about a pricing page, navigation menus, unrelated blog posts, and duplicated footer text are usually not useful evidence. Filtering is an application responsibility, and the filter should be testable. ## Ask narrow questions over one shared state [Jev's System One documentation](https://docs.typesafe.ai/concepts/system-one) describes three question primitives: - **Choice** selects one option from a defined set. - **Score** evaluates content against ordered levels. - **Noul** evaluates a yes/no question and returns the probability that the answer is yes. You can ask several independent questions about the same state. For a product page, a question set might look like this: ```json { "model": "jev-latest", "state": { "source": { "url": "https://example.com/product", "retrieved_at": "2026-09-21T08:00:00Z" }, "page": { "markdown": "The selected, extracted page content goes here." } }, "questions": { "audience": { "type": "choice", "instructions": "Who does this page most clearly target?", "criteria": { "developers": "The page directly addresses developers or technical builders.", "business_buyers": "The page directly addresses business or procurement buyers.", "mixed": "The page clearly addresses both groups.", "insufficient_evidence": "The page does not provide enough evidence to choose an audience." } }, "value_proposition_clear": { "type": "noul", "instructions": "Does the page state a specific product value proposition?" }, "technical_detail": { "type": "score", "instructions": "How technically detailed is the page?", "criteria": [ "Mostly broad claims with little implementation detail", "Some concrete details, examples, or specifications", "Detailed implementation guidance or technical reference material" ] } } } ``` The `insufficient_evidence` option is deliberate. If the page does not support a reliable audience classification, forcing it into `developers`, `business_buyers`, or `mixed` hides the gap. Your application can route that result to review instead of presenting a guess as a fact. Keep each question narrow enough that its answer space is reviewable. “What should we do with this website?” combines classification, evaluation, prioritization, and policy. Separate those decisions, then combine the results in code. A high score on technical detail does not prove that the value proposition is clear, and a likely audience category does not prove that the page converts. ## Treat the page as untrusted data A scraped page is content, not an instruction channel. It can contain text such as “ignore previous instructions,” requests for secrets, fake citations, or directions intended for a model rather than a reader. The [Jev Web Analyzer repository](https://github.com/replynodes/jev-web-analyzer) demonstrates this boundary with a public implementation: a URL is fetched through ReplyNodes, converted into clean Markdown, and passed as state to Jev through Vercel AI Gateway. The repository is an unofficial community project, not a TypeSafe-affiliated product. Its server-side route validates public URLs, keeps credentials private, treats webpage content as untrusted state, and exposes the real fetch, extraction, and evaluation stages. Use defense in depth around the state pipeline: - keep API keys outside the state and outside browser bundles; - label retrieved page text as data in the model boundary; - enforce URL, domain, redirect, page-count, depth, timeout, and response-size rules in application code; - validate that citations and URLs came from the retrieved source set; - test with pages containing indirect prompt-injection text; - require review or an insufficient-evidence result when the records do not support the decision. Prompt instructions alone are not a security boundary. The network client, credentials, tool registry, and output validator must enforce the boundary independently. ## Common state-preparation failures ### Sending raw HTML without selection Raw HTML contains layout, navigation, scripts, duplicated text, and other material that may not help the question. Extracting clean content can make the state easier to inspect, but it is not an accuracy guarantee. Measure a change against labeled examples if the decision matters. ### Losing provenance during normalization If the Markdown is copied into a string without its URL, retrieval timestamp, and request ID, a later answer can no longer show which page supported it. Keep provenance beside the content from retrieval through the final result. ### Mixing unrelated pages in one state An array remains one shared state; it is not a batch of independent website judgments. Keep pages together only when the question explicitly concerns the collection. Otherwise, evaluate each page in its own context or create named records with a clear relationship. ### Rewriting hypotheses as facts Preserve language such as “may be aimed at enterprise buyers” as a hypothesis or uncertain observation. Do not turn it into “the company targets enterprise buyers” during preprocessing unless the source actually supports that statement. ### Letting a typed answer trigger an unreviewed action Choice, Score, and Noul constrain the shape of a decision; they do not give the model authority to publish, modify an account, or change production. Keep thresholds, permissions, and side effects in application code. ReplyNodes' current public data surface is read-only; downstream actions, if any, belong to your application and its own controls. ## A practical checklist Before sending web context to Jev, verify: - The state contains only pages and fields relevant to the decision. - Every page has a source URL and retrieval time when freshness matters. - The API request ID is retained for debugging and correlation. - Evidence is separate from questions and application policy. - Uncertain observations remain visibly uncertain. - An insufficient-evidence path exists when a forced choice would mislead. - The question criteria describe observable differences rather than vague labels. - Retrieved text is treated as untrusted data. - The ReplyNodes key stays server-side. - Thresholds and side effects are enforced outside Jev. For the retrieval layer, start with the [ReplyNodes web-context guide](https://docs.replynodes.com/docs/guides/web) and the [web scraping API guide](https://replynodes.com/blog/web-scraping-api-for-ai-agents/). For a complete, inspectable example of the boundary, read the [Jev Web Analyzer source repository](https://github.com/replynodes/jev-web-analyzer). The goal is not to make the page sound more certain than it is. The goal is to give Jev a bounded, inspectable state, ask a decision that has a clear answer space, and let application code decide what happens next.