Skip to main content
Configure event sources on a seller account for conversion tracking. Supports upsert semantics, seller-managed sources, and setup instructions. Response Time: ~1s (synchronous configuration) Request Schema: /schemas/v3/media-buy/sync-event-sources-request.json Response Schema: /schemas/v3/media-buy/sync-event-sources-response.json

Quick Start

Configure an event source for purchase tracking:
import { testAgent } from "@adcp/client/testing";
import { SyncEventSourcesResponseSchema } from "@adcp/client";

const result = await testAgent.syncEventSources({
  account: { account_id: "acct_12345" },
  event_sources: [
    {
      event_source_id: "website_pixel",
      name: "Main Website Pixel",
      event_types: ["purchase", "lead", "add_to_cart"],
      allowed_domains: ["www.example.com", "shop.example.com"],
    },
  ],
});

if (!result.success) {
  throw new Error(`Request failed: ${result.error}`);
}

// Validate response against schema
const validated = SyncEventSourcesResponseSchema.parse(result.data);

// Check for operation-level errors first (discriminated union)
if ("errors" in validated && validated.errors) {
  throw new Error(`Operation failed: ${JSON.stringify(validated.errors)}`);
}

if ("event_sources" in validated) {
  for (const source of validated.event_sources) {
    console.log(`${source.event_source_id}: ${source.action}`);
    if (source.setup?.snippet) {
      console.log(`  Install: ${source.setup.snippet_type}`);
    }
  }
}

Request Parameters

ParameterTypeRequiredDescription
accountaccount-refYesAccount reference. Pass { "account_id": "..." } or { "brand": {...}, "operator": "..." } if the seller supports implicit resolution.
event_sourcesEventSource[]NoEvent sources to sync. When omitted, the call is discovery-only and returns all existing event sources without modification.
delete_missingbooleanNoWhen true, buyer-managed event sources on the account not in this request are removed (default: false)

Event Source Object

FieldTypeRequiredDescription
event_source_idstringYesUnique identifier for this event source
namestringNoHuman-readable name
event_typesEventType[]NoEvent types this source handles. If omitted, accepts all event types.
allowed_domainsstring[]NoDomains authorized to send events for this source

Response

Success Response:
  • event_sources - Results for each event source, including both synced and seller-managed sources on the account
Error Response:
  • errors - Array of operation-level errors (auth failure, account not found)
Note: Responses use discriminated unions - you get either success fields OR errors, never both. Each event source in success response includes:
  • All request fields
  • seller_id - Seller-assigned identifier for this event source
  • action - What happened: created, updated, unchanged, deleted, failed
  • action_source - Type of event source (website pixel, app SDK, etc.)
  • managed_by - Who manages this source: buyer or seller
  • setup - Implementation details (snippet, instructions)
  • health - Event source health assessment (when seller supports health scoring)
  • errors - Per-source errors (only when action: "failed")
See schema for complete field list: sync-event-sources-response.json

Event Source Health

Sellers that evaluate event source quality include a health object on each source in the response. This is analogous to Snap’s Event Quality Score or Meta’s Event Match Quality — it tells the buyer whether their event integration is working well enough for optimization.
FieldTypeDescription
statusstringAdCP-standardized health level: insufficient, minimum, good, excellent. Use this for cross-seller decisions.
detailobjectSeller-specific scoring (optional). Contains score, max_score, and optional label. Only present when the seller has a native quality score.
match_ratenumberFraction of events matched to ad interactions (0.0-1.0).
last_event_atdate-timeTimestamp of the most recent event received.
evaluated_atdate-timeWhen this health assessment was computed. Stale for sellers computing from reporting data.
events_received_24hintegerEvents received in the last 24 hours (0 = not firing).
issuesarrayActionable problems with severity (error, warning, info) and message. Sort by severity — don’t rely on array position.
test=false
{
  "event_sources": [
    {
      "event_source_id": "web_pixel",
      "action": "unchanged",
      "managed_by": "buyer",
      "health": {
        "status": "good",
        "detail": { "score": 7.2, "max_score": 10, "label": "Event Match Quality" },
        "match_rate": 0.72,
        "last_event_at": "2026-03-23T14:22:00Z",
        "evaluated_at": "2026-03-23T14:30:00Z",
        "events_received_24h": 14200,
        "issues": [
          {
            "severity": "warning",
            "message": "Missing hashed_email parameter on 38% of purchase events. Adding this improves match rate for cross-device attribution."
          }
        ]
      }
    }
  ]
}
Buyer agents should key decisions off status, not detail.score. The four-tier status is comparable across all sellers — a buyer agent writes one rule (“require good or better for DR products”) that works everywhere. The detail object is for human dashboards or advanced diagnostics. Buyer agents can use health data to:
  • Gate product selection on event quality (e.g., require good or better for DR products)
  • Surface setup issues to the buyer before campaign launch
  • Prioritize which event sources to fix first

Common Scenarios

Discovery Only

Discover all event sources on an account (including seller-managed sources) without making changes. Useful for platform-managed conversion tracking where the seller provides always-on attribution:
import { testAgent } from "@adcp/client/testing";
import { SyncEventSourcesResponseSchema } from "@adcp/client";

