# Reconcile Customer.io profiles against your HubSpot records

> See which people in a Customer.io segment have blank or outdated personalization fields, then copy the right values over from HubSpot in bulk.

- Workflow type: app
- Services: Customer.io, HubSpot
- Categories: Marketing, Operations
- Published: 2026-08-17

## What it does

- Pick any Customer.io segment and page through everyone in it, matched to their HubSpot record by email address.
- Shows one row per person comparing the fields you personalize on, with anything blank or disagreeing with HubSpot highlighted.
- Gives you a summary bar for the share of the segment missing each field, plus filters for only mismatches or only blanks.
- Lets you accept the HubSpot value for one person or approve a whole page at once, with a preview of every change before anything saves.

## What you'll need

- A Customer.io account with at least one saved segment
- A HubSpot login that can view contacts and companies
- Email addresses stored on both sides, since that is how people get matched
- Agreement on which fields you personalize on, such as first name, company, plan and lifecycle stage

## Prompt

Build an internal app for our marketing ops team: a profile drift desk where we reconcile the people in a Customer.io segment against their HubSpot records once a week. Personalization breaks silently when traits go stale or empty, so the job of this app is to show, in one table, every person whose personalization fields are blank in Customer.io or disagree with HubSpot, and to let me fix them in place. HubSpot is the source of truth and Customer.io is the only system we write to.

Segment picker. When the app opens, load the workspace's segments with Customer.io List Segments and let me choose one. Then page through its members with Get Segment Membership, which is cursor based: each response carries a next token that you pass back as the start parameter on the following call. Work one page at a time, around 100 people per page, instead of pulling an entire segment into memory.

Matching. For the people on the current page, look each one up in HubSpot by email. Use Batch Read Contacts with email as the id property, which accepts up to 100 records per request, so one page of the segment costs one request. For anyone the batch does not resolve, fall back to Search Contacts on the email address, and use Get Contact when I refresh a single row. Note that a Customer.io person carries three identifier types (a customer supplied id, email, and the auto generated cio_id) and the workspace designates one of them as primary. Use email as the HubSpot join key, but carry each person's primary identifier alongside it, because that is the value every write back has to key on.

The comparison table. One row per person, with a column for each field we personalize on: first name, last name, company, plan and lifecycle stage. Every field cell shows the Customer.io value beside the HubSpot value and carries a status of match, mismatch, blank in Customer.io, or missing in HubSpot. Highlight blanks and mismatches so they are obvious at a glance. Rows where HubSpot has no matching contact render as unmatched and offer no fix.

Summary bar. Across the top of the segment, show for each personalization field the share of the loaded segment that is blank in Customer.io, alongside counts of mismatches and unmatched people. Add filters for everything, only mismatches, only blanks, and only unmatched, so I can work one problem at a time.

Fixes. On any row, let me accept the HubSpot value for a single field or for the whole row, and write it back with Customer.io Identify Person, keyed on that person's primary identifier and sending only the fields I accepted. Identify Person goes through the Pipelines (CDP) API, which returns HTTP 200 even for invalid payloads unless strict mode is requested, so always request strict mode and confirm each save individually. Show a saved or failed state per row with the actual error rather than assuming the write worked.

Company attach. Where the matched HubSpot contact carries a company (the company name and associated company id on the contact record), offer to attach that person to the matching company object in Customer.io using Add Person to Group. Show which group the person will be attached to before the write runs.

Bulk approve. Give me a button to approve every pending fix on the current page at once. Before anything saves, open a preview that lists exactly what will be written: person, field, old value, new value, and any company attachments. Nothing is written until I confirm inside that preview. Run the writes with per record results so a partial failure is visible rather than swallowed, and leave failed rows in place so I can retry them.

Persistence and behavior. Remember which fields count as personalization fields as a configurable list, defaulting to first name, last name, company, plan and lifecycle stage, and remember the last segment I had open. Keep a log of every write the app makes, including who accepted it and when, so a bad bulk approve can be traced afterwards. Respect the rate limits on both sides: the Customer.io App API allows roughly 10 requests per second and HubSpot's search endpoint about 4 per second, so throttle paging and honor the Retry-After header when either side rate limits us.

## How to customize

- Change which fields count as personalization fields, so the table tracks what your emails actually use
- Decide whether HubSpot always wins, or leave certain fields out of the comparison entirely
- Adjust how many people load per page and which filter the desk opens on

## FAQ

### Does this change anything in HubSpot?

No. HubSpot is read only here. It is treated as the source of truth, and every change the app makes is written back to Customer.io.

### What happens if someone in the segment is not in HubSpot?

The row is marked as unmatched and no fix is offered for it. Those people still count in the summary bar, so you can see how much of the segment your CRM does not cover.

### Can I see what will change before it saves?

Yes. Approving a page opens a preview listing every person, every field, the old value and the new value. Nothing is written until you confirm.

### We use a customer ID rather than email in Customer.io. Will this still work?

Yes. Email is only used to find the matching HubSpot record. Updates are written using whichever identifier your Customer.io workspace treats as primary.

### How many people can it handle at once?

It works a page at a time, up to 100 people per page, so large segments stay responsive. You page through the segment rather than loading all of it at once.

Use this prompt in General Input: https://www.generalinput.com/prompts/reconcile-customerio-profiles-against-your-hubspot-records