Skip to content

Scan to Item

Item intake turns a scanned barcode, product URL, typed description, or Arda card QR into a reviewable Item. Enrichment builds the product draft and buying options; blank-card binding attaches a QR to an Item. Enrichment runs as in-process modules inside the arda-frontend-app BFF, behind POST /api/enrichment/import, the single front door that wraps Amazon server-side. This document covers the working flow and the remaining decisions.

StatusPrototype under evaluation
Production runtimearda-frontend-app BFF — POST /api/enrichment/import
Enrichment engineIn-process modules in the BFF, src/server/lib/enrichment/, with the ported engine under src/server/lib/enrichment/engine/
User entry pointScanner and quick item form in arda-frontend-app
Write pathExisting Item and ItemSupply flow

The first production version should:

  • accept a product barcode from the camera scanner, and accept a URL or plain text when the user starts enrichment from a form;
  • reach Amazon only through server-side delegation to the Amazon BFF routes (/api/amazon/import for a URL or ASIN, /api/amazon/search for keyword text merged with web discovery), never as a client-selectable provider;
  • return a reviewable draft with product details, offers, images, confidence, and field-level evidence;
  • use exact identifier sources for identity and web search for supporting product or supplier information;
  • keep Arda card QR codes in the card and blank-card binding flows;
  • continue when an individual provider fails or times out;
  • keep provider credentials out of the browser and response body;
  • scope every run and tenant-owned source to the authenticated tenant; and
  • leave Item and ItemSupply creation in the existing save flow; and
  • return a single reviewable draft that already folds in optional offers and packaging suggestions where available, without ever overwriting a field the user has edited.

The input contract also needs room for new acquisition adapters. An image or document can be uploaded separately and referenced by an enrichment call, then an OCR adapter can turn it into text and identifier candidates. Those candidates can use the same provider and reconciliation pipeline as a typed description or scanned barcode.

For V1, a missing or uncertain result is better than a confident false match. The UI keeps needs_review results editable and asks the user to confirm them before saving.

These are prototype gates rather than contractual SLOs.

MeasureTarget
Confidently wrong identitiesZero in the evaluation set
Precision among matched resultsAt least 90%
Useful coverage across the mixed item setAt least 70%
First authoritative resultp95 below 2 seconds
Completed enrichment callBarcode scans at or below 5 seconds; URL and text lookups at or below 10 (enforced budgets)
Provider resilienceOne failed provider does not prevent a usable result from another
Credential safetyNo credential appears in a result or source trace
  • Enrichment does not create or update an Arda item.
  • Tenant catalogs, accepted-result memory, and durable result caching are not required for the first live flow.
  • Package conversions are not inferred without explicit unit and quantity data.

The component view below shows a scan moving through the in-process enrichment modules inside the BFF, while accepted item data continues through the existing Operations write path.

PlantUML diagram

The frontend calls POST /api/enrichment/import, a same-origin BFF route, so provider credentials never reach the browser. The route runs exact identity sources in parallel, reconciles their responses, and then uses the resolved product to search for supplier and product pages. It returns a suggestion; the user reviews it and saves through the existing item flow.

Scanning an Arda card QR does not start product enrichment. That scan continues through the existing card lookup or blank-card binding flow, which prevents a card identifier from being mistaken for a product barcode.

Barcode Lookup is the primary identity source for a scan; UPCitemdb and Open Food Facts add catalog coverage. Exa contributes supporting search evidence rather than deciding exact identity by itself. Amazon is not an engine adapter: the BFF reaches it directly — an Amazon URL or ASIN delegates to /api/amazon/import for exact lookup, and freeform text fans out to /api/amazon/search alongside Exa discovery, merged by the reconciler.

After identity resolves, an optional small-model adapter suggests unit type, units per package, minimum order, and order increment. Those fields keep their own evidence and remain suggestions in the form.

The response keeps the provider, authority, and cache policy for each selected or competing field. Offers stay separate from product identity and are marked exact, equivalent, or related. This gives the UI enough information to show uncertainty instead of flattening every provider response into one opaque answer.

Enrichment prepares form values but does not own Arda item state. This keeps validation, authorization, audit history, and ItemSupply creation in the existing Operations path.

The frontend calls POST /api/enrichment/import once per scan and waits for the response. There is no run resource, no polling, and no Prefer header — the route runs every adapter and returns the reconciled draft. The frontend can still show its own loading state while the request is in flight; that is UI-only, not a second round trip to the BFF.

The working local demo uses the in-process engine in the arda-frontend-app BFF, plus the frontend scanner and quick item form.

The automated live corpus has nine cases: two barcode matches, a negative short-code case, and six supplier-URL cases. Unit tests cover the reconciler and normalization in depth (source precedence, corroboration, confidence, package mismatches, image selection), the orchestrator’s budget deadline, Exa search targeting and acceptance, OpenAI packaging output limits, supplier catalog matching, and model extraction; the import route covers auth, validation, success, and error mapping; and the QR resolve/bind mocks have their own suite. Live provider behavior end to end is exercised by the corpus (npm run eval:live). A local run has also exercised the OpenAI adapter against the real API; that is not yet a representative model evaluation.

The next evaluation step is a set of 30–50 physical items from real Arda environments, with ground truth recorded before enrichment runs. Promotion beyond an internal pilot should require the precision, coverage, latency, and credential-safety gates above.

  • Frontend and UX describes scan routing, loading and review states, form mapping, mobile and desktop behavior, and production UX work.
  • Backend service describes the import contract, provider pipeline, reconciliation, ranking constants, tenancy, persistence, observability, and endpoint alternatives.
  • Adapters and source authority describes how to author a new adapter and how identity, discovery, and tenant sources are ranked.
  • Evaluation and acceptance describes the physical-item test plan and the gates a pilot needs to clear.
  • Bind a QR to an Item is the short version of the blank Card flow and the backend work it still needs.
  • For freeform text, does merging /api/amazon/search with the engine’s Exa-search behind one enrichment front door justify enrichment depending on the Amazon route being up, versus keeping /api/amazon/import and /api/enrichment/import as composable routes and routing client-side?
  • Should reusable enrichment contain provider output, user-approved results, or both?
  • How should tenant and supplier catalogs be ingested, updated, and owned?
  • The OpenAI packaging model already runs on every scan wherever OPENAI_API_KEY is set. Should production keep that default or require an explicit opt-in? What acceptance and edit rates would justify default-on?

Both live on feature/pdev-1161-scan-to-item.