Skip to main content
Your sales team is working a deal. Your marketing email goes to that person anyway. Fixing that means Brew needs to know what your CRM knows. If you have both connected to an MCP assistant, you can sync the fields you filter on, with no code. This page covers a one-directional sync, CRM into Brew. It works with any MCP-connected CRM, including Attio and HubSpot. Using Attio? See Sync Brew and Attio Over MCP for its list-entry model and the engagement writeback loop.
Every marketing recipient must have opted in to hear from you. Importing or syncing contact data into Brew is not permission to email it. See What You Can Send.
This syncs CRM context onto contacts who already opted in. It is not a way to import a prospecting list. A CRM contact from a sales conversation has not consented.

Before You Start

You need:
  • A Brew brand connected to your assistant, with opted-in contacts already in it
  • A CRM connected to the same assistant, with read access to contacts
  • Contacts that exist in both systems under the same email address
Matching is on email. A contact in one system and not the other won’t sync.

1. Check What Brew Already Has

Some fields are native. Don’t create custom fields that duplicate them. Ask your assistant:
List the custom fields in my Brew brand.

2. Create the Fields You’re Missing

Name each field after its source so nobody has to guess where the data came from.
Create these custom fields in Brew: crm_lifecycle_stage (string), crm_owner (string), crm_last_contacted (date), crm_record_id (string).
Brew normalizes names to camelCase. You declare the underscored name and get the camelCase one back:
The returned fieldName is what your audience filters use, not the name you typed. Field types are string, number, date, or bool. Declare anything date-like as date so comparisons work.

3. Pick Which CRM Properties to Sync

Only sync what you’ll actually filter on. Four fields cover most exclusion cases: Verify the internal property names before you build anything. Display labels and internal names differ in most CRMs.
Read the property definitions for contacts in [CRM] and show me the internal names and stored values for [your properties].
Some CRMs store dropdown values as internal codes or numeric IDs rather than the label you see. A filter written against the label matches nothing, so check the real stored values first.

4. Run the Sync

Get [your four properties] from [CRM] for every contact in [list or segment]. Update each matching Brew contact with those values. Tell me how many you updated and list any you couldn’t match.
Always ask for the count and the unmatched list. An assistant working a long list can skip rows without error. The count is your only proof it finished. Ask for the report in this shape so gaps are obvious:
Run a full sync across the whole group once before you trust any filter. After that, re-runs only need to cover what changed.

5. Build the Audience

Add filter conditions in the Brew UI, or ask your assistant:
Create a Brew audience called “Q3 nurture” where crmLifecycleStage is not opportunity, crmOwner is empty, and crmLastContacted is before 30 days ago.
Use the camelCase field names from step 2. Available operators: Audiences are saved filters, not frozen snapshots. They re-evaluate at send time against whatever was last synced. Creating one returns the match count immediately:
If that count is zero and you expected more, check your filter first. The usual cause is a value written against a display label instead of the stored one. See Create Audiences for what else you can filter on.

6. Verify

Change one thing in your CRM and re-run the sync. Check that the audience count moves by the number you expect, not just that it moved. Use two or three test contacts and confirm before you rely on it for a real send.

From MCP

Your Brew connection covers one brand or your whole organization, depending on what you picked when you connected. The CRM side may be workspace-scoped depending on the provider, so confirm you are pointed at the right brand before writing. On a brand connection, brand_id is optional. Omit it to use the bound brand, since supplying a different one fails with BRAND_SCOPE_MISMATCH rather than writing to the wrong place. On an organization connection, every brand-scoped tool requires brand_id, so get the id from list_brands and pass it on every call. See Authentication and Scoping.
  • list_contact_fields lists the brand’s contact fields, including built-in ones marked isCore: true, so you can see which custom fields already exist. Call this before creating anything. Built-in fields such as email can’t be created or deleted.
  • create_contact_field takes fieldName and fieldType. Creating a field that already exists with the same type is safe, it returns the existing definition. Field names can’t contain whitespace, path separators, or quotes.
  • save_contact takes an email and a fields map, one contact per call, and changes only the fields you pass. An address Brew doesn’t have fails with CONTACT_NOT_FOUND. There’s no batch update-only write (import_contacts_csv also creates contacts Brew doesn’t have), so work through a large list in chunks and report the running count.
  • save_contact with createIfMissing: true also creates the contact when it’s absent, and returns created: true|false. Use it only for contacts who opted in through your own signup or checkout, not to pull a CRM list into Brew.
Pass an idempotency_key, a fresh UUID per intended action, so a retry replays the original result instead of acting twice. The same key with a different payload returns a 409. See Idempotency. If a CRM property has no value for a contact, leave the Brew field unset. Don’t write an empty string. An unset field reads as “not synced yet”, while a blank one falsely reads as “synced, and empty.” The full tool catalog is in MCP Tools.

When the Sync Fails

What This Does and Doesn’t Do

Syncs when asked, not automatically. Nothing fires when a record changes in your CRM. Run the sync immediately before a send. A daily sync leaves up to 24 hours of drift. That’s long enough for a rep to open a deal at 9am and your 4pm send to email them anyway. One direction. This pulls CRM data into Brew. To write engagement back to the CRM, see Sync Brew and Attio Over MCP. Matches on email only. If your CRM stores multiple addresses per contact, decide which one is authoritative before syncing. Otherwise one person becomes two Brew contacts. Stores values, not flags. Write crmLifecycleStage = "customer", not isCustomer = true. Keeping the real value means un-suppressing costs nothing and new filters don’t need a re-sync. Empty fields may be invisible. Some CRMs omit unset properties from API responses entirely rather than returning null. An absent value is ambiguous. It can mean “never happened” or “this CRM isn’t tracking it.” Check that a real sample is populated before you filter on it. A recency filter built on an empty field won’t error. It will silently fail to suppress, and you’ll hear about it from a recipient.

Need Help?

Our team is ready to support you at every step of your journey with Brew. Choose the option that works best for you:

Search Documentation

Type in the “Ask any question” search bar at the top left to instantly find relevant documentation pages.

ChatGPT/Claude Integration

Click “Open in ChatGPT” at the top right of any page to explore it further with ChatGPT or Claude.