Skip to main content
Manage first-party CRM audiences on a seller account. Upload hashed customer lists, check matching status, and reference the resulting audiences in create_media_buy targeting overlays for explicit retargeting or suppression. Audiences are distinct from signals: signals are named targeting dimensions discovered through product signal options, provider-published signal definitions, or get_signals; audiences are data you own and upload. Use audience_include to target only members of an uploaded list. audience_include is a hard constraint — only users on the list are eligible. A signal returned by get_signals is not a synced audience ID and instead belongs in signal_targeting_groups. To find new users similar to an audience (lookalike expansion), describe that intent in your campaign brief — the seller handles expansion strategy. Note: lookalike intent expressed in the brief cannot be verified through the protocol; confirm via seller-side reporting. Response Time: Upload accepted in ~1–2s. Per-audience matching is asynchronous — the task remains active until matching completes (1–48 hours depending on the seller). Configure push_notification_config to receive a webhook when the audience is ready. Sellers whose ingestion pipeline cannot return per-audience results in the synchronous response (batch ingestion, governance-gated upload, clean-room flows) MAY respond with an operation-level submitted task envelope — see Response shapes. Request Schema: /schemas/3.2.0-beta.7/media-buy/sync-audiences-request.json Response Schema: /schemas/3.2.0-beta.7/media-buy/sync-audiences-response.json

Quick Start

Upload a customer list and check its status:

Request Parameters

Audience Object

Audience Member

Every member requires an external_id (buyer-assigned stable identifier) plus at least one matchable identifier. Hash all values with SHA-256 before sending — normalize emails to lowercase+trim, phone numbers to E.164 format (e.g. +12065551234). Providing multiple identifiers for the same person improves match rates. Composite identifiers (e.g. hashed first name + last name + zip) are not yet standardized — use ext for platform-specific extensions. Identifier support varies by seller: Check get_adcp_capabilitiesmedia_buy.audience_targeting.supported_identifier_types and media_buy.audience_targeting.supported_uid_types before sending. MAID support is not universal (LinkedIn does not accept MAIDs; iOS IDFA requires App Tracking Transparency consent). The media_buy.audience_targeting.matching_latency_hours range and media_buy.audience_targeting.minimum_audience_size in capabilities are also seller-specific. Activation paths beyond inline push: Inline members are one activation method. Sellers declare which paths they support — AdCP push, dataset sharing, clean rooms, platform distribution — via audience_activation on products and media_buy.audience_targeting.supported_activation_methods in get_adcp_capabilities. Filter discovery with filters.audience_activation_methods on get_products to find inventory compatible with your data pipeline. External source references — pointing this task at a shared dataset or vendor-distributed segment instead of pushing members — are available experimentally on sellers that declare the corresponding activation patterns. Size limit: Payloads are limited to 100,000 members per call across all audiences. For larger lists, chunk into sequential calls using add deltas. Concurrency: Ensure that calls made to sync_audience are independent of eachother. They may be processed out-of-order. If you need sequential execution, wait for the callback to your configured webhook before making another call.

Response shapes

Responses use discriminated unions — a response has exactly one of three shapes, never mixed: 1. Synchronous success — per-audience results:
  • audiences — Results for each audience on the account, including audiences not in this request
  • sandbox — Boolean indicating if this response is from sandbox mode (optional)
2. Terminal failure — no audiences processed:
  • errors — Array of operation-level errors (auth failure, account not found, invalid request format)
3. Submitted task envelope — whole operation queued asynchronously (batch ingestion, governance-gated upload, clean-room flows where the seller cannot return per-audience results before the response is emitted):
  • status — Always "submitted"
  • task_id — Handle for polling via get_task_status on the A2A profile (or the advertised AdCP polling task on MCP), or receiving a webhook on completion
  • message — Optional human-readable explanation of the queue state
The final per-audience audiences array lands on the task completion artifact, not on the submitted envelope. Per-audience asynchronous matching (one audience in processing while the rest of the sync resolves synchronously) belongs on the synchronous success branch with status: "processing" on that item, not on the submitted envelope. Matching latency on the per-audience audience-status enum is the common case; the submitted envelope is for the less-common operation-level async case. Each audience in success response includes:

Match breakdown

When a seller supports per-identifier-type reporting, the response includes match_breakdown — an array showing which identity types are resolving and at what rate. This helps buyers decide which identifiers to prioritize in future uploads.
Key semantics:
  • submitted and matched are cumulative across all syncs, matching total_uploaded_count semantics (not uploaded_count).
  • effective_match_rate is deduplicated — a member matched via both email and phone counts once. It will be less than or equal to the sum of per-type match rates.
  • match_rate is server-authoritative — consumers should prefer this value over computing their own from submitted/matched.
  • id_type values combine hashed PII types (hashed_email, hashed_phone) with universal ID types (rampid, uid2, id5, euid, pairid, maid).
Sellers that only support aggregate match counts omit match_breakdown entirely.

Common Scenarios

Discovery Only

Check status of all existing audiences without making changes. The response includes all audiences on the account — filter by audience_id to find the one you care about:

