Why this automation is needed
Native HubSpot workflows often struggle with complex multi-object traversals, conditional lookback attribution windows, custom phone format standardizations, and custom scoring calculations.
With Workflood, this recipe runs in a secure gVisor container sandbox with pre-authenticated HubSpot SDK injection, avoiding HubSpot Data Hub execution limits, memory bottlenecks, and per-seat fees.
Action Source Code (Python)
def main(input_data, hs_client, http_client, secret, console):
raw = input_data[0] if isinstance(input_data, list) and input_data else (input_data or {})
marketing_event_id = raw.get('marketingEventId') or raw.get('marketing_event_id') or raw.get('eventId')
contact_id = raw.get('contactId') or raw.get('contact_id') or raw.get('objectId') or raw.get('hs_object_id')
email = raw.get('email')
attendance_state = raw.get('attendanceState') or raw.get('attendance_state')
# Only link company if the contact actually attended
if attendance_state and attendance_state != 'ATTENDED':
return {
"associated": False,
"marketingEventId": marketing_event_id,
"message": f"Skipped: contact attendanceState is '{attendance_state}' (not ATTENDED)"
}
# Trigger-agnostic fallback if no inputs given
if not contact_id and not email and not marketing_event_id:
recent = hs_client.crm.contacts.basic_api.get_page(limit=25, properties=['email'])
linked = 0
for c in (recent.results or []):
c_assoc = hs_client.crm.associations.v4.basic_api.get_page('contacts', c.id, 'companies', limit=1)
comp_id = c_assoc.results[0].to_object_id if c_assoc.results else None
if not comp_id:
continue
e_assoc = hs_client.crm.associations.v4.basic_api.get_page('contacts', c.id, 'marketing_events', limit=5)
for e in (e_assoc.results or []):
hs_client.crm.associations.v4.basic_api.create(
'companies', comp_id, 'marketing_events', e.to_object_id,
[{"associationCategory": "HUBSPOT_DEFINED", "associationTypeId": 14}]
)
linked += 1
return {"associated": linked > 0, "processedCount": len(recent.results or []), "message": f"Trigger-agnostic scan: linked {linked} company-to-event associations"}
# Resolve contact ID if email was provided
if not contact_id and email:
contacts = hs_client.crm.contacts.basic_api.get_page(
limit=1,
properties=['email'],
filters=[{ "value": email, "propertyName": "email", "operator": "EQ" }]
)
if contacts.results:
contact_id = contacts.results[0].id
if not contact_id:
raise ValueError("Could not determine contactId (provide contactId or valid email)")
# Fetch associated companies for contact
assocs = hs_client.crm.associations.v4.basic_api.get_page(
from_object_type='contacts',
object_id=contact_id,
to_object_type='companies',
limit=5
)
if not assocs.results:
return {
"associated": False,
"contactId": contact_id,
"marketingEventId": marketing_event_id,
"message": f"No associated company found for contact ID {contact_id}"
}
company_id = assocs.results[0].to_object_id
if not marketing_event_id:
e_assocs = hs_client.crm.associations.v4.basic_api.get_page('contacts', contact_id, 'marketing_events', limit=5)
if not e_assocs.results:
return {"associated": False, "companyId": company_id, "contactId": contact_id, "message": f"No events on contact {contact_id}"}
marketing_event_id = e_assocs.results[0].to_object_id
# Create association between Company and Marketing Event
hs_client.crm.associations.v4.basic_api.create(
from_object_type='companies',
object_id=company_id,
to_object_type='marketing_events',
to_object_id=marketing_event_id,
types=[{"associationCategory": "HUBSPOT_DEFINED", "associationTypeId": 14}]
)
return {
"associated": True,
"companyId": company_id,
"contactId": contact_id,
"marketingEventId": marketing_event_id,
"message": f"Successfully associated Company {company_id} to Marketing Event {marketing_event_id}"
}
How to deploy in Workflood
Connect your HubSpot Portal
Add your HubSpot Private App token once. Workflood securely encrypts it via libsodium XChaCha20 and injects pre-authenticated SDK clients.
Create Action from Template
Select this recipe in Workflood. Edit variables, customize logic in Monaco editor, or execute dry runs with sample JSON payloads.
Trigger via Webhook or Cron
Trigger from standard HubSpot workflows via Webhook action, or schedule recurring batch syncs with cron expressions and timezone handling.
Deploy in Workflood
Single Portal is $79/mo flat with unlimited team seats, 50,000 runs/mo, and no per-seat costs.
