Every 15 minutes
Check new HubSpot contacts and companies for bad data
New contacts with broken emails and new companies with dead websites are flagged in Slack before anyone works them.
- Runs
- Every 15 minutes
- Apps
- HubSpot, Slack
- AI
- Not needed
- Access
- HubSpot crm.objects.contacts.read/write, crm.objects.companies.read/write; Slack chat:write
Send this to your assistant
Create a ClickRuntime automation: every 15 minutes, check new HubSpot companies and contacts. If a company website doesn't load or a contact email looks invalid, mark the record and post it in #crm.
First time? Adding ClickRuntime to your assistant takes about two minutes.
Why use it
Why teams use this automation
A marketing team imports leads from events, forms and partner lists into HubSpot. A good share arrive with typos in email addresses or company websites that no longer exist. Reps discover this one at a time, after a bounced email or a wasted research session.
This automation checks each new contact and company soon after it is created, marks the ones that fail, and posts them in Slack so someone can fix or remove them while the source is still fresh.
What you get out of it
Fewer bounced emails
Catching malformed addresses before the first sequence goes out protects your sending reputation and keeps open rates honest.
Reps spend time on real companies
A company whose website does not load is often closed, renamed or a typo. Flagging it saves a rep from researching a dead end.
Problems are fixed at the source
Seeing bad records within minutes of import makes it easy to trace them back to the form or list that produced them.
A cleaner CRM over time
Every record is checked once when it arrives, so data quality improves steadily instead of needing a big clean-up every quarter.
How it works
What happens, step by step
Every 15 minutes ClickRuntime looks for contacts and companies created in HubSpot since the last check.
It checks that each contact's email address is well formed and that each company's website loads.
Records that fail are marked in HubSpot so they are easy to filter.
A list of the failed records is posted in your #crm Slack channel. Records already checked are never checked again.
Before it runs live
- Connect HubSpot
- Connect Slack, then invite the app to the channel (type /invite @ClickRuntime there)
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
- Act only on the items that match your own rules, and skip the rest
- Add an AI step, for example to summarize, sort or draft a reply
- Add another app, for example also send it to Google Sheets
- Combine it with other HubSpot and Slack steps into one automation
Under the hood
The code that runs this automation
import re
from clickruntime import on_trigger, credentials, http, notify, state, report_fetched
API = "https://api.hubapi.com/crm/v3/objects"
@on_trigger
def run(event):
h = {"Authorization": f"Bearer {credentials.secret('HUBSPOT_API_KEY')}"}
since = state.get("since") or "2026-01-01T00:00:00Z"
body = lambda props: {"filterGroups": [{"filters": [{"propertyName": "createdate", "operator": "GT", "value": since}]}],
"properties": props, "sorts": [{"propertyName": "createdate", "direction": "ASCENDING"}], "limit": 100}
contacts = http.post(f"{API}/contacts/search", headers=h, json=body(["email", "createdate"])).json()["results"]
companies = http.post(f"{API}/companies/search", headers=h, json=body(["name", "domain", "createdate"])).json()["results"]
report_fetched(len(contacts) + len(companies))
if not contacts and not companies:
return {"checked": 0}
bad = []
for c in contacts:
if not re.fullmatch(r"[^@\s]+@[^@\s]+\.[a-z]{2,}", (c["properties"].get("email") or "").lower()):
bad.append(("contacts", c["id"], f"email {c['properties'].get('email')}"))
for c in companies:
try:
ok = http.get(f"https://{c['properties'].get('domain')}", timeout=10).status_code < 400
except Exception:
ok = False
if not ok:
bad.append(("companies", c["id"], f"{c['properties'].get('name')} website"))
for kind, rid, _ in bad:
http.patch(f"{API}/{kind}/{rid}", headers=h, json={"properties": {"data_check": "failed"}})
if bad:
notify.slack("Data check failed:\n" + "\n".join(f"• {why}" for _, _, why in bad), channel="#crm")
state.set("since", max(r["properties"]["createdate"] for r in contacts + companies))
return {"checked": len(contacts) + len(companies), "flagged": len(bad)}FAQ
Common questions
What makes an email look invalid?
Does it delete bad records?
Are existing records checked too?
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 new HubSpot deals and stage changes in Slack
Your sales channel hears about every new deal and every stage move, with amount and owner.
See how it worksAlert Slack about urgent HubSpot tickets left open
High-priority HubSpot tickets open for more than 4 hours are flagged in your support channel so someone picks them up.
See how it worksPost high-quality HubSpot leads to Slack
New HubSpot contacts from larger companies or with a high lead score show up in #sales within minutes, with a link to the record.
See how it worksKeep exploring
Set up “Check new HubSpot contacts and companies for bad data” from your assistant
Copy the prompt above, or connect your assistant first. Free plan, no credit card.