> ## Documentation Index
> Fetch the complete documentation index at: https://docs.apostra.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Sync event sources

> Register or update event source configurations on an advertiser

`POST /api/v2/buyer/advertisers/{advertiserId}/event-sources/sync`

Syncs your event source configurations onto an advertiser. Each source gets an `event_source_id` you pass on every `log-event` call. Sync is idempotent — re-sending the same config returns `unchanged`. Events sent to an unregistered source are rejected, so sync before you log.

Omit `event_sources` entirely for a discovery-only call: it returns every existing event source on the account without modifying anything, up to 200 sources. `delete_missing` has no effect on a discovery-only call. An account with more than 200 sources gets a `400 VALIDATION_ERROR` from discovery-only sync — use [list event sources](/v2/buyer/event-sources/tasks/list-event-sources) instead, which paginates.

Set a body-level `idempotency_key` (16–255 letters, numbers, underscores, periods, colons, or hyphens) to make a retry safe. It prevents a retried request from re-triggering the audit events and downstream pixel provisioning the sync emits, and on a discovery-only call it doubles as a request ID. Repeating the same key with the same request returns the original result and sets `Idempotency-Replayed: true`; reusing it with a different request returns `409 CONFLICT`. Omitting it is still accepted for compatibility, but a retried call with no key is not deduplicated.

## Request

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST https://api.apostra.com/api/v2/buyer/advertisers/12345/event-sources/sync \
    -H "Authorization: Bearer $SCOPE3_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "idempotency_key": "sync-hightouch-20260923-001",
      "account": { "account_id": "12345" },
      "event_sources": [
        {
          "event_source_id": "hightouch_purchase",
          "name": "Hightouch Purchase Sync",
          "event_types": ["purchase", "refund"],
          "integration_platform": "Hightouch",
          "mapping": {
            "eventIdField": "id",
            "eventTypeField": "event_type",
            "eventTimeField": "occurred_at",
            "userMatchFields": ["email_sha256", "sc3clid"],
            "valueField": "revenue",
            "currencyField": "currency",
            "orderIdField": "order_id",
            "consentField": "consent_status",
            "dedupeStrategy": "Use the warehouse event primary key."
          },
          "test_event_code": "TEST-HIGHTOUCH"
        }
      ]
    }'
  ```
</CodeGroup>

## Parameters

| Field | Type | Required | Notes |
| - | - | - | - |
| `advertiserId` | string | Yes | Path. Owning advertiser |
| `idempotency_key` | string | No | Client-generated unique key for at-most-once execution (16–255 chars: letters, numbers, `_`, `.`, `:`, `-`). Also serves as a request ID on discovery-only calls. Strongly recommended — retries are not deduplicated without it |
| `account` | object | Yes | `{ account_id }`. `account_id` must equal the path `advertiserId` |
| `event_sources` | object\[] | No | 1–50 sources to sync. Omit for a discovery-only call that returns every existing source unmodified |
| `event_sources[].event_source_id` | string | Yes | Buyer-assigned ID (1–255 chars) |
| `event_sources[].name` | string | No | Human-readable label (1–255 chars) |
| `event_sources[].event_types` | enum\[] | No | Restricts accepted types (e.g. `purchase`, `lead`, `add_to_cart`, `custom`). Omit to accept all |
| `event_sources[].allowed_domains` | string\[] | No | Domains authorized to send events for this source |
| `event_sources[].integration_platform` | string | No | Apostra extension for the source system, such as `Hightouch`, `CRM`, `MMP`, or `server API` |
| `event_sources[].mapping` | object | No | Apostra extension that records the source fields used to produce `log-event` payloads |
| `event_sources[].mapping.eventIdField` | string | No | Source field mapped to `events[].event_id` for dedupe and retries |
| `event_sources[].mapping.eventTypeField` | string | No | Source field or expression mapped to canonical ADCP `event_type` |
| `event_sources[].mapping.eventTimeField` | string | No | Source timestamp field mapped to `event_time` |
| `event_sources[].mapping.userMatchFields` | string\[] | No | Source fields available for matching, such as click IDs or hashed identifiers |
| `event_sources[].mapping.valueField` | string | No | Source field mapped to `events[].custom_data.value` |
| `event_sources[].mapping.currencyField` | string | No | Source field mapped to `events[].custom_data.currency` |
| `event_sources[].mapping.orderIdField` | string | No | Source field mapped to `events[].custom_data.order_id` |
| `event_sources[].mapping.contentIdsField` | string | No | Source field mapped to product, SKU, or content identifiers |
| `event_sources[].mapping.consentField` | string | No | Source field used to gate consented event forwarding |
| `event_sources[].mapping.dedupeStrategy` | string | No | How stable event IDs are generated and deduplicated |
| `event_sources[].mapping.notes` | string | No | Human-readable implementation notes for the source contract |
| `event_sources[].test_event_code` | string | No | Default test code to use while validating the integration |
| `event_sources[].value_currencies` | string\[] \| null | No | ISO 4217 currencies this source accepts for monetary `custom_data.value`. Required for canonical ROAS — once declared, `log-event` rejects monetary records outside this list. Send `null` to clear a previously declared list |
| `event_sources[].action_source` | enum \| null | No | AdCP flat action-source category: `website`, `app`, `offline`, `phone_call`, `chat`, `email`, `in_store`, `system_generated`, or `other`. Send `null` to clear a previously declared value |
| `event_sources[].surface` | object \| null | No | Structured AdCP surface this source represents, for when `action_source` is too coarse (an owned channel, profile, feed, podcast, or newsletter list). `{ category, property_type?, namespace?, property_id? }`, plus up to 20 extension fields (primitive values only, 2000 bytes serialized total). Send `null` to clear a previously declared surface |
| `delete_missing` | boolean | No | Archive buyer-managed sources not in this payload (default `false`). Has no effect on a discovery-only call |

## Response

```json theme={null}
{
  "event_sources": [
    { "event_source_id": "hightouch_purchase", "action": "created" }
  ]
}
```

`action` is one of `created`, `updated`, `unchanged`, `failed`, or `deleted`. A `failed` result carries an `error` message; the rest of the batch still applies.

## Errors

* `400 VALIDATION_ERROR` — malformed `idempotency_key`, `account.account_id` does not match the path, empty `event_sources`, more than 50 sources in one sync, or discovery-only on an account with more than 200 sources.
* `404 NOT_FOUND` — `advertiserId` does not exist or is not visible to the authenticated account.
* `409 CONFLICT` — `idempotency_key` was already used with a different request, is outside its replay window, or the original request is still in flight.

See [Errors](/v2/reference/errors) for the full error contract.

## Related

<CardGroup cols={2}>
  <Card title="Event source tasks" href="/v2/buyer/event-sources/tasks" icon="list-check">
    All event source operations
  </Card>

  <Card title="Log Events guide" href="/v2/guides/log-events" icon="circle-dot">
    How event sources and log-event fit together
  </Card>

  <Card title="Log event" href="/v2/buyer/event-sources/tasks/log-event" icon="circle-dot">
    Send events through a registered source
  </Card>

  <Card title="Conversion API" href="/v2/integrations/conversion-api" icon="bolt">
    Identity-hashing rules and event payloads
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.