Nightly sync of HubSpot list to Meta Ads Custom Audience

Every night at 2am, rebuild a Meta Ads Custom Audience from a HubSpot list so retargeting always matches your current CRM segment.

Deterministic Code
HubSpotMeta AdsSlack BotMarketingData SyncNotifications & Alerts
PromptCreate

Build a code workflow that runs every night at 2am and keeps a Meta Ads Custom Audience in sync with a HubSpot contact list, so retargeting always reflects the current CRM segment. This is a deterministic pipeline, not a decision agent.

Trigger: cron, daily at 02:00 in the workspace's default timezone.

Inputs (so this same workflow can be reused for multiple segments like churn risk, high LTV, product A buyers):

1) HubSpot list identifier (the named list or filter to pull from).

2) Meta Ads Custom Audience id (a pre-created customer list audience).

3) Slack channel id for the summary message.

Steps:

1. Use HubSpot Search Contacts to pull every contact that matches the given list or filter. Paginate all the way through until the full membership is loaded. Request email, phone, first name, last name, city, state, country, and zip so Meta has plenty of match fields.

2. Format each contact into the user record shape Meta's Custom Audience upload expects. Normalize email (trim and lowercase) and phone (digits only, keep country code) so the platform's hashing produces stable matches. Skip contacts with no email and no phone.

3. Call the Meta Ads Replace Users in Custom Audience operation against the given audience id. This swaps the entire membership in a single session, so adds and removals both take effect atomically. Handle Meta rate limit responses with retry-and-backoff, and fail loudly on non-retryable errors.

4. Send a Slack Bot Send a Message to the configured channel with a short summary: HubSpot list name, Meta audience id, records pulled from HubSpot, records sent to Meta, session id, records Meta accepted for matching, and any error codes returned. Keep it terse enough to be readable in a channel.

Error handling: strict. If HubSpot pagination fails, abort before touching Meta so we never send a partial list. If Meta rejects the session, post the failure to Slack with the error payload so someone can act on it in the morning.

What does this prompt do?

  • Pulls every contact from a chosen HubSpot list, no matter how large.
  • Rebuilds a matching Meta Ads Custom Audience from scratch each night, so adds and removals are reflected in a single atomic swap.
  • Posts a short Slack summary showing how many contacts were sent, how many Meta accepted for matching, and any errors from the upload.
  • Runs unattended on a schedule, so retargeting audiences stay fresh without anyone touching a CSV.

What do I need to use this?

  • A HubSpot login with access to the list you want to sync.
  • A Meta Ads account and a Custom Audience already created for customer lists.
  • A Slack workspace where the summary should be posted.
  • The HubSpot list identifier and the Meta Custom Audience identifier ready to paste in as inputs.

How can I customize it?

  • Change the time of day or the run frequency, for example run it hourly for a fast-moving segment.
  • Point it at a different HubSpot list and Meta audience to sync a second segment such as churn risk or high value buyers.
  • Change the Slack channel or replace it with an email summary if the marketing team lives somewhere else.

FAQs

Do I need to hash emails and phone numbers myself?
No. The workflow prepares each contact's email and phone in the exact shape Meta expects and Meta handles the matching. You just point it at the HubSpot list and the Custom Audience.
What happens if someone was removed from the HubSpot list?
Because the workflow replaces the whole audience each night, anyone no longer on the HubSpot list is also removed from the Meta audience automatically. Adds and removals happen together in one swap.
Can I use this for more than one audience?
Yes. The HubSpot list and the Meta audience are inputs, so you can run separate copies for different segments like product buyers, high value customers, or churn risk.
Will this work with a very large list?
Yes. The workflow pages through the full list until every matching contact has been pulled, then sends them to Meta in one session.
What if Meta rejects the upload or throttles the request?
The workflow watches for Meta rate limit and error responses, retries where it can, and includes any final error codes in the Slack summary so you know exactly what to fix.

Related templates

A brand asset library your marketing team actually searches

Every logo, photo, video cut and ad export in one searchable grid, with previews, campaign tags and rights expiry dates at a glance.

General Input Storage
General Input Database
JigsawStack
+1
App
Turn Mailjet email clicks into ranked HubSpot follow-ups

Twice a day we spot the people clicking your pricing and demo pages, create a follow-up task for their owner, and post a ranked recap to Slack.

Mailjet
HubSpot
Slack Bot
Agentic Task
Clean out the Looker dashboards and Looks nobody opens

One board showing every dashboard and Look with its folder, owner and favorite count, so you can find dead content and retire it safely.

Looker
Slack Bot
App
LiveKit live operations console for room moderation

Watch every live room on one screen, remove disruptive guests, start recordings and end stuck sessions without anyone opening a terminal.

LiveKit
Slack Bot
General Input Database
App
Wake up dormant Keap leads with a researched reason

Every Monday we find leads who never bought, research a real reason to reach out, and send a short personal email instead of another check in.

Keap
Slack Bot
Agentic Task
iMessage campaign console with pre-flight checks and delivery board

Build every text campaign in one screen: check who is actually reachable, see a realistic send plan, then watch delivery land row by row.

LoopMessage
Google Sheets
HubSpot
App

Stop uploading retargeting CSVs by hand.

Connect HubSpot, Meta Ads, and Slack once, and Geni keeps your Custom Audience in lockstep with your CRM every night at 2am.