HubSpot leads sync — what flows

Last updated: May 19, 2026

HubSpot leads sync — what flows

Wevion lead captures (from ad campaigns) sync to HubSpot Contacts in real time via an outbox + queue pattern. Attribution data (source campaign, UTM, first/last touch) lands in wevion_* custom properties. Ad-clicks and conversions logged in the HubSpot contact timeline via event sink. Bulk historical sync via reverse ETL.

Who is this for

Admins setting up HubSpot integration, anyone wondering "why is my Wevion lead not in HubSpot yet" or "what attribution data does HubSpot get".

The lead-to-HubSpot pipeline

1. User clicks ad on Meta/Google/TikTok
   ↓
2. Lands on landing page → fills lead form
   ↓
3. Wevion pixel/CAPI fires Lead event (browser + server)
   ↓
4. Wevion creates lead record in DB
   ↓
5. Outbox table queues HubSpot sync (apps/backend/src/services/hubspot-outbox.service.ts)
   ↓
6. SQS worker (apps/backend/src/sqs/workers/sync/hubspot-sync.worker.ts) processes
   ↓
7. HubSpot Contacts API → Contact created or updated
   ↓
8. Event sink (apps/backend/src/services/event-sinks/hubspot-event-sink.service.ts)
   logs ad-click + conversion to HubSpot timeline
   ↓
9. HubSpot Contact visible in HubSpot CRM with full attribution

Typical end-to-end latency: < 60 seconds.

Default property mapping

Wevion auto-maps these fields:

Wevion lead field

HubSpot Contact property

HubSpot type

Email

email

Standard

First name

firstname

Standard

Last name

lastname

Standard

Phone

phone

Standard

Company (if B2B)

company

Standard

Source platform

wevion_source_platform

Single-line text

Source campaign

wevion_source_campaign

Single-line text

Source ad set

wevion_source_adset

Single-line text

Source ad

wevion_source_ad

Single-line text

UTM source

wevion_utm_source

Single-line text

UTM medium

wevion_utm_medium

Single-line text

UTM campaign

wevion_utm_campaign

Single-line text

First touch timestamp

wevion_first_touch_at

DateTime

Last touch timestamp

wevion_last_touch_at

DateTime

Lead score (if set)

wevion_lead_score

Number

You can map additional Wevion custom fields to HubSpot properties via /connect/hubspot → Property mapping → Add custom mapping.

The wevion_* prefix

All Wevion-created HubSpot properties use the wevion_* prefix. This:

  • Avoids collision with HubSpot default properties

  • Avoids collision with other integrations (Marketo, Salesforce sync, etc.)

  • Provides clear ownership / audit trail

  • Allows safe deletion (filter by prefix to clean up)

Important: post-PR #588 rebrand (April 2026), all properties moved from adrow_* to wevion_*. If you have old adrow_* properties:

  • They're kept for backward compatibility

  • Wevion writes to wevion_* going forward

  • Migrate (via support) when you're ready: backfill wevion_* from adrow_*, then archive adrow_* properties

Custom property setup

For custom fields not in the default map:

Step 1: Create the property in HubSpot

  • HubSpot Settings → Properties → Create property

  • Object: Contact (most common)

  • Group: pick existing group or create "Wevion" group

  • Type: match Wevion's data type (text, number, datetime, etc.)

  • Internal name: wevion_my_custom_field (use the wevion_ prefix)

Step 2: Map in Wevion

  • /connect/hubspot → Property mapping → Add

  • Wevion field: pick from dropdown

  • HubSpot property: pick the new property you created

  • Save

Step 3: Sync going forward

New leads include the custom field; existing contacts updated via reverse ETL if you trigger backfill.

Reverse ETL (bulk historical sync)

For initial population or recovery:

  • /connect/hubspot → Reverse ETL panel

  • Pick date range (e.g. last 12 months)

  • Pick properties to include (default: all mapped)

  • Click Start backfill

  • Service: apps/backend/src/services/hubspot-reverse-etl.service.ts

  • Bulk-writes to HubSpot with rate-limit awareness (chunked to avoid HubSpot's 100 req/10s limit)

Use cases:

  • Just connected HubSpot — backfill all historical leads

  • New custom property mapping — backfill to populate for existing contacts

  • Post-rebrand adrow_*wevion_* migration

Event timeline integration

Wevion-tracked events appear on the HubSpot contact's timeline:

Wevion event

Timeline entry

Ad clicked

"Clicked ad: [Campaign Name] on [Platform]"

Landing page viewed

"Viewed: [Page URL]"

Lead captured

"Submitted form: [Form/Page]"

Conversion (e.g. Purchase)

"Converted: [Event Name] - [Value]"

Email opened (if marketing email integration on)

"Opened: [Email Subject]"

Implemented via apps/backend/src/services/event-sinks/hubspot-event-sink.service.ts.

This gives HubSpot users a unified view of the user's full ad journey + offline activity, useful for sales follow-up + lead scoring.

Bidirectional sync (limited)

Currently Wevion-side syncs:

  • Deal stage updates (HubSpot → Wevion): when a deal stage changes in HubSpot (e.g. Qualified → Won), Wevion can update lead status + attribution for closed-loop reporting

  • Configure at /connect/hubspot → Bidirectional sync

Future bidirectional features on roadmap (contact activity sync, deal value sync).

Rate limiting + throttling

HubSpot enforces 100 requests / 10 seconds per OAuth app. Wevion:

  • Queues writes via outbox pattern (decouples lead capture from sync)

  • Batches when possible (HubSpot batch API for bulk contact create/update)

  • Auto-throttles to stay under limits

  • Retries on transient errors with exponential backoff

You shouldn't hit rate limits in normal use. Heavy lead volumes (10k+/day): contact support for elevated quota arrangement with HubSpot.

What you'll see

Healthy sync:

  • /connect/hubspot shows last sync timestamp recent

  • Outbox queue (admin view) low / zero (events flushed quickly)

  • HubSpot Contacts show recent Wevion-sourced contacts with wevion_* properties populated

  • Timeline entries appearing on contacts within minutes of ad events

Common issues

  • Lead in Wevion but not in HubSpot after 5 min: check outbox queue (admin view) for stuck events. Check connector card for errors.

  • Property mapping not working: HubSpot property doesn't exist (must create in HubSpot first) or wrong data type. Verify HubSpot Settings → Properties.

  • Duplicate contacts in HubSpot: same email created twice — HubSpot's dedup didn't match. Check HubSpot Contacts → Settings → Duplicate management.

  • Timeline events not appearing: event sink may need re-init. Contact support to verify.

  • Reverse ETL stuck: large batch (100k+ contacts). Patience; if > 24h contact support.

  • Legacy adrow_* properties still being written: shouldn't happen post-rebrand. Verify Wevion version + contact support if observed.

Related