Cloud · Serverless Technical Owner Production

Multi-Venue Serverless
Integration Platform

Designed and owned a serverless integration platform on AWS for a 20+ brand hospitality portfolio running on a single HubSpot CRM. I built and ran multiple production integrations across scheduled and webhook patterns, managed live sync state in DynamoDB, led incident response on two production data bugs, and cut stale infrastructure out of the account.

20+
Brands on one HubSpot portal and one AWS account
6
Production serverless integrations built and owned
4,600+
Marina contacts synced automatically every 12 hours
Role
Developer Co-op · Technical Owner
Timeline
Jan – Jul 2026
Organization
Boston-based hospitality group
Stack
AWS Lambda · EventBridge · API Gateway · DynamoDB · Secrets Manager · IAM · CloudWatch · HubSpot / Tripleseat / Molo / D-Tools APIs
Platform architecture · Two ingress patterns, one HubSpot portal
Scheduled Webhook DynamoDB
EventBridge Scheduler scheduled · rate(12h) + 5-min polls API Gateway · oo822zhp14 webhook · POST /prod molo sync EventBridge · 12h tripleseat sync API Gateway · deals d-tools sync API Gateway · contacts hubspot dedup on-demand scan DynamoDB MD5 change-detection HubSpot CRM one portal · 44586838 · 20+ brands S3 dedup report .xlsx Isolated Lambdas · Secrets Manager · IAM least-privilege · CloudWatch on every function
Context

One HubSpot portal, one AWS account, 20+ brands.

The group runs 20+ hospitality, marine, and real-estate brands on a single HubSpot CRM and a single AWS account in us-east-1. Each brand ran different source systems, and none of them fed HubSpot on their own.

I was the technical owner of the integration layer that connected them. Over the co-op I built and ran six production serverless integrations, moving contact and deal data between HubSpot and three source systems (Molo, Tripleseat, and D-Tools) and running a deduplication engine over HubSpot's own 56k+ contacts. No other engineer was on the team, so I owned scoping, build, deploy, incident response, and the handoff documentation.

Architecture

Two ingress patterns, chosen per source system.

Every integration lands data in the same HubSpot portal, but the right way to get it there depends on the source. I used two patterns, chosen per system.

Scheduled · EventBridge + DynamoDB
Molo has no public API, so its sync is pull-based. EventBridge triggers the flyingbridge-molo-hubspot-sync Lambda every 12 hours. The Lambda pulls contacts and vessels for three marinas, compares each record against state in DynamoDB, and pushes only what changed. The two Tripleseat lead syncs, for Harbor Lights and Basecamp, use the same scheduled pattern on a 5-minute poll.
Webhook · API Gateway
Tripleseat and D-Tools both emit webhooks, so those syncs are event-driven and near real time. The source system posts to API Gateway, which proxies the raw event to a Lambda: harborlights-tripleseat-deal-sync turns Tripleseat bookings into HubSpot deals, and vineyard-dtools-hubspot-sync turns D-Tools clients into HubSpot contacts. The Lambda fetches the full record, then creates or updates in HubSpot.
On-demand · scan to S3
The deduplication engine runs on demand against the full contact base, categorizes duplicate groups, and writes a color-coded report to S3. It defaults to a dry-run gate, so the merge API is never called unless a run is explicitly flipped to live, because merges are irreversible.
State & Merge Logic

Idempotent syncs that never clobber a record.

The scheduled Molo sync is the most stateful integration. A third-party developer wrote the reverse-engineered Molo auth client; I built everything around it: the AWS orchestration, the DynamoDB state design, the change-detection, and the merge logic.

Each run computes an MD5 hash of a contact's synced fields and compares it to the last hash stored in DynamoDB. Unchanged contacts are skipped, so a run that pulls ~4,600 contacts writes only the handful that actually changed. That keeps the sync idempotent and well inside HubSpot's rate limits.

Writes go through a three-tier merge so an automated sync never overwrites human-entered data.