const result = await testAgent.syncEventSources({
  account: { account_id: "acct_12345" },
});

if (!result.success) {
  throw new Error(`Request failed: ${result.error}`);
}

const validated = SyncEventSourcesResponseSchema.parse(result.data);

if ("errors" in validated && validated.errors) {
  throw new Error(`Operation failed: ${JSON.stringify(validated.errors)}`);
}

if ("event_sources" in validated) {
  for (const source of validated.event_sources) {
    console.log(`${source.event_source_id} (${source.managed_by}): ${source.name}`);
  }
}

Multiple Event Sources

Configure separate sources for website and app:
import { testAgent } from "@adcp/client/testing";
import { SyncEventSourcesResponseSchema } from "@adcp/client";

const result = await testAgent.syncEventSources({
  account: { account_id: "acct_12345" },
  event_sources: [
    {
      event_source_id: "web_pixel",
      name: "Website Pixel",
      event_types: ["purchase", "lead", "add_to_cart", "view_content"],
      allowed_domains: ["www.example.com"],
    },
    {
      event_source_id: "app_sdk",
      name: "Mobile App SDK",
      event_types: ["purchase", "app_install", "app_launch"],
    },
  ],
});

if (!result.success) {
  throw new Error(`Request failed: ${result.error}`);
}

const validated = SyncEventSourcesResponseSchema.parse(result.data);

if ("errors" in validated && validated.errors) {
  throw new Error(`Operation failed: ${JSON.stringify(validated.errors)}`);
}

if ("event_sources" in validated) {
  for (const source of validated.event_sources) {
    console.log(`${source.event_source_id} (${source.managed_by}): ${source.action}`);
  }
}

Discovering Seller-Managed Sources

Sellers may provide always-on event sources (e.g. Amazon sales attribution). These appear in the response with managed_by: "seller" alongside your buyer-managed sources:
test=false
{
  "event_sources": [
    {
      "event_source_id": "web_pixel",
      "name": "Website Pixel",
      "seller_id": "px_abc123",
      "action": "created",
      "managed_by": "buyer",
      "setup": {
        "snippet": "<script src=\"https://seller.example/pixel/px_abc123.js\"></script>",
        "snippet_type": "javascript",
        "instructions": "Place this tag in the <head> of all pages where you want to track events."
      }
    },
    {
      "event_source_id": "seller_sales_attribution",
      "name": "Sales Attribution",
      "seller_id": "internal_attr",
      "action": "unchanged",
      "managed_by": "seller",
      "action_source": "in_store"
    }
  ]
}
Products with conversion_tracking.platform_managed: true indicate the seller provides these sources.

Clean Sync with delete_missing

Replace all buyer-managed event sources on the account:
import { testAgent } from "@adcp/client/testing";
import { SyncEventSourcesResponseSchema } from "@adcp/client";

const result = await testAgent.syncEventSources({
  account: { account_id: "acct_12345" },
  delete_missing: true,
  event_sources: [
    {
      event_source_id: "unified_pixel",
      name: "Unified Tracking Pixel",
    },
  ],
});

if (!result.success) {
  throw new Error(`Request failed: ${result.error}`);
}

const validated = SyncEventSourcesResponseSchema.parse(result.data);

if ("errors" in validated && validated.errors) {
  throw new Error(`Operation failed: ${JSON.stringify(validated.errors)}`);
}

if ("event_sources" in validated) {
  for (const source of validated.event_sources) {
    if (source.action === "deleted") {
      console.log(`Removed: ${source.event_source_id}`);
    } else {
      console.log(`Active: ${source.event_source_id} (${source.action})`);
    }
  }
}

Setup Instructions

The response includes setup details for each event source. The setup object tells you how to activate the source:
snippet_typeDescriptionAction Required
javascriptJavaScript tag for website pagesPlace in <head> of tracked pages
htmlHTML pixel/iframePlace before </body>
pixel_urlURL that fires on eventsSend GET request on each event
server_onlyNo client-side tag neededUse log_event API directly

Error Handling

Error CodeDescriptionResolution
ACCOUNT_NOT_FOUNDAccount does not existVerify account_id from account setup
INVALID_EVENT_TYPEUnrecognized event typeCheck seller’s supported_event_types in get_adcp_capabilities
DUPLICATE_EVENT_SOURCE_IDMultiple sources with same ID in requestUse unique event_source_id values
RATE_LIMITEDToo many sync requestsWait and retry with exponential backoff

Best Practices

  1. Sync before logging - Always configure event sources before sending events via log_event. Events sent to unconfigured sources will be rejected.
  2. Use descriptive IDs - Choose event_source_id values that are meaningful (e.g. web_pixel, app_sdk, crm_import) rather than opaque identifiers.
  3. Specify event_types - Restrict each source to relevant event types for better validation and debugging.
  4. Check seller capabilities - Use get_adcp_capabilities to discover supported event types, UID types, and action sources before configuring event sources.
  5. Install setup snippets - When the response includes setup instructions, install the provided snippet before logging events. Server-only sources (snippet_type: "server_only") skip this step.
  6. Handle seller-managed sources - The response may include sources with managed_by: "seller" that you didn’t configure. These are always-on and provide additional attribution data.

Next Steps