Skip to content

RFC: per-account execution readiness for products (required resources, candidates, and binding) #7763

Closed

Description

@ohalushchak-exadel

Working-group proposal

Let get_products tell a buyer, for each product and in the context of the buyer's account, which account resources the product needs before create_media_buy can succeed, whether the account has them, and which one to use when there are several.

This sits next to three existing pieces and is meant to compose with them, not replace them:

Problem

Many platform products need account resources that exist outside the media buy itself. The buy fails at creation when one is missing or ambiguous. Typical cases on social platforms:

  • a conversion or website-sales product needs a pixel or dataset on the ad account;
  • a lead-generation product needs a page and, depending on the destination, a lead form or a messaging destination;
  • an app-promotion product needs a registered app;
  • a catalogue-sales product needs a product catalogue and product set;
  • a product that runs as a page or profile needs that identity to be connected.

What the platforms document:

  • Meta. The ad set's promoted_object is "The object this ad set is promoting across all its ads. Required with certain campaign objectives." Conversions need a pixel_id (with custom_event_type), app installs need application_id and object_store_url, lead generation needs the page_id, and catalogue sales need a product_set_id. "If you use page_id, the creative must promote that page_id." Lead ads also need a lead form created under the Page and referenced by lead_gen_form_id on the creative. (Ad Set reference, Ad Promoted Object, Create lead ads)
  • TikTok. "You must use a TikTok Pixel to run a conversion campaign." App campaigns depend on a TikTok App ID, which "is required to complete the process of connecting your app with a Mobile Measurement Partner (MMP)", the TikTok SDK or the Events API. Lead-generation ads attach "a new or … existing Instant Form at the Ad level". (Conversions objective, TikTok App ID, Lead generation with Instant Form)
  • Snap. "The pixel_id parameter must be set in the Ad Squad creation call" to track conversions. Snap also exposes an eligibility endpoint "to find out which Optimization Goals a specific Pixel/Snap App/Ad Account is eligible to use", which is the same pre-creation check this proposal would standardise. (Snap Pixel, Ad Squads)

A buyer planning from a wholesale catalogue today cannot see any of this before create_media_buy. The answer is also rarely a yes/no. Common states are:

  • ready: the account has exactly one suitable resource, or the seller has a documented default;
  • setup required: the account has none, and the buyer can create one on the platform;
  • selection required: the account has several, and the buyer must choose which one the buy uses;
  • input required: the resource cannot be discovered from the account, and the buyer must supply it;
  • unknown: the seller could not read the account's resources (permission or platform error).

Omitting products that are not ready hides products the buyer could run after one setup step, and cannot express "choose one of these three". Leaving it to vendor extensions means each buyer agent needs per-seller code.

Proposed direction

Add an optional, account-scoped readiness declaration per product. Illustrative shape:

{
  "execution_readiness": {
    "status": "selection_required",
    "resources": [
      {
        "kind": "event_source",
        "status": "ready",
        "selected": { "resource_id": "px_123", "name": "Main site pixel", "reason": "only_candidate" }
      },
      {
        "kind": "publisher_identity",
        "status": "selection_required",
        "candidates": [
          { "resource_id": "pg_1", "name": "Brand FR" },
          { "resource_id": "pg_2", "name": "Brand BE" }
        ],
        "binding": "…where the chosen resource_id goes on create_media_buy…"
      }
    ]
  }
}

This is deliberately illustrative. Open design points:

  1. Resource kinds. A closed, canonical set: at least event_source, publisher_identity, catalog, app, lead_form, messaging_destination. Reuse existing AdCP concepts where they exist: event_source should line up with sync_event_sources identifiers, catalog with sync_catalogs, and identity/grant cases with downstream-connection-requirement.
  2. Binding. How the buyer passes a chosen resource on create_media_buy. Some kinds already have a standard place (optimization_goals[].event_source_id, packages[].catalogs). Others (page or profile identity, lead form, messaging destination, app) have none today. The working group should decide whether to add typed package fields for them or a single typed resource_bindings list keyed by kind.
  3. Status vocabulary. One status set for the product plus one per resource, distinguishing "the buyer can fix this" (setup_required), "the buyer must choose" (selection_required), "the buyer must supply" (input_required), and "the seller could not check" (absent, or an explicit unknown). The last must never be read as "not runnable".
  4. Caching. Readiness depends on the account, while the product catalogue is often the same for every account. Readiness should not force an otherwise public wholesale response into cache_scope: "account". One option is to carry readiness in a separate response-level map keyed by product_id, versioned separately from wholesale_feed_version.