01
Semicolon-append, never overwrite
Multi-value fields like hs_all_assigned_business_unit_ids and marina_brand_interest get the new value appended to whatever is already there, so a contact keeps every brand and interest it has ever had.
02
Only set if empty
Ownership and lifecycle fields such as hubspot_owner_id, hs_lead_status, and lifecyclestage are written only when the field is blank, so the sync never reassigns a contact a human already touched.
03
Always overwrite
Source-of-truth fields like name, email, phone, vessel data, and the Molo record IDs always take the latest value from Molo.
Code: change-detection core

What each run does.

The core of each Molo run is small. Hash the record, compare it against DynamoDB, skip if nothing changed, otherwise merge into HubSpot and persist the new state for next time.

lambda_function.py · Molo change-detection Python 3.12
def sync_contact(contact, table, hubspot): # skip unchanged contacts: hash fields, compare to DynamoDB data_hash = md5(serialize(contact)).hexdigest() prior = table.get_item(Key={"molo_contact_id": contact["id"]}) if prior.get("Item", {}).get("data_hash") == data_hash: return "unchanged" # three-tier merge: append / set-if-empty / overwrite hubspot.create_or_update_contact(contact["email"], build_props(contact)) # persist state so the next 12h run can diff against it table.put_item(Item={ "molo_contact_id": contact["id"], "data_hash": data_hash, "last_synced": now_iso(), }) return "synced"

The same append and set-if-empty merge rules are the default for every integration on the platform, not just Molo. An automated write should never be able to destroy data a person entered by hand.

Incident Response

Two production incidents, two fixes that stuck.

01
Molo: 7 contacts overwritten during early testing
Before the merge logic existed, an early Molo test run overwrote Brands, Lead Status, and Lifecycle Stage on 7 real contacts. I restored all 7 by hand from HubSpot property history, then built the three-tier merge so a sync can never blanket-overwrite a contact again. That incident is why the merge logic exists, and it became the default pattern for every integration after it.
02
Harbor Lights: duplicate deals from a webhook race
Harbor Lights staff reported duplicate deals in HubSpot. The root cause was a race condition. Tripleseat fired UPDATE_BOOKING and UPDATE_EVENT webhooks at the same time, both Lambda invocations searched HubSpot for the deal, both found nothing, and both created one. I fixed it at the source by removing the Update Event trigger from the webhook, so exactly one webhook fires per booking action and the dedup search stays authoritative.
Infrastructure Hygiene

Neutralized the legacy stack before scaling.

A previous vendor had left two legacy Tripleseat sync Lambdas, tripleseat-prod-tripleseatWebhook and tripleseat-prod-queueWorker, running in the account. Once my replacement was confirmed working, I disabled both by setting Reserved Concurrency to 0 rather than deleting them, so they stopped executing but stayed recoverable pending owner sign-off.

Across the platform I kept the operational surface tight. Each integration is an isolated Lambda with its own credentials and environment, IAM roles are scoped to least privilege (for example secretsmanager:GetSecretValue limited to the waterside/* prefix), credentials move toward Secrets Manager, and every function logs to CloudWatch for debugging.

Timeline

What shipped, in order.

Mar 2026
Scoped the Molo marina sync. Blocked on Molo API access, which the vendor refused for smaller accounts.
Apr 2026
Built the Molo sync with DynamoDB change-detection and the three-tier merge across three marinas. Caught and fixed the 7-contact overwrite incident. Ran a full sync of ~4,600 contacts and enabled the EventBridge 12-hour schedule. Disabled the two legacy Tripleseat Lambdas.
May 2026
Took the Harbor Lights Tripleseat deal sync live on API Gateway webhooks. Diagnosed the duplicate-deal race condition and fixed it in the webhook configuration.
Jun 2026
Shipped the D-Tools contact sync as a webhook into HubSpot, and built the deduplication engine, which scanned 56,676 contacts and categorized duplicate groups for a sales-team review.
Jul 2026
Wrote the platform handoff: a master index plus per-integration deep dives covering architecture, credentials, and open items for the next developer.
Next Project
NUCEE: Data Platform & Impact Infrastructure