Every 3 hours
Store new reviews in Supabase and send angry ones to Slack
Every 3 hours, new reviews are saved once in Supabase with a sentiment label, and negative ones go straight to Slack.
- Runs
- Every 3 hours
- Apps
- Slack, Supabase
- AI
- Yes, uses the built-in AI allowance
- Access
- Supabase read+write; Slack chat:write
Send this to your assistant
Create a ClickRuntime automation: every 3 hours, pull new Google reviews for my business, store them in my Supabase reviews table without duplicates, and post the negative ones to #customer-care.
First time? Adding ClickRuntime to your assistant takes about two minutes.
Why use it
Why teams use this automation
A local business gets reviews across its locations, and a one-star review left on Friday night can sit unanswered all weekend, in front of every new customer who looks it up.
Collecting reviews every few hours, labelling their sentiment and pushing the negative ones to Slack means unhappy customers get a reply while it can still change their mind, and Supabase keeps the full history.
What you get out of it
Reply to complaints quickly
A fast, polite reply to a bad review limits the damage and sometimes wins the customer back.
A full review history
Every review is stored once in Supabase, ready for reports by location or month.
Only the urgent ones interrupt
Positive reviews are stored quietly; only negatives reach Slack.
No duplicates
Reviews already stored are skipped, however often the check runs.
How it works
What happens, step by step
Every 3 hours, ClickRuntime fetches the latest reviews for your business.
It skips reviews already stored in your Supabase reviews table.
AI labels each new review as positive, neutral or negative.
New reviews are saved to Supabase with their label.
Negative reviews are posted to #customer-care in Slack.
Before it runs live
- Connect Slack, then invite the app to the channel (type /invite @ClickRuntime there)
- Connect Supabase
What you get
Build your own
Need it to work differently?
Your own rules
Conditions, thresholds, filters and exceptions, written the way you'd explain them to a colleague.
Any number of apps
One automation can read from one app and update several others. Over 248 apps are ready, and anything with an API works too.
AI where it helps
Summaries, classification, extraction and drafts use ClickRuntime's built-in AI, with no extra key.
On a schedule or on each event
Runs as often as every 5 minutes, or the moment an app sends an event, and keeps track of what it already handled.
Changes you can ask for
- Run it at a different time, more or less often, or only on weekdays
- Change what the AI looks for, or how it writes the result
- Add another app, for example also send it to Google Sheets
- Combine it with other Slack and Supabase steps into one automation
Under the hood
The code that runs this automation
from clickruntime import on_trigger, credentials, http, notify, ai, report_fetched
PLACE_ID = "your-google-place-id"
SUPABASE = "https://your-project.supabase.co/rest/v1"
def sb(method, path, **kw):
key = credentials.secret("SUPABASE_KEY")
r = http.request(method, f"{SUPABASE}/{path}", **kw, headers={"apikey": key, "Authorization": f"Bearer {key}", "Prefer": "return=representation,resolution=merge-duplicates"})
return r.raise_for_status() or (r.json() if r.content else None)
@on_trigger
def run(event):
place = http.get(f"https://places.googleapis.com/v1/places/{PLACE_ID}", headers={"X-Goog-Api-Key": credentials.secret("GOOGLE_PLACES_KEY"),
"X-Goog-FieldMask": "reviews"}).json()
reviews = [{"id": r["name"], "rating": r["rating"], "text": r.get("text", {}).get("text", ""), "author": r["authorAttribution"]["displayName"]}
for r in place.get("reviews", [])]
known = {r["id"] for r in sb("GET", "reviews", params={"select": "id", "id": "in.(" + ",".join(f'"{r["id"]}"' for r in reviews) + ")"})} if reviews else set()
new = [r for r in reviews if r["id"] not in known]
report_fetched(len(new))
if not new:
return {"summary": "Nothing new"}
listing = "\n".join(f"{i}. ({r['rating']}/5) {r['text'][:500]}" for i, r in enumerate(new))
labels = ai.generate(f"Label each review positive, neutral or negative. One word per line.\n{listing}", max_output_tokens=8 * len(new)).text.split()
for r, label in zip(new, labels):
r["sentiment"] = label.lower()
sb("POST", "reviews", json=new)
for r in [r for r in new if r.get("sentiment") == "negative"]:
notify.slack(f":warning: {r['rating']}★ review from {r['author']}: {r['text'][:400]}", channel="#customer-care")
return {"new": len(new)}FAQ
Common questions
Which review sources does it read?
How is sentiment decided?
Does it reply to reviews?
What permissions does it need?
Does it need AI?
How many runs does it use?
Can I change how it works?
Related
You might also like
Post a monthly summary of your Supabase figures to Slack
On the 1st of each month, Slack gets a short written summary of last month's figures from your Supabase table, with the changes from the month before.
See how it worksPost members who've stopped checking in to Slack each morning
Every morning, Slack lists the members in your Supabase table who haven't checked in for 14 days, highlighting the ones new to the list since yesterday.
See how it worksScore every new lead and post the hot ones to Slack
Each new lead gets an AI score and is saved to your Supabase leads table, and hot leads are posted to your sales channel straight away.
See how it worksKeep exploring
Set up “Store new reviews in Supabase and send angry ones to Slack” from your assistant
Copy the prompt above, or connect your assistant first. Free plan, no credit card.