Skip to main content

HubSpot to CDP Integration Guide

This guide describes how to push contact data from HubSpot to the CM.com Customer Data Platform (CDP) using a HubSpot Workflow with a Python Custom Code action. It is based on the provided Quick Setup document and expands it with best practices, validation, and troubleshooting.

Overview​

You will build a HubSpot workflow that enrolls contacts on form submission, executes a Python 3.9 Custom Code action, and posts a structured payload to CM.com CDP’s Events API using your tenant, event, and product token.

Prerequisites​

  • HubSpot Operations Hub Professional or Enterprise (required for Custom Code actions).
  • A HubSpot form that captures at least email, first name, last name, and mobile phone (you can extend fields as needed).
  • Access to CM.com CDP with a valid tenant ID, event ID, and product token (for the Events API).
  • Ability to map HubSpot contact/company properties to the payload schema used by CDP.

Architecture at a Glance​

  • Trigger: HubSpot Workflow (Form Submission).
  • Transformation: Python Custom Code action formats HubSpot properties into CDP event payload.
  • Transport: HTTPS POST to CM.com CDP Events API with product token authentication.

Step-by-Step Setup​

1) Build the workflow and enroll from a form​

  1. In HubSpot: Automation → Workflows → Create workflow from scratch.
  2. Add enrollment trigger: Form submission, then select the form you want to use.
  3. Optionally enable re-enrollment if repeat submissions should trigger the workflow again.

2) Add a Custom Code action (Python 3.9)​

  1. Inside the workflow, click “+” and choose Custom code.
  2. Set Language = Python 3.9.
  3. Add the following Properties to include in code (names must match exactly):
    • email → map to {{ contact.email }}
    • firstname → {{ contact.firstname }}
    • lastname → {{ contact.lastname }}
    • company → {{ company.name }}
    • mobilephone → {{ contact.mobilephone }}
    • recordID → an internal ID (e.g., {{ company.hs_object_id }})

3) Paste and configure the Python code​

Update placeholders for tenant, event, and product token before deploying.

import requests
import json


def main(event):
# --- 1. Extract input from the HubSpot workflow ---
email = event["inputFields"].get("email")
firstname = event["inputFields"].get("firstname")
lastname = event["inputFields"].get("lastname")
company = event["inputFields"].get("company")
mobilephone = event["inputFields"].get("mobilephone")
recordID = event["inputFields"].get("recordID")

# --- 2. Prepare the payload ---
payload = [{
"firstName": firstname,
"lastName": lastname,
"companyName": company,
"email": email,
"mobilePhoneNumber": mobilephone,
"phoneNumber": mobilephone, # duplicated intentionally if CDP expects either field
"recordIDCompany": recordID,
"event": "TM13", # replace with your event name/code if needed
"SMSOptIn": True
}]

# --- 3. Configure request details ---
# Replace placeholders with your CM values
tenant_id = "<YOUR_TENANT_ID>"
event_id = "<YOUR_EVENT_ID>"
product_token = "<YOUR_CM_PRODUCT_TOKEN>"

url = (
f"https://api.cdp.cm.com/events/v1.0/tenants/{tenant_id}/events/{event_id}"
"?Content-Type=application%2Fjson"
f"&X-CM-PRODUCTTOKEN={product_token}"
)

headers = {
"Content-Type": "application/json",
"X-CM-PRODUCTTOKEN": product_token
}

# --- 4. Send POST request ---
try:
response = requests.post(url, headers=headers, data=json.dumps(payload), timeout=10)
response.raise_for_status()
result = response.json() if response.content else {"status": response.status_code}
except Exception as e:
result = {"error": str(e)}

# --- 5. Return output for subsequent steps ---
return {
"outputFields": {
"email": email,
"cm_response": json.dumps(result)
}
}

Security note on secrets: If your token naming policy allows, use HubSpot Managed Secrets and read via environment variables. Some CM token names include dashes that may not be allowed as HubSpot secret names; adjust naming or store under an allowed alias if needed.

4) Test the action​

  1. Use the Test button in the Custom Code action to run against a sample contact.
  2. Inspect logs and the cm_response output to verify a 2xx response and expected body.
  3. Once validated, turn the workflow on.

Payload Mapping Reference​

HubSpot Property (source)Custom Code Input NameCDP Payload Field
contact.emailemailemail
contact.firstnamefirstnamefirstName
contact.lastnamelastnamelastName
company.namecompanycompanyName
contact.mobilephonemobilephonemobilePhoneNumber, phoneNumber
company.hs_object_id (example)recordIDrecordIDCompany

Best Practices​

  • Validation: In code, .get() inputs and consider null-safe defaults (e.g., empty strings) to avoid KeyErrors.
  • Timeouts and retries: Use a reasonable timeout (10–15s). If your volumes are high or network is flaky, add simple retry logic with backoff for 5xx responses.
  • Data quality: Normalize phone numbers to E.164 and trim whitespace on string fields before sending.
  • Observability: Return cm_response in outputFields to make the downstream debug easier; optionally log response.status_code and a short body snippet.
  • Security: Prefer HubSpot Managed Secrets where feasible. If naming constraints conflict, store tokens under an allowed alias or rotate frequently.

Extending the Integration​

  • Add fields: Include more Properties to include in code (e.g., city, country, lifecycle stage) and map them in the payload.
  • Conditional logic: Branch on consent flags or form choices to set event types or opt-in booleans.
  • Upserts vs. events: Confirm with CM.com whether your event triggers identity resolution, and whether additional identifiers (e.g., external IDs) should be sent for better matching.

Troubleshooting​

  • 401/403: Verify X-CM-PRODUCTTOKEN is valid, active, and matches the tenant.

  • 404: Check tenant_id and event_id; confirm the event exists in CDP.

  • 415/400: Ensure Content-Type header and payload schema match CDP expectations; validate JSON structure.

  • Confirm the form is the enrollment trigger and the contact submitted that specific form.

  • Check re-enrollment if multiple submissions are expected to trigger repeatedly.

  • Ensure property names in HubSpot match your Custom Code input names exactly.

  • Validate mapping in the Payload Mapping Reference; align field casing and spelling (e.g., firstName vs firstname).

Operational Checklist​

  • Create the HubSpot workflow with Form Submission enrollment.
  • Add Custom Code action and configure required input properties (email, firstname, lastname, company, mobilephone, recordID).
  • Paste Python code and replace &lt;YOUR_TENANT_ID>, &lt;YOUR_EVENT_ID>, and &lt;YOUR_CM_PRODUCT_TOKEN>.
  • Test with a sample contact; verify a successful 2xx response and expected payload processing in CDP.
  • Enable the workflow and monitor logs/outputs for the first week of operation.