Suppression List

Upload a list of existing customers to suppress from acquisition campaigns:

Removing Members

Update an audience incrementally — add new members and remove ones that no longer qualify:

Deleting an Audience

Remove a specific audience from the account without affecting others. Set delete: true on the audience object:
To delete multiple audiences in one call, include each with delete: true. To delete all buyer-managed audiences at once, use delete_missing: true with an empty audiences array — but be careful, this removes everything.

Using Audiences in a Media Buy

Once an audience is ready, reference it by audience_id in create_media_buy targeting overlays. Audience IDs are scoped to the seller account — they cannot be used across sellers.
test=false

External Audience Sources (experimental)

Experimental (media_buy.audience_activation). External sources share the activation-declaration feature id: sellers implementing them MUST list media_buy.audience_activation in experimental_features, and the surface may change between 3.x releases with notice. See Experimental status.
Instead of pushing hashed members inline, an audience can carry a source reference — a pointer to buyer data the seller ingests over a declared activation rail. The data never transits AdCP: the protocol moves the pointer, ingestion happens on the vendor’s rails, and the resulting audience lands in the same lifecycle (status enum, match reporting, discovery, targeting overlays) as a pushed audience. Two source kinds, mapping 1:1 onto the seller’s declared activation patterns. Buyers MUST only send kinds whose pattern the seller declared (dataset_querydataset, platform_distributionplatform_segment); undeclared kinds are rejected with UNSUPPORTED_FEATURE. Schema: /schemas/3.2.0-beta.7/core/audience-source.json

Dataset (grant-based)

The seller reads a table or share the buyer granted through the vendor’s grantee-identified sharing mechanism (Snowflake Secure Data Sharing, Databricks Delta Sharing using D2D or OIDC token federation, BigQuery authorized views):
  1. Read the seller’s declared consumer_identities from its audience_activation declaration.
  2. Grant access in the vendor’s system (e.g. ALTER SHARE audience_share ADD ACCOUNTS = SELLERORG.INGEST_US). Out of band — AdCP never carries credentials. The grant MUST name a principal without conveying a secret through AdCP: Databricks D2D and open sharing with OIDC token federation qualify; the classic bearer-token recipient profile does not. A request carrying credential material is rejected with CREDENTIAL_IN_ARGS.
  3. Call sync_audiences with the source reference:
test=false
For Databricks, keep the two sharing identifiers distinct. The seller advertises its non-secret recipient identity in consumer_identities[] as <cloud>:<region>:<uuid>. The buyer passes what to read in source.locator using the Databricks-path convention share://<provider-sharing-identifier>/<share-name>/<schema>.<object>. For example: share://aws:us-east-1:<provider-uuid>/pinnacle_audiences/suppression.high_value_v3. The seller resolves the provider identity and share name against the grant before reading the object.
  1. The seller reads, matches, and the audience moves processingready / too_small exactly as for pushed audiences. A missing or revoked grant is a retryable per-audience failure (action: "failed" with SOURCE_ACCESS_FAILED) — grant and retry.
Column contract. The shared object MUST expose the canonical AdCP identifier columns mirroring the audience member shape: external_id (required; alias any stable per-row key, e.g. a clean-room output’s resolved key), plus hashed_email, hashed_phone, and uid_type/uid_value rows as available. Same normalization and hashing rules as the inline path — these are the canonical pre-hash forms; receivers needing other forms cannot re-derive them from a one-way hash, so a table storing a non-canonical form silently loses match rate. The seller does any joining against its own tables internally; the buyer never conforms to seller-internal schemas. For a multi-publisher Databricks share, the buyer or broker can publish one canonical view and use recipient properties for row-level isolation so each publisher sees only its entitled rows:
Refresh. A standing grant is a live pipe: while access lasts, the seller MAY re-read the source to keep membership as current as the share’s replication lag (direct same-region shares are zero-copy live; cross-region replication or listing auto-fulfillment bounds freshness by the replication schedule). Each read updates last_synced_at. Buyers wanting snapshot semantics revoke the grant after ready.

Platform segment (destination-initiated)

The vendor’s rails deliver the segment to the seller’s declared platform_distribution destination:
  1. Read the seller’s destination_ref from its activation declaration (or account setup).
  2. Initiate distribution in the vendor’s system.
  3. Call sync_audiences with source: { "kind": "platform_segment", "vendor": { "domain": "activation-hub.example" }, "segment_ref": "seg_88213" } to bind the arriving segment to your audience_id.
segment_ref is the vendor’s identifier as issued to you — the ID you can observe in the vendor’s console or API. The seller owns the mapping to whatever identifier its ingest minted; it configured the destination and is the only party that sees both sides. Binding is order-independent: if the bind arrives before the segment, the audience sits in processing. Initial vendor distribution is days-scale with multi-day refresh cadences — sellers MAY expire an unfulfilled bind after their declared bind_expiry_days (per-audience action: "failed"), and declared windows shorter than the vendor’s documented distribution latency are non-conformant.

