Skip to content

Recipe 02: Scheduled data sync (HubSpot → Postgres)

Goal: every hour, fetch the newest HubSpot leads and upsert them into a Postgres table, so reporting tools can query them.

Difficulty: Intermediate · Time: about 25 minutes · Connectors: HubSpot, Database


Prerequisites

  • A HubSpot credential with read access to contacts.
  • A Postgres database Flero can reach, with this table:
    CREATE TABLE hubspot_leads (
      hubspot_id  text PRIMARY KEY,
      email       text,
      first_name  text,
      last_name   text,
      company     text,
      synced_at   timestamptz DEFAULT now()
    );
  • At least one HubSpot contact with lifecycle stage Lead.

Finished workflow

Workflow: Scheduler to Find leads, Find leads to Loop, Loop to Upsert lead, Upsert lead to Loop, Loop to Post summary SchedulerTrigger on schedule Find leadsHubSpot CRMLoopIterate over dataUpsert leadDatabase Post summarySlack

The Slack summary on the done port is optional; it's added in the last step.


Step-by-step

1. Create the workflow

On the Workflows page, click Create Workflow and name it HubSpot leads hourly sync.

2. Add the Scheduler

Add a Scheduler trigger:

  • Type: Cron
  • Cron: 0 * * * * (the top of every hour)
  • Timezone: yours

3. Search HubSpot for leads

Add a HubSpot node and name it Find leads:

  • Credential: your HubSpot credential
  • Resource: Contact
  • Operation: Search
  • filterGroups:
    [{ "filters": [{ "propertyName": "lifecyclestage", "operator": "EQ", "value": "lead" }] }]
  • sorts: [{ "propertyName": "createdate", "direction": "DESCENDING" }]
  • properties: ["email", "firstname", "lastname", "company"]
  • limit: 100

The matching contacts are in {{ $node["Find leads"].output.data.results }}. Each one has an id and a properties object.

4. Loop over the results

Add a Loop node after Find leads:

  • Mode: array
  • Field to loop on: {{ $node["Find leads"].output.data.results }}

Because the value is a single expression, it resolves to the real list. Inside the loop body, the current contact is {{ $input.item }}.

5. Upsert each contact

Connect the Loop's loop output to a Database node named Upsert lead, and connect that node back into the Loop:

  • Mode: Remote, with your Postgres connection details
  • Read only: off
  • Operation: Insert
  • SQL:
    INSERT INTO hubspot_leads (hubspot_id, email, first_name, last_name, company)
    VALUES ($1, $2, $3, $4, $5)
    ON CONFLICT (hubspot_id) DO UPDATE SET
      email = EXCLUDED.email,
      first_name = EXCLUDED.first_name,
      last_name = EXCLUDED.last_name,
      company = EXCLUDED.company,
      synced_at = now()
  • Parameters:
    1. {{ $input.item.id }}
    2. {{ $input.item.properties.email }}
    3. {{ $input.item.properties.firstname }}
    4. {{ $input.item.properties.lastname }}
    5. {{ $input.item.properties.company }}

ON CONFLICT makes the sync safe to repeat: a contact seen twice updates its row instead of adding a second one.

6. Save and activate

Save, then set the workflow to Active in the editor toolbar's version menu. The Scheduler fires at the next hour.


Try it

  1. Click Run in the editor to run it once now.
  2. Watch the nodes turn green. The Loop runs the Database node once per contact.
  3. Query the table: SELECT * FROM hubspot_leads ORDER BY synced_at DESC;
  4. Run it again. The row count stays the same and synced_at moves forward.

Variations

Weekdays only

Cron: 0 * * * 1-5 (every hour, Monday to Friday).

Post a summary

Connect the Loop's done output to a Slack node that posts "HubSpot sync finished" to your ops channel.

More than 100 leads

HubSpot returns the next page cursor in {{ $node["Find leads"].output.data.paging.next.after }}. Pass it as after in a second search, or loop with a While node until no cursor comes back.

Alert on failure

Set the Database node's error mode to retry in Node Settings, so a brief connection drop does not fail the run. See Retries & error handling.


Troubleshooting

Symptom Likely cause Fix
Loop runs zero times No contacts match the filter Check that some contacts have lifecycle stage Lead
[EXPR-ERROR: Node "Find leads" not found …] The HubSpot node has a different name Rename the node to Find leads, or update the expressions
Database node refuses the insert Read only is on Turn Read only off
Columns are empty The property is missing from properties Add it to the search's properties list

Next


Found something out of date? This page lives in the Flero docs content set.