Skip to main content
list_account_changes returns material changes to AdCP-visible state on one account, including changes made outside the observing buyer’s calls. Use it after a snapshot bootstrap, after receiving account.change_recorded, or for an account-level audit of state transitions.
This is a 3.2 implementation draft for RFC #6810. It is not normative until the RFC is ratified.
The feed is optional and appears only when get_adcp_capabilities.account.change_feed.supported is true.

What the feed covers

For every resource type the seller advertises, a material committed change must both appear here and be reflected on its authoritative read, regardless of whether it originated through AdCP, a seller operator or system, another authorized principal, or a connected platform. The feed describes control-plane changes: creation, configuration, lifecycle, relationships, reporting corrections, and deletion markers. It excludes reads, failed operations, dry runs, exact idempotency replays, delivery counter increments, raw audience members, raw events, catalog item bodies, and webhook delivery attempts. For the complete coverage matrix and rationale, see the account change feed draft.

Request

Request schema: account/list-account-changes-request.json Acquire a high-water checkpoint before reading several snapshots:

Response

Response schema: account/list-account-changes-response.json Each change record names the changed resource, action, server-derived origin, optional changed paths and revision, and an allowlisted read-only repair.task hint. It deliberately does not include full before/after values.

Cursor contract

Cursor order, not timestamps, defines the total order. Appends never reorder prior pages. A filtered empty page still advances across scanned nonmatching records, so persist every returned cursor. A cursor is scoped to the authenticated principal, resolved account, and normalized filters. Do not move it between credentials or filter sets. Sellers MUST reject a principal, account, or filter mismatch with INVALID_REQUEST at cursor; applying new filters at an old position could permanently skip history. The cursor also binds an authorization-scope epoch. If visible resources for the principal expand or contract, the seller returns CURSOR_EXPIRED with details.reason: "authorization_scope_changed". Rebootstrap snapshots so changes skipped under the old visibility cannot become an invisible gap. Sellers retain changes for at least 90 days. An expired cursor returns CURSOR_EXPIRED; it never silently restarts. Recovery is to obtain a new latest checkpoint, rebuild all authoritative snapshots, then drain changes after the checkpoint.

Race-free bootstrap

  1. Register account.change_recorded through sync_accounts.
  2. Obtain C0 with starting_position: "latest".
  3. Enumerate list_accounts, get_media_buys, list_creatives, financials, delivery, and other advertised reads for the account. Include all lifecycle statuses and exhaust every page.
  4. Drain changes after C0. Locally allowlist repair.task, construct and validate the read request from the authenticated account and resource identity, then invoke it. Never dispatch feed-supplied task arguments.
  5. Persist the returned cursor and repeat after each signed notification.
  6. Poll periodically so a missed webhook does not create a gap.
The webhook’s optional through_cursor is only a target watermark. Do not install it without reading all intervening pages.

Change granularity

The feed contains at least one record per independently repairable identity. Several package or assignment changes may coalesce under their media buy when get_media_buys repairs the complete changed closure; changes to independent creatives require separate records. Records from one operation may share batch_id, but retain independent change_id values and notifications. For seller-mediated mutations, snapshot and records commit atomically. For an external platform, records commit with the seller’s ingestion of the observed change. A pass-through authoritative read may lead the feed only within the source’s declared stale_after_seconds freshness bound.

Shared-account example

Suppose a connected platform changes cr_8421 while the buyer is idle. The seller records the creative revision, makes it visible on list_creatives, appends the account change, and then fires account.change_recorded. The buyer drains from its cursor and rereads list_creatives; it does not treat the webhook payload as the creative document. This is the same flow for a seller operator changing a campaign budget or another authorized buyer pausing a package. Account visibility is based on current authorization, not on which principal created the resource.

Relationship to webhook_activity

webhook_activity[] answers whether a webhook delivery was attempted. This feed answers which material business-state changes were recorded. A change can exist even when a webhook is missed; one change can also have several delivery attempts. Never use the transport log as account history.