Part 2 of 7 · Price monitor series ~4 min read

How a competitor page gets watched

The monitor only watches what’s on the watch list. So the first job is making sure that list reflects the competitors you actually care about. There are three ways a page gets in: somebody types a row in the Drive sheet, somebody pastes a link into Slack, or a product in your own catalog gets matched to a competitor page. The first one is obvious. The other two exist because in real life nobody opens a spreadsheet to add the rival page they just spotted in a browser tab.

Key takeaways

  • Three intake lanes feed one watch list: the Drive sheet, a paste-a-link lane, and a catalog import.
  • Each new page is read once so the monitor can save a price rule — where on the page the number lives.
  • If a plain rule can’t find the price, Bedrock Haiku 4.5 proposes one from the page text.
  • Every proposed row goes to a person for one-tap approval before it lands on the list.
  • The watch list stays the canonical store. The other lanes are conveniences that write into it.

Three lanes into one watch list

Three intake lanes funnel into one watch list A diagram with three vertical lane columns at the top and a single unified row at the bottom. Lane one, Drive sheet: somebody types a row directly into the Google Sheet that holds the watch list; the drive-sync Lambda mirrors the sheet to S3 every 15 minutes, and the monitor reads from there. Lane two, Paste a link: somebody drops a competitor product URL into a Slack box; a reader Lambda fetches the page once, politely, and tries a plain price rule; if that fails it calls Bedrock Haiku 4.5 to find the price in the page text and propose a saved rule; the proposed row gets posted back to Slack with Approve and Edit buttons; on approve, the row is added to the Drive sheet via the Sheets API. Lane three, Catalog import: products in your own store catalog tagged for watching get matched hourly by a catalog-sync Lambda to known competitor pages; the same propose-and-approve flow runs in Slack before the row lands in the sheet. All three lanes converge on the same Drive sheet, which the drive-sync Lambda keeps mirrored to S3 for the monitor to read. A note at the bottom: the Drive sheet stays the source of truth — the other lanes are conveniences that propose rows for it. Lane 1 · manual Drive sheet • Somebody types a row directly • drive-sync mirrors to S3 every 15 min • Monitor reads from S3 • Source of truth stays in Drive Lane 2 · fetch + read Paste a link • Drop a URL into a Slack box • Reader fetches once, tries a plain rule • If stuck, Haiku 4.5 proposes a price rule • Slack one-tap approve → sheet Lane 3 · hourly poll Catalog import • Tag a product in your catalog to watch • catalog-sync polls hourly, matches pages • Same proposal flow to Slack as Lane 2 • On approve → added to sheet Drive watch list (source of truth) product · competitor · URL · field · check rate · your price · threshold · owner drive-sync mirrors it to S3 every 15 min — monitor reads from S3 to checker, on schedule The Drive sheet stays the source of truth — the other lanes are conveniences that propose rows for it.
Fig 2. Three lanes converge on one Drive sheet. The sheet is the source of truth; the paste-a-link lane and the catalog lane are conveniences that propose rows for human approval. The drive-sync Lambda mirrors the sheet to S3 so the monitor can read it without hitting Drive on every check.

Lane 1: the Drive sheet itself

The simplest lane. Open the watch list in Drive, add a row, save. The columns are short: product name, competitor, page URL, the field to read, a check rate, your own listed price, a move threshold, and the owner email. A small Lambda — drive-sync — runs every fifteen minutes, exports the sheet as plain CSV via the Drive API, and writes it to s3://pm-watchlist-source/watchlist.csv if the sheet has changed since the last sync. The monitor reads from S3, not Drive directly. That keeps Drive API calls predictable and gives you S3 versioning for free, so a bad bulk-edit can be rolled back in one click.

This lane covers the cases where you already know the page, you know what to read, and you can spend thirty seconds typing it in. Most existing competitors go in this way during the initial setup.

Set up a small Slack box — a slash command like /watch or a dedicated channel — where anyone on the team can drop a competitor product URL. A reader Lambda takes it from there. The Lambda fetches the page once, politely (one quiet request, a clear identifying user agent, honoring the site’s robots rules), and tries a plain price rule first: look for common price markup — a price microdata field, a JSON-LD offers.price, or a labelled price element. Most well-built shop pages give the price up cleanly this way, and no model is needed.

When the plain rule comes up empty — the page renders the price oddly, or buries it in a script — the Lambda calls Bedrock Haiku 4.5. The prompt is short: “Here is the visible text of a product page. Return the current price, the currency, and a short rule describing where you found it. Return JSON only. Do not invent a price that isn’t in the text.” The output — product name, competitor, the price found, and a proposed saved rule for reading it next time — goes to a small Slack interactive message that pings whoever pasted the link: the proposed row, the price the monitor read, and three buttons — approve, edit, discard. On approve, a Lambda writes the row to the Drive sheet via the Sheets API. On edit, the person gets a fillable modal pre-populated with the proposal. On discard, the message is logged.

The reason every proposed row goes to a human first is simple: a price the monitor misread is worse than a page that never made it onto the list. The misread one will quietly report a wrong number every day until somebody notices the alerts don’t match reality.

Lane 3: catalog import

Some teams already keep a list of which of their products map to which competitors — in their store admin, or a tagging column in their catalog export. Forcing those teams to also type rows in a separate sheet is a fight you don’t need to have on day one.

Lane 3 picks up products in your own catalog that carry a “watch” tag and a competitor URL. A small catalog-sync Lambda runs hourly, reads the catalog export (from a store API or a nightly CSV drop, using a credential stored in Secrets Manager), and finds any newly tagged products. Each becomes a proposal in the same Slack flow as Lane 2 — read once, propose a saved rule, one-tap approve to add to the watch list. Once approved, the catalog tag can stay where it is; the watch list now owns the page.

Catalog import is the most opt-in of the three lanes. A team that doesn’t use it loses nothing; a team that does avoids retyping things they already keep elsewhere.

Why the watch list stays the source of truth

Three lanes in, but only one place where the monitor actually looks. That’s a deliberate constraint. If two lanes both wrote directly to the monitor’s state, every “why did this alert go out?” question would mean checking three places. Funneling everything through the Drive sheet means there is exactly one row per page, and any rep can read or edit any of it without learning a new tool. The convenience lanes are first-class for getting pages in, but they always pass through the sheet on the way.

Next post: how the monitor actually checks a page, reads the price, compares it to history, and picks one of four moves.

All posts