A collage of Offline members eating and drinking at local restaurants

Case study · Offline

A weekly chore, turned into a queue of drafts

Learn more about Offline →

The problem

“We'd already automated the check against Google. But every mismatch it found still landed on a person to sort out.”

Automatically syncing updates from an API is easy, but when they are prone to errors it can lead to a lot of problems down the line. With the inclusion of AI, I could finally build the judgment needed to fully automate this process: reading and comparing business websites to find out whether Google is actually right.

Aerial view of a city where Offline partners are located
Italian$50 TO SPENDMexican$30 TO SPENDNew American$30 TO SPEND
500+ restaurant partnersone market
500+restaurant partners whose hours are checked every week
1+ hof manual work, now handled by an agent
3–4AI-drafted partner emails ready to send each week

The before

A chore that had to be remembered

At Offline we work with 500+ active restaurant partners, and partner hours and closures change constantly. A weekly job already compared each partner's listed hours against Google Places and raised a content alert on any discrepancy. Resolving those alerts was the manual part.

About ten alerts a week, two to three minutes each to investigate properly. A genuine conflict meant an email — another ten minutes to find the contact, compose it, and gather the links. More than an hour a week, all told, and its real cost was never the time: it had to be remembered.

The same city, with each partner's hours checked in a weekly jobContent alerts10 openHours differ from Google2–3 minClosed on Google, open here2–3 minHoliday hours on Google2–3 minHours differ from Google2–3 minNew hours on their site?2–3 min+ 5 moreNEEDS A PERSON · ~30 MIN/WKCHECKED000/500025/500050/500075/500100/500125/500150/500175/500200/500225/500250/500275/500300/500325/500350/500375/500400/500425/500450/500475/500500/500ALERTS012345678910
checked weekly~10 raise an alert

Why it wasn't already solved

The checking was easy. The judgment wasn't.

The checking was never the hard part. The judgment was. Google returns two different kinds of hours — this week, and in general — and holidays land in the wrong one, which manufactures false flags. Confirming a real conflict means reading the partner's own website, where the hours are never in the same place twice, and where a multi-location partner needs the right location identified first. That is unstructured reading across 500+ different sites, ten times a week, and until recently the only place to get it was a person.

VERIFIEDstamped for 30 daysCONFLICTemail drafted for a personGGooglehoursMon11–9Tue11–9Wed11–9Thu11–9FriClosedSat10–10Sun10–9Mon11–9Tue11–9Wed11–9Thu11–9Fri11–10Sat10–10Sun10–9their own websiteHOURSMon–Fri 11–9Sat–Sun 10–10Sat 10–10 · Sun 10–9Mon–Thu 11–9 · Fri 11–10GOOGLE: CLOSED · SITE: OPENBOTH SAY SAT 10–10GOOGLEWEBSITECONTENT ALERTFri · hours don't matchCONTENT ALERTSat · flagged as changed
both sources, every alertthe reading is the hard part

The solution

The agent works the queue, but a person still sends.

An agent works the alert queue: classifies each alert, verifies it against both Google and the partner's own website, applies the right change, closes the alert — and drafts a contextualized email whenever a person needs to be in the loop.

The weekly check runs itself and most alerts resolve silently. What reaches a person is a short queue of draft emails, with the contact already looked up in HubSpot and links to the content, the partner's Google review page, and their website already gathered. Edit, approve, send.

Human in the loop

Keeping communication person to person

Marking something as closed wrongly is reversible and costs a partner a day of visibility. Sending someone to a restaurant that actually is closed breaks all trust. So the agent marks the restaurant unavailable immediately and the email is drafted waiting for a human to send. This allows mistakes to be reviewed and corrected before needless communication is sent out.

THE AGENT DOES ITeverything it touches can be undoneA PERSON SENDS ITan email can't be unsentCAN IT BE UNDONE?Marked the partner unavailablemembers won't be sent thereundoClosed Sunday on the listingmatches their websiteundoStamped Mon verifiedwon't re-alert for 30 daysundoResolved the alertone transaction, one audit entryundoDrafted an email to the partnerand stoppedDraftin GmailTothe partner's contactReAre you closed Sundays now?HubSpot contactlistingGoogle reviewstheir sitewaiting on youSendsent by a person
reversible → the agentcan't be unsent → a person

What makes it trustworthy

Nothing commits unread

I made every write run as a dry run first. The agent reads back exactly what would change — including how many members would be notified — before it commits to anything. The change and the alert's resolution then land together, in one transaction, producing one audit entry.

Built with

Rails 8PostgreSQLGoogle Places APIHubSpotGmail draftsn8nClaude agent skill

A weekly cron raises the alerts each Sunday. The agent works the queue from there, one alert at a time.

The checking was always easy. The judgment is what just became possible.

It found its own blind spot

My hand-drawn map was incomplete, and the agent is what found the gap. After it had been running a while, it diagnosed the cause of its own false positives: those addresses were linked to a physical address rather than a Google Place, so no place data came back at all. Three hand-mapped branches became four. More usefully, the tool stopped being only an hours-checker and became a data-validation instrument — surfacing bad address links nobody knew were there.

Glenwood AvePeace Stthe addressAN ADDRESS, NOT A PLACEthe restaurantGPlace lookupLINKED TOA STREET ADDRESSBUSINESS—HOURSno hours returnedLINKED TOTHE RESTAURANTBUSINESSRestaurant · ItalianHOURSMon–Thu11–9Fri11–10Sat10–10Sun10–9ALERT · NO HOURS TO CHECKMATCHES · VERIFIED
an address, not the businessa few metres, and the hours arrive

Where would this fit?

This started as a process somebody was already doing by hand. If you have one of those, I'd like to hear about it.