How a stock level gets read
The bot only watches the counts that are in the stock sheet. So the first job is making sure those counts actually reflect what’s on the shelf. There are three ways a count gets updated: somebody types it in the Drive sheet, somebody forwards a stock-count sheet to a dedicated address, or your till tells the bot the moment something sells. The first one is obvious. The other two exist because in real life nobody types a new count into a sheet every time a customer buys something.
Key takeaways
- Three intake lanes feed one stock sheet: the Drive sheet, an inbox-forwarding lane, and a live POS lane.
- Inbound stock sheets are parsed by Textract; Bedrock Haiku 4.5 reads the text and proposes updated rows.
- Every parsed row goes to the owner’s Slack for one-tap approval before it lands in the sheet.
- The POS lane posts each sale to the bot, which lowers the on-hand count in near real time.
- The stock sheet stays the canonical store. The other lanes are conveniences that write into it.
Three lanes into one stock sheet
Lane 1: the Drive sheet itself
The simplest lane. Open the stock sheet in Drive, edit a count, save. The columns are short: name, SKU, supplier, on-hand count, daily sales rate, supplier lead-time days, safety buffer, pack size, and unit cost. A small Lambda — drive-sync — runs every fifteen minutes, exports the sheet as plain CSV via the Drive API, and writes it to s3://ir-stock-source/stock.csv if the sheet has changed since the last sync. The bot 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 do a physical count, you know the new number, and you can spend a few seconds typing it in. Most items get set up this way during the initial stocktake, and a monthly count is the natural time to correct any drift.
Lane 2: inbox forwarding (for count sheets and price lists)
Set up a dedicated inbound address — something like stock@your-company.com — via Amazon SES. Anyone on the team forwards a stock-count sheet (the printout from a shelf count, or a supplier’s packing list) to that address and the bot takes it from there. SES writes the raw MIME to s3://ir-raw-mime/. The S3 PUT triggers a parser Lambda. The Lambda walks the MIME tree to the attachment, runs Amazon Textract on it (Textract reads PDF, PNG, JPEG, and TIFF natively, and pulls tables cleanly; if somebody forwards a spreadsheet, the parser falls back to openpyxl), and gets back the extracted text plus any tables.
Then a Bedrock Haiku 4.5 call reads the text and emits structured rows: for each line it can match to a known SKU, the new on-hand count and, if the document is a price list, the new unit cost. The model prompt is short: “Match each line to a SKU in this list. Return JSON only. Mark each field with a confidence score. Do not invent a count that isn’t in the text.” The output goes to a Slack interactive message that pings the owner: the proposed changes, the confidence per field, and three buttons — approve, edit, discard. On approve, a Lambda writes the rows to the Drive sheet via the Sheets API. On edit, the owner gets a fillable modal pre-populated with the proposal. On discard, the message is logged and the file moved to a discarded prefix in S3 for audit.
The reason every parsed change goes to a human first is simple: a count the model misread is worse than a count that never made it into the sheet at all. The misread one will quietly tell you a bin is full when it’s nearly empty, and the first you’ll hear of it is the missed reorder.
Lane 3: live POS
Most shops already have a till or an online store that records every sale. Forcing the team to also re-type counts in a sheet is a fight you don’t need to have on day one. Lane 3 lets the point-of-sale system keep the counts current on its own.
Your till or store is configured to post each sale to a Function URL — a small web address the bot exposes — the moment it happens. A small pos-handler Lambda receives the SKU and quantity sold, lowers the matching on-hand count in the sheet (batched every few minutes so a busy hour doesn’t hammer the Sheets API), and that’s it. No approval step, because a recorded sale is already a fact — unlike a forwarded sheet, there’s nothing to second-guess. If your POS supports it, the same lane can pick up stock received (a delivery arriving) and add to the count too.
The POS lane is the most hands-off of the three. A shop that can’t wire its till to a webhook just leans on Lanes 1 and 2 and loses nothing but immediacy; a shop that can gets counts that are always close to reality.
Why the sheet stays the source of truth
Three lanes in, but only one place where the bot actually looks. That’s a deliberate constraint. If two lanes both wrote directly to the bot’s state, every “why did this reorder fire?” question would mean checking three places. Funneling everything through the Drive sheet means there is exactly one row per item, and any staffer can read or edit any count without learning a new tool. The convenience lanes are first-class for keeping counts fresh, but they always pass through the sheet on the way.
Next post: how the bot actually reads the sheet, works out the reorder point per item, and picks one of four moves.
All posts