---
title: "How to Sync Customer Audiences from Any CDP with a Unified API"
img: https://unified.to/images/logo.svg
date: 2026-08-04T18:25:00.000Z
tag: Product
description: "Your product can read a customer's audiences from whichever customer data platform they run, through one connection and one method call...."
url: "https://unified.to/blog/how_to_sync_customer_audiences_from_any_cdp_with_a_unified_api"
---

# How to Sync Customer Audiences from Any CDP with a Unified API
------
_August 4, 2026_

Your product can read a customer's audiences from whichever customer data platform they run, through one connection and one method call. `sdk.cdp.listCdpSegments` returns the same object whether the customer is on Twilio Segment, Adobe Experience Platform, Lytics, or Tealium, and a webhook subscription keeps your copy of that list current as the customer builds and edits audiences in their own tool. Every request is executed directly against the customer's CDP, so what you read is what exists there now.


## Why audiences are the hard part of a CDP integration


Any product that targets, messages, personalizes, or reports on groups of people needs to know which group the customer means. The customer has already answered that question, in detail, inside their CDP. Reading their answer is the integration.


The difficulty is that each platform models the same idea differently and names it differently.


| Concept                   | Twilio Segment                          | mParticle  | Adobe Experience Platform     | Unified object |
| ------------------------- | --------------------------------------- | ---------- | ----------------------------- | -------------- |
| Where data arrives        | Source                                  | Input      | Source                        | `Source`       |
| Where data leaves         | Destination                             | Output     | Destination                   | `Destination`  |
| A defined group of people | Audience (Engage)                       | Audience   | Audience / segment definition | `Segment`      |
| Sending a group somewhere | Connecting an audience to a destination | Connection | Activation via dataflow       | `Activation`   |
Building against each one means a separate integration, separate auth, and separate handling for [pagination](/docs/reference/pagination), [rate limits](/docs/reference/rate_limits#rate-limits), and errors, before you have written any of your own product. The Unified [CDP API](/cdp) normalizes the concept into a single `Segment` object so you build the read once.


## What you are building


An audience list inside your product: the customer connects their CDP, sees their own audiences, and picks the ones your feature should act on. The list stays current without a nightly job.

1. Authorize the customer's CDP and store one connection ID
2. List their audiences
3. Show the detail their platform provides
4. Subscribe to changes so the list updates itself

## Connect once and list every audience


Initialize the SDK with your API key. This is the same for every category.


```typescript
import { UnifiedTo } from '@unified-api/typescript-sdk';
import * as errors from '@unified-api/typescript-sdk/sdk/models/errors';

const sdk = new UnifiedTo({
  security: {
    jwt: process.env.UNIFIED_API_KEY,
  },
});
```


If your customers require their data to stay in a region, pass `serverIdx` when initializing: `0` for the North American server, `1` for European, `2` for Australian.


Each end-customer authorizes their own CDP through Unified's authorization component, which returns a connection ID. Store one per customer. That ID is the only thing that changes between a customer on Twilio Segment and a customer on Adobe Experience Platform.


Listing their audiences takes one call:


```typescript
const segments = await sdk.cdp.listCdpSegments({
  connectionId,
  limit: 50,
  offset: 0,
  sort: 'updated_at',
  order: 'desc',
});
```


`id` and `name` are your floor. Every integration that supports the `Segment` object returns them, which is enough to render a working picker on day one.


## What each field tells you, and why it changes your interface


The rest of the `Segment` object is where a generic list becomes something a customer trusts. Five fields carry information most audience pickers do not show, because most audience pickers are built against one platform and inherit its assumptions.


**`compute_mode`** returns `REALTIME` or `BATCH`. This is the field to read first, because it bounds what your product can honestly promise. A realtime audience recalculates membership as data arrives. A batch audience recalculates on a schedule, which means the answer you get may describe the customer's world several hours ago. If your feature acts on an audience immediately, surface this. A customer who picks a batch-computed audience for a realtime trigger has made a mistake your interface could have prevented.


**`type`** returns `USERS`, `ACCOUNTS`, or `LINKED`. For a B2B product this is the difference between a feature working and quietly targeting the wrong thing. An `ACCOUNTS` audience is a set of companies; a `USERS` audience is a set of people. Filter on this rather than asking the customer to infer it from an audience name.


**`definition`** returns the membership rule itself. Names decay: an audience called "High Intent Q3" was defined by someone who has since left, and nobody is certain what it selects for any more. Showing the rule alongside the name lets a customer confirm they are picking what they think they are picking.


**`size`** returns the number of profiles currently in the audience. Useful as a sanity check in your interface, and useful as a guard: a customer about to run a campaign against an audience of four has probably chosen the wrong one.


**`slug`** is the data-plane handle that identifies membership in profiles and destinations. Where `id` is what you store, `slug` is what you match on when you follow an audience into other parts of the customer's stack.


**`is_active`** tells you whether the audience is live in the customer's CDP. An inactive audience is one they have paused or retired, and it usually should not appear as a choice.


## Handling coverage that varies by platform


Each CDP's own API determines what Unified can return, so a field that exists on the object may not be available on every integration. This is not something to design around after the fact. It is a first-class part of building on any category with a wide integration surface, and three habits handle it.


**Check support before you scope.** [Field support, webhook support, and list parameters are documented per integration](https://docs.unified.to/cdp/integrations). If your feature depends on `definition`, confirm which platforms return it before you promise it.


**Treat** **`501`** **as information, not failure.** Where a platform's API does not support an object, the request returns `501 Not Implemented` rather than an empty list. That distinction matters: an empty list means the customer has no audiences, and a `501` means this platform cannot tell you either way. Show those differently.


```typescript
try {
  const segments = await sdk.cdp.listCdpSegments({ connectionId });
  return renderPicker(segments);
} catch (error) {
  if (error instanceof errors.SDKError && error.statusCode === 501) {
    return renderUnsupported();
  }
  throw error;
}
```


`SDKError` is thrown for every 4XX and 5XX response and carries `statusCode` alongside the message and body, so the same pattern handles authorization failures and rate limiting next to the unsupported case.


**Render what returns.** Build the interface so `name` and `id` are sufficient, and every other field is an enhancement that appears when the platform provides it. A picker that degrades gracefully works across the whole category. A picker that assumes `definition` works on a subset of it.


## Stay current when the customer changes an audience


A customer who builds a new audience expects to see it in your product without being told to refresh anything. Rather than re-reading the full list on a schedule, subscribe to changes and let Unified detect them and deliver events to your endpoint.


```json
{
  "connection_id": "5de520f96e439b002043d8dc",
  "hook_url": "https://yourapp.com/webhooks/cdp",
  "object_type": "cdp_segment",
  "event": "created",
  "interval": 5,
  "webhook_type": "virtual"
}
```


Subscribe separately for `created` and `updated`. The `interval` field sets how often Unified checks for changes, and can be set as low as one minute on paid accounts and 60 minutes on free ones. `webhook_type` selects between `virtual`, where Unified detects changes and delivers events, and `native`, where the platform's own webhooks are used; which is available depends on the integration.


Passing `include_all` on the create request sends your server all existing audiences on the first run, which backfills without a separate import path.


Each subscription carries a read-only `is_healthy` field. Read it as part of your own monitoring, so a subscription that has stopped delivering surfaces in your product rather than in a support ticket.


`Segment` has the widest webhook coverage of any object in the CDP category, but it is not universal. Confirm your customers' platforms on the Supported Integrations page, and fall back to a scheduled read where a platform has no webhook events.


## Read only what changed


For the platforms where you are reading on a schedule, `updated_gte` returns only records modified since a timestamp you supply.


```typescript
const changed = await sdk.cdp.listCdpSegments({
  connectionId,
  updated_gte: lastSyncedAt,
  sort: 'updated_at',
  order: 'asc',
  limit: 100,
});
```


Sorting ascending on `updated_at` means the last record in the response carries the timestamp for your next call, so you do not maintain a cursor. The same parameters work on every object in the category.


Two details make the paging loop simpler than it looks. `offset` is zero-based on every integration regardless of how that platform pages internally, so you count records rather than pages. And you know you have reached the end when a response returns fewer records than the `limit` you asked for, which is the exit condition for the loop. Most list endpoints cap at 100 records per page, and not every integration supports every pagination parameter, which is recorded on the Feature Support tab of the integration's page in `app.unified.to`.


→ [Start your 30-day free trial](https://app.unified.to/login)
→ [Book a demo](https://calendly.com/d/cph9-g8n-jzg/connect-with-unified)


## Frequently asked questions


**Should I read audiences at request time or synchronize them into my own database?**


Both, for different jobs. Read at request time when you need the current state of a record you can already identify, such as the audience a customer has just selected. Synchronize when you need to search, filter, or join across records, because the `query` parameter filters only on `email` or `name`, and anything beyond that has to happen in a database you control. Unified.to recommends synchronizing if you plan to run queries, using webhooks as the mechanism, which is the same subscription described above. The read path and the sync path are not alternatives: the webhook that keeps your picker current is also what populates the copy you query against.


**What happens on a platform that returns only a name and an ID?**


Your picker still works. `id` and `name` are returned by every integration supporting the `Segment` object, and a picker built to treat the remaining fields as optional will render correctly on a minimal platform and richly on a complete one. This is why the coverage question is an interface decision rather than a blocker.


**How do I tell whether an audience is safe to act on in real time?**


Read `compute_mode`. `REALTIME` means the CDP recalculates membership as data arrives. `BATCH` means it recalculates on a schedule set inside the customer's platform, so membership can be hours out of date regardless of how current your read is. Unified returns the audience as it exists right now; `compute_mode` tells you how recently the CDP itself worked it out.


**Is a batch-computed audience still worth reading?**


Yes, for anything that is not time-critical. Reporting, list building, campaign setup, and account-level targeting are all well served by a batch audience. The distinction matters for triggers that fire on a user action, where acting on stale membership produces a visibly wrong result.


---


**Author**


Written for Unified.to by Mallory Greene


_About the author: Mallory Greene is a writer specializing in generative engine optimization (GEO) and content, through her practice Search Everywhere. She covers integration infrastructure and technical content across Unified.to's technical content library. Based in Toronto._