Unified.to
All articles

How to Sync Leads into Audiences and Trigger Campaigns Across Marketing Platforms using a Unified Marketing API


April 10, 2026

If you need to integrate with tools like HubSpot, Mailchimp, Klaviyo, ActiveCampaign, or Customer.io, you need a consistent way to sync leads, assign them to audiences, trigger campaigns, and retrieve performance data—without building separate integrations for each API.

This guide shows how to implement that workflow using a unified Marketing API.

Use case: sync leads → assign audiences → trigger campaigns → measure performance

Build a marketing integration that:

  • syncs leads into marketing platforms
  • assigns leads to audiences (lists)
  • creates campaigns targeting those audiences
  • retrieves campaign performance metrics

All through a single normalized API.

Core objects

ObjectPurpose
Listaudience definition and targeting
Memberlead/contact record
Campaignemail or automation targeting lists
Reportcampaign performance metrics

Step 1: List audiences

Before syncing leads or creating campaigns, retrieve available lists.

const lists = await sdk.martech.listMartechLists({
  connectionId,
  limit: 100,
  offset: 0,
  sort: 'updated_at',
  order: 'asc',
  fields: ['id', 'name', 'is_active'],
});

Each list represents an audience that can be:

  • assigned to members via list_ids
  • targeted by campaigns via list_ids

Step 2: Create a member (lead)

Create a new lead using normalized member fields.

const member = await sdk.martech.createMartechMember({
  connectionId,
  martechMember: {
    first_name: 'Jane',
    last_name: 'Doe',
    emails: [
      {
        email: 'jane@example.com',
        type: 'WORK',
      },
    ],
    status: 'SUBSCRIBED',
  },
  fields: ['id', 'emails', 'status', 'list_ids'],
});

Writable fields include:

  • emails
  • first_name, last_name
  • status
  • list_ids
  • tags

Step 3: Assign the member to audiences

Audience membership is handled directly on the member.

const updatedMember = await sdk.martech.updateMartechMember({
  connectionId,
  id: member.id,
  martechMember: {
    list_ids: ['list_123'],
    tags: ['trial-user'],
    status: 'SUBSCRIBED',
  },
  fields: ['id', 'list_ids', 'tags', 'status'],
});

This step:

  • attaches the lead to one or more lists
  • sets subscription state
  • adds segmentation metadata via tags

There is no separate membership endpoint required.

Step 4: Create a campaign targeting those audiences

Campaigns are created with list_ids as the targeting mechanism.

const campaign = await sdk.martech.createMartechCampaign({
  connectionId,
  martechCampaign: {
    name: 'Welcome Campaign',
    type: 'regular',
    status: 'SCHEDULED',
    list_ids: ['list_123'],
    subject_line: 'Welcome to the platform',
    preview_text: 'Here's what to do next',
    from_name: 'Acme',
    from_email: 'team@acme.com',
    reply_to_email: 'support@acme.com',
    send_at: new Date().toISOString(),
  },
  fields: [
    'id',
    'status',
    'list_ids',
    'subject_line',
    'send_at',
  ],
});

Campaign status values include:

  • DRAFT
  • SCHEDULED
  • SENDING
  • SENT
  • CANCELLED
  • PAUSED
  • ARCHIVED

Step 5: Retrieve campaign state

Campaigns should be treated as stateful objects.

const latestCampaign = await sdk.martech.getMartechCampaign({
  connectionId,
  id: campaign.id,
  fields: ['id', 'status', 'updated_at'],
});

You can also retrieve campaigns incrementally:

const campaigns = await sdk.martech.listMartechCampaigns({
  connectionId,
  updated_gte: lastSyncAt,
  limit: 100,
  offset: 0,
  sort: 'updated_at',
  order: 'asc',
  fields: ['id', 'status', 'list_ids', 'updated_at'],
});

Step 6: Retrieve campaign performance (reports)

Reports provide normalized performance metrics across platforms.

const reports = await sdk.martech.listMartechReports({
  connectionId,
  campaign_id: campaign.id,
  limit: 100,
  offset: 0,
  sort: 'updated_at',
  order: 'asc',
  fields: [
    'campaign_id',
    'emails_sent',
    'unsubscribed',
    'hard_bounces',
    'soft_bounces',
    'opens_total',
    'unique_opens',
    'open_rate',
    'clicks_total',
    'unique_clicks',
    'click_rate',
  ],
});

Available metrics include:

  • delivery (emails_sent, bounces)
  • engagement (opens_total, clicks_total)
  • rates (open_rate, click_rate)
  • list impact (unsubscribed)

You can filter reports by:

  • campaign_id
  • list_id
  • start_gte
  • end_lt

Step 7: Maintain state incrementally

All major endpoints support incremental sync via updated_gte.

Example for members:

const members = await sdk.martech.listMartechMembers({
  connectionId,
  updated_gte: lastSyncAt,
  limit: 100,
  offset: 0,
  sort: 'updated_at',
  order: 'asc',
  fields: ['id', 'emails', 'list_ids', 'status', 'updated_at'],
});

Use the same pattern for:

  • campaigns
  • lists
  • reports

This avoids full re-syncs and keeps data current.

Step 8: Webhooks and polling strategy

Webhook support varies by object and integration.

Member

  • created and updated events are widely supported
  • use webhooks as the primary update mechanism
  • use polling (updated_gte) as fallback

List

  • partial webhook support
  • rely on polling for consistency

Campaign

  • inconsistent webhook support
  • treat polling as the source of truth

Report

  • retrieved via API
  • use polling for updates

End-to-end flow

const lists = await sdk.martech.listMartechLists({ connectionId });

const member = await sdk.martech.createMartechMember({
  connectionId,
  martechMember: {
    emails: [{ email: 'jane@example.com' }],
    status: 'SUBSCRIBED',
  },
});

await sdk.martech.updateMartechMember({
  connectionId,
  id: member.id,
  martechMember: {
    list_ids: ['list_123'],
  },
});

const campaign = await sdk.martech.createMartechCampaign({
  connectionId,
  martechCampaign: {
    name: 'Welcome Campaign',
    list_ids: ['list_123'],
    status: 'SCHEDULED',
  },
});

const reports = await sdk.martech.listMartechReports({
  connectionId,
  campaign_id: campaign.id,
});

Key takeaways

  • Audience membership and campaign targeting both use list_ids
  • Lead sync and audience assignment are part of the same write flow
  • Campaigns must be treated as stateful objects
  • Reports provide a normalized performance layer across platforms
  • Member updates can be handled with webhooks, while campaigns and reports require polling
  • Incremental sync with updated_gte is required for consistency across integrations

This structure lets you support multiple marketing platforms with one implementation, while maintaining consistent lead data, audience targeting, and campaign performance tracking.

Start your 30-day free trial

Book a demo

All articles