Lifecycle rules

  • Transport is fixed at creation. An audience is either buyer-pushed or externally sourced for its lifetime. Cross-transport upserts (member deltas against a sourced audience, or source against a pushed one) are rejected with CONFLICT (error.field: "audience_id"); convert by delete-and-recreate.
  • Loss of source access MUST NOT change audience status. The seller cannot reliably distinguish a revoked grant from an expired one from a transient vendor outage — all three observables are a failed read — so the only implementable rule keys on read failure: last matched state persists and remains targetable, last_synced_at freezes, and failed re-reads MUST NOT emit suspended (that value stays reserved for the consent/policy causes on the status enum, which remain orthogonal and still apply). Source health is reported separately via source.access_status (active | unavailable).
  • Access expiry is not deletion. access_expires_at and revocation bound the access window, not what the seller matched during it — exactly as un-pushing is impossible on the inline path. Retention, deletion, and data-subject requests remain governed by the buyer–seller data processing agreement; spec text MUST NOT present expiry as a privacy feature. Teardown stays an explicit buyer action (delete: true).
  • Counts are as-of last_synced_at. Required on sourced audiences whenever counts are populated (ready / too_small): two reads can differ because membership changed, match rate changed, or the seller re-read the source, and the timestamp is the buyer’s only anchor. total_uploaded_count is cumulative-distinct and monotonic — not a size metric.
  • Single pane. Externally sourced audiences appear in discovery-only calls identically to pushed ones — same status enum, same match reporting, same targetability — with the source echoed (kind, vendor, reference; never credentials) plus columns_read (which canonical columns the seller actually read, so zero matches on an offered identifier type is distinguishable from a column the seller never read) and access_status.

Audience Status

Platform matching is asynchronous. The status field reflects the current state: status is present when action is created, updated, or unchanged. It is absent when action is deleted or failed. Sellers MUST emit too_small whenever matched_count < minimum_size. Returning ready with a matched_count below the platform minimum is non-compliant — buyers rely on the status value as a programmatic signal that targeting will fail, not on post-hoc interpretation of the count. Webhook (recommended): Configure push_notification_config at the protocol level before uploading. The task stays active while the seller’s platform matches members. When matching completes, the task completes and the webhook fires with the final result — status: "ready" or status: "too_small". Check get_adcp_capabilitiesaudience_targeting.matching_latency_hours to set realistic expectations (typically 1–48 hours). Polling fallback: If not using webhooks, poll with discovery-only calls (omit audiences) no more frequently than every 15 minutes. A2A profile callers use get_task_status with the task_id; MCP callers use the advertised AdCP polling task. The task will be submitted while matching is in progress and completed when the audience is ready or too small. Agent workflow: Upload with push_notification_config set. Externalize the audience_id and account_id before the session ends. When the webhook fires with status: "ready", resume and proceed to create_media_buy.

Async patterns

Two distinct async patterns — match the right one to the seller’s behavior: Per-audience async matching (common): the sync operation itself resolves synchronously and returns per-audience results immediately. Audiences whose matching is still running come back on the synchronous success response with status: "processing". The buyer reconciles terminal state (ready / too_small) via subsequent discovery-only calls or a webhook. This is the case covered by the Audience Status enum above. Operation-level async (less common): the whole sync is queued — the seller cannot return any per-audience results before responding, because ingestion is batched, governance review gates the upload, or an upstream clean-room flow must settle before matching can start. The response is a submitted envelope:
  • Top-level status: "submitted" with task_id
  • message — optional human-readable explanation
  • No audiences array on this envelope
Invoke get_task_status on the A2A profile (or the advertised AdCP polling task on MCP), or wait for the webhook. The completion artifact carries the audiences array with per-item action/status results; operation-level failures surface as status: "failed" on the task. See: Webhooks for webhook configuration.

Hashing Requirements

Hash all identifiers with SHA-256 before sending. Normalize first:
test=false

Privacy Considerations

The schema never carries cleartext email or phone — buyers MUST hash before transport. The seller matches by independently hashing its own user data with the same algorithm. Hashed identifiers are pseudonymous PII, not anonymous. Unsalted SHA-256 of an email or phone number is recoverable via precomputed dictionaries of the email and E.164 namespaces, so hashed_email and hashed_phone MUST be treated as PII for retention, consent, access control, and data-subject-request purposes. Do not describe them as “privacy-preserving” in operator documentation or DPAs. See Privacy Considerations. Buyer obligations: The buyer is responsible for having a lawful basis to process and share audience data, regardless of jurisdiction. Include consent_basis on each audience to communicate the GDPR lawful basis to sellers operating in regulated markets — some sellers require this field for EU audiences. Data handling: Once uploaded, data processing and retention are governed by your agreement with the seller. Review the seller’s data processing terms before uploading audience data.

Error Handling

Next Steps

  • Targeting — Reference audiences in targeting_overlay.audience_include and audience_exclude
  • create_media_buy — Apply audience targeting to packages
  • Conversion Tracking — Track outcomes from audience-targeted campaigns