Design requirements

Acceptance criteria

  • A buyer can tell, before create, that a product needs a resource the account lacks, and which kind.
  • A buyer can see the candidates when the account has several, and knows where to put the chosen one on create_media_buy.
  • "Seller could not check" is distinguishable from "account lacks the resource".
  • A public wholesale catalogue stays cacheable across accounts when readiness is added.
  • create_media_buy failures for a missing or unbound resource return an actionable, field-addressable error naming the kind.
  • Conformance covers: ready by unique candidate, setup required, selection required with candidates, and the seller unable to read the account.

Activity

  1. added
    claude-triagingTriage routine is actively working on this issue (1-3 min)
    rfcProtocol change — auto-adds to roadmap board
    needs-wg-reviewBlocked on a working-group decision — surface in WG meeting agendas
    media-buyIssue concerns the media-buy protocol domain
    and removed
    claude-triagingTriage routine is actively working on this issue (1-3 min)
    on Sep 29, 2026
  2. bokelley commented on Sep 29, 2026

    @bokelley
    Contributor

    Triage

    Classification: Feature request (RFC)
    Bucket(s): spec / protocol
    Status: ready-for-human
    Milestone: Spec Backlog (#9)

    What the experts said:

    • ad-tech-protocol-expert: sound-with-caveats — proposal protocol-izes pre-flight account-resource logic that Meta/TikTok/Snap implementers already perform out-of-band; two schema decisions must be resolved before spec work starts
    • adtech-product-expert: landing-right — pre-creation gap is real; account-scoped design keeps wholesale catalogues cacheable; caching resolved by embedding readiness in the product object per measurement_readiness precedent

    My take: Well-formed RFC filling a genuine buyer-agent gap. Two WG decisions must settle before schema work begins.


    Decision 1 — Binding mechanism for chosen resources on create_media_buy:

    event_source (optimization_goals[].event_source_id) and catalog (packages[].catalogs) already have binding slots. The WG must decide how buyers pass the chosen resource_id for kinds that have none today (publisher_identity, lead_form, app, messaging_destination).

    Option A — typed per-kind fields:

    // create_media_buy — one new field per unbound kind
    {
      "product_id": "...",
      "publisher_identity_id": "pg_2",
      "lead_form_id": "lf_456"
    }

    Discoverable via schema; typesafe. Requires a new field per future kind.

    Option B — generic resource_bindings list:

    // create_media_buy — single extension point keyed by kind
    {
      "product_id": "...",
      "resource_bindings": [
        { "kind": "publisher_identity", "resource_id": "pg_2" },
        { "kind": "lead_form", "resource_id": "lf_456" }
      ]
    }

    Accommodates future kinds without schema changes; less discoverable.


    Decision 2 — publisher_identity / DCR authorization interaction:

    downstream-connection-requirement already expresses authorization state. Must execution_readiness[kind=publisher_identity] with status: "selection_required" be gated on DCR authorization being connected? Showing candidates for an identity the account isn't authorized to use misleads the buyer agent.

    @bokelley, your call on both.


    Triaged by Claude Code. Session: https://claude.ai/code/session_01LCV9s7CiqkcJbJCd4196bP

  3. added
    claude-triagingTriage routine is actively working on this issue (1-3 min)
    on Sep 29, 2026
  4. added this to the Spec Backlog milestone on Sep 29, 2026
  5. added
    claude-triagedIssue has been triaged by the Claude Code triage routine. Remove to re-triage.
    and removed
    claude-triagingTriage routine is actively working on this issue (1-3 min)
    on Sep 29, 2026
  6. bokelley commented on Sep 30, 2026

    @bokelley
    Contributor

    Decision: split — static requirements ship in 3.2, per-account readiness moves to 3.3

    Piece 1 — 3.2, experimental (media_buy.execution_requirements): #7774

    A product can now say "you can buy this, but you have to give me these things." It doesn't depend on the account, so wholesale catalogues stay publicly cacheable and cache_scope doesn't change.

    • Product.execution_requirements[] has three kinds, each tied to a request field that already exists:
      • event_source (optional event_types) → packages[].optimization_goals[] of kind: "event", via event_sources[].event_source_id
      • catalog (catalog_types) → packages[].catalogs[]. App promotion uses catalog_types: ["app"]; app-item already carries app_id, bundle_id, apple_id and store_url.
      • downstream_connection → reuses downstream-connection-requirement (for example publisher_identity). It's met by completing the connection, not by a request field.
    • Every entry is required; alternatives within one entry are any-of. When the list is present it is complete for these kinds. When it's absent, the product hasn't declared anything (not "needs nothing").
    • If a product requires an event_source, it must also declare conversion_tracking. If it requires a catalog, it must declare catalog_types.
    • Unmet event source or catalog → VALIDATION_ERROR, with error.field at the binding and error.details per the new error-details/execution-requirement-unmet.json (reason: not_bound / not_found / ineligible, plus the product's requirement repeated back). An unmet connection keeps using AUTHORIZATION_REQUIRED.

    This covers the acceptance criteria "buyer can tell, before create, that a product needs a resource, and which kind" and "create_media_buy failures … name the kind" for these three kinds.

    Piece 2 — deferred to 3.3 (this issue stays open for it)

    • Per-account execution_readiness: ready / setup_required / selection_required / input_required / unknown, with account-scoped candidates.
    • A response-level readiness map keyed by product_id, versioned separately from wholesale_feed_version, so readiness doesn't force cache_scope: "account".
    • Bindings for kinds that have no request slot yet: lead_form and messaging_destination. Meta and TikTok attach lead forms at the ad/creative level, so check whether they belong on the creative before adding package fields.
    • Letting the buyer choose among several connected identities. Until then the seller applies its documented default.
    • Conformance scenarios: ready by unique candidate, setup required, selection required, and the seller unable to read the account.

    Resolving the two triage decisions for Piece 2:

    1. Binding: typed fields, not a generic resource_bindings list. Buyer agents build calls from the schema; a generic {kind, resource_id} list is harder to discover and validate.
    2. publisher_identity candidates: yes, only list identities that are connected. An unconnected identity shows as setup_required and links to the connection's authorization_url.
  7. modified the milestones: Spec Backlog, 3.3.0 on Sep 30, 2026
  8. removed
    needs-wg-reviewBlocked on a working-group decision — surface in WG meeting agendas
    on Sep 30, 2026
  9. bokelley commented on Sep 30, 2026

    @bokelley
    Contributor

    Consolidated 3.3 scope, after #7774 merged

    #7774 is merged (static Product.execution_requirements, experimental in 3.2). This collects everything we deferred, what we learned while reviewing it, and what the Meta adapter work (AI-10516) exposed, so 3.3 design can start from one place.

    What 3.2 can already express natively

    Question Native 3.2 answer
    What does this product need? execution_requirements: event_source, catalog (including app), downstream_connection
    Which Pixels or event sources does the account have? sync_event_sources with no event_sources (read-only; includes seller-managed sources with health)
    Is the event data good enough to optimize? measurement_readiness on the product (account-scoped; insufficient with missing_event_types and issues)
    Which catalogues does the account have? sync_catalogs, read-only mode
    Is a connection or Page connected? A format's required_connections with status and resource_ref.identity_id, in account-scoped responses

    The answers are spread over several places and the buyer has to assemble them. That's why Meta, TikTok and Snap each ship ext.scope3_execution_readiness.

    Still open for 3.3

    1. One readiness answer per product. ready / setup_required / selection_required / input_required / unknown, per requirement and rolled up per product. "Seller could not check" must be distinguishable from "account lacks it".

    2. Candidates. Account-scoped, never disclosed across accounts. A Pixel or catalogue candidate's resource_id must be a value the buyer can put directly into the existing field: the sync_event_sources ID (not the raw platform Pixel ID, if they differ) or a catalogue reference.

    3. Caching. Carry readiness in a response-level map keyed by product_id, versioned separately from wholesale_feed_version, so a public wholesale catalogue doesn't have to become cache_scope: "account". One simpler option was considered for 3.2 and not taken: allow the map only in account-scoped responses. Decide which one 3.3 uses.

    4. Bindings for kinds with no request field:

      The triage decision stands: typed fields, not a generic resource_bindings list.

    5. Proposal buys. Packages created from create_media_buy.proposal_id carry no optimization_goals or catalogs, so feat(schema): add experimental product execution requirements #7774 left them out of the rejection rule. 3.3 needs either proposals that carry bindings, a binding step after creation (update_media_buy replacing packages[]), or a rule that sellers must not finalize a proposal whose requirements are unmet.

    6. Conformance. Storyboards for: ready by unique candidate, setup required, selection required with candidates, seller unable to read the account, and each VALIDATION_ERROR reason (not_bound / not_found / ineligible).

    Lessons from review and the Meta adapter work

    • Declare what execution needs, not everything that's nice to have. A Pixel used only for attribution (link-click traffic, reach) is not an event_source requirement. Declaring it pushes buyers into an event goal that changes how the platform bids. Check the adapter mapping: on Meta, instant-form leads, calls and messaging need a Page, form or destination, not a Pixel.
    • Several Pixels means "choose one", not "ready". Meta accounts often have several Pixels or datasets.
    • Don't make one product per Page. A Page is something the buyer's account has, not something the seller sells. A product per Page makes the catalogue account-specific, multiplies it by the number of Pages, and puts account IDs in product IDs.
    • Compute readiness once per account and requirement kind, not per product. Whether a Pixel, app or Page exists is the same answer for every product that needs it. Prefetch once per request and stamp every product on the page.
    • Seller-managed event sources. When platform_managed is true and the product requires event_source, the buyer needs the seller-managed source's ID from sync_event_sources (managed_by: "seller"). Discovery could be smoother.
    • App promotion. Platforms promote one app on one store per package. feat(schema): add experimental product execution requirements #7774 lets sellers require a single-item app catalogue. Consider whether 3.3 should make that the normative default.

    Housekeeping

    • The MCP parity compile limit (tests/mcp-schema-projection.test.cjs) is 1,265,000 bytes, with about 4.6 KB of headroom. The readiness map will go over it. Plan to raise it, or move the seeded Product in comply_test_controller out of the parity-compiled path.
    • Once feat(schema): add experimental product execution requirements #7774 ships in an RC, Meta, TikTok and Snap can emit execution_requirements from their templates with no inventory reads. The ext readiness field should keep the shape of the planned map so moving to 3.3 is a rename.
  10. bokelley commented on Oct 6, 2026

    @bokelley
    Contributor

    WG decision brief (3.3)

    Problem. A buyer planning from a wholesale catalogue cannot tell, before create_media_buy, whether its account has the pixel, catalogue, app or connected identity a product needs, or which one to use when it has several. #7774 (merged, experimental in 3.2) declares what a product needs; nothing says whether this account has it. Pulling for it: the Meta, TikTok and Snap adapters (AI-10516), which each ship ext.scope3_execution_readiness today and would move to the standard shape as a rename; #7782 (advertiser identity) composes with it.

    Proposed shape. Draft PR: #7934 (experimental media_buy.execution_readiness, additive, optional fields, changeset, 10 schema tests, conformance scenario media_buy_seller/execution_readiness; all local gates green, CI pending). Reviewed by the protocol and product experts; their changes are folded in.

    • get_products request: opt-in include_execution_readiness: true plus account.
    • Response: a separate execution_readiness map keyed by product_id, with its own version. It is not part of the cache layer, so cache_scope and wholesale_feed_version are unchanged and a public catalogue stays public.
    • Statuses ready | setup_required | selection_required | input_required | unknown, per declared requirement and rolled up per product. unknown (seller could not read the account) carries unknown_reason and is never reported as setup_required.
    • ready does not waive binding: buyers still pass resolved.resource_id in the existing field.
    "execution_readiness": { "version": "er_7f3a", "products": { "social_conversions": {
      "status": "selection_required",
      "requirements": [{
        "requirement": { "kind": "event_source", "event_types": ["purchase"] },
        "status": "selection_required",
        "binding_field": "packages[].optimization_goals[].event_sources[].event_source_id",
        "candidates": [ { "resource_id": "es_site", "name": "Site pixel" }, { "resource_id": "es_checkout", "name": "Checkout pixel" } ]
      }] } } }

    Open questions for the WG

    1. Caching: separate response-level map (A) or map only in account-scoped responses (B)? Recommend A. B forces cache_scope: "account" on the whole catalogue and loses public dedupe. Cost of A: one extra version token and the rule that buyers key it by (account, version). Both experts agree.
    2. Must candidate resource_id be directly usable in the existing binding field (sync_event_sources ID or catalogue ID, never a raw platform ID)? Recommend yes. Otherwise the buyer needs a per-seller translation step. Both experts agree; ratify unless someone objects.
    3. lead_form / messaging_destination: typed package fields now, or check creative-level placement first? Recommend creative-first, nothing in 3.3. Meta and TikTok attach lead forms at the ad level, so a package field could be the wrong home and 3.3 is additive-only. Vocabulary stays at the three kinds feat(schema): add experimental product execution requirements #7774 declares.
    4. Buyer choice among several connected identities in 3.3, or keep "seller applies documented default"? Recommend seller default in 3.3. Choice needs a binding field whose placement (package, media buy, creative) is still open in RFC: advertiser identity model (brand claims, account connections, buy binding) #7782 Q3. This slice reports one connection_state for the identity the seller would use. If the WG says "choice in 3.3", settle RFC: advertiser identity model (brand claims, account connections, buy binding) #7782 Q3 first.
    5. Identity candidates: only connected (maintainer call) or all with status? Recommend confirm connected-only. A candidate is something the buyer can use now; a non-connected identity shows through connection_state with authorization_url. No identity list ships in this slice, so this settles the rule for RFC: advertiser identity model (brand claims, account connections, buy binding) #7782 without blocking the PR.

    Out of scope (3.4 unless noted)

  11. added
    needs-wg-reviewBlocked on a working-group decision — surface in WG meeting agendas
    on Oct 6, 2026
  12. bokelley commented on Oct 8, 2026

    @bokelley
    Contributor

    Cross-linking #8023 (audience size as a range, and audience overlap as an optional read; 3.3.0 milestone), which is written to compose with this proposal:

    • Add audience as a resource kind. Line it up with sync_audiences identifiers, the way event_source lines up with sync_event_sources. Retargeting and CRM products need an audience on the account just as conversion products need a pixel.
    • Use one size shape on both surfaces. An audience candidate here MAY carry RFC: report audience size as a range, and audience overlap as an optional read #8023's audience_size / targetable_size objects (lower_bound, upper_bound, precision, as_of). A buyer choosing between existing custom audiences can then compare them the same way it reads a synced list.
    • Status mapping. An audience below the seller's minimum reads as setup_required here and too_small in sync_audiences.

    One piece of implementation evidence on ready meaning "exactly one suitable resource": suitability has to include the event type, not just the resource kind. A Meta dataset receives every event type, but a LinkedIn conversion rule is one event type. An account whose only rule is "Lead" is not ready for a purchase-optimised product, even though it has exactly one candidate. If candidates carry their event_types, buyers and sellers can both apply that check.

  13. removed
    needs-wg-reviewBlocked on a working-group decision — surface in WG meeting agendas
    on Oct 28, 2026
  14. bokelley commented on Oct 31, 2026

    @bokelley
    Contributor

    Shipped in 3.3 (experimental) via #7774 (static execution_requirements, first in 3.3.0-beta.0) and #7934 (per-account execution_readiness, 3.3.0-beta.1). Piece 2 was moved into 3.3 here: #7763 (comment).

    Deferred to 3.4 and tracked in #8655: lead_form and messaging_destination, proposal-buy bindings, the audience resource kind and conditional fetch. Buyer choice among identities is #7782; the publisher_identity rename is #7783 (4.0).

  15. bokelley commented on Nov 2, 2026

    @bokelley
    Contributor

    Meta Page evidence for Q5, and an identity read for 3.4

    Two proposals from building Meta Page support in our adapter. They change nothing in #7934's 3.3 slice; the first is input to Q5, the second is a 3.4 item that composes with #7782.

    1. Q5: report every identity candidate with its own status, not only connected ones

    The brief recommends connected-only. Meta argues for the other answer.

    An ad account can see several Facebook Pages. A Page can fail for a product in two different ways, and they need different fixes:

    • The ad account isn't authorised to promote the Page. The fix is a Business Manager permission.
    • The ad account is authorised, but the Page isn't eligible for this product: no Shop for Shop ads, no messaging set up for click-to-Messenger or WhatsApp, no lead forms for Instant Forms. The fix is on the Page itself.

    Our own adapter filtered both cases out and returned only the winners. A buyer who expected a particular Page saw it vanish, with no way to tell which problem applied. connection_state plus authorization_url covers the first case for one identity. It can't express the second, and it can't say which of several Pages is the problem.

    Proposal: where a requirement lists identity candidates, each candidate carries its own status (same vocabulary as requirements: ready, setup_required, and so on), plus a reason when it isn't ready that says which cause applied: not_authorized or not_eligible. A ready candidate behaves exactly as in the connected-only rule. Buyers that only want usable candidates filter on status: "ready", so nothing is lost for them.

    This keeps resource_ref as it is: an opaque routing hint, not proof of authorisation. Statuses belong on candidates in the readiness response, not on DownstreamConnectionRequirement.

    2. 3.4: a way to list an account's identities without going through a product

    Today a buyer agent can only find out which Pages an account has by asking for readiness on a specific Page-gated product. The natural question, "what identities can this account use?", has no direct answer.

    Proposal: list_accounts (or a sibling read) can optionally return identity sub-resources per account: Pages on Meta, the user identity on TikTok, the Public Profile on Snap. Each would use the same candidate shape and statuses as point 1.

    On type: an advertiser's own Page is connection_type: "advertiser_account" with scope: "identity". It is not publisher_identity, which AdCP keeps for a third party's granted identity, such as TikTok Spark authorisation (#7783).

    This belongs with #7782 (advertiser identity model) and its Q3 binding field. I'd rather it land as part of that than as a separate mechanism.

    We'll carry both in our adapter as a named extension until AdCP has an equivalent, and drop the extension when it lands.

  16. bokelley commented on Nov 2, 2026

    @bokelley
    Contributor

    Published a concrete proposed 3.4 design under #7782 incorporating this evidence: account identity discovery without a product, per-candidate authorization/eligibility diagnostics, and typed package bindings.

    It includes proposed wire shapes, rollup rules, pagination and visibility semantics, failure handling, creative validation, and acceptance cases. Discovery shape, binding placement, and connection terminology are explicit recommendations for review, not recorded decisions. The shipped 3.3 behavior stays unchanged; this is a design proposal, with implementation to follow after those choices are accepted.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    claude-triagedIssue has been triaged by the Claude Code triage routine. Remove to re-triage.media-buyIssue concerns the media-buy protocol domainrfcProtocol change — auto-adds to roadmap boardspec / protocol

    Type

    No type

    Projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions