Skip to content

update_media_buy: refuse start_requires_all_creatives_approved: true for a seller that does not declare creative_start_policy #1869

Closed

Description

@bokelley

AdCP 3.3 adds packages[].start_requires_all_creatives_approved to update_media_buy (and to package creation). The field's schema text says a seller without media_buy.features.creative_start_policy: true "MUST reject true with UNSUPPORTED_FEATURE before mutation". false is always allowed: it is the default, serving approved creatives as they are approved.

That rule is the same for every seller. It follows from the seller's own capabilities, so the SDK can enforce it the way it enforces the other 3.3 seller-side rules (#1668). Today it doesn't. A request with true reaches a seller that never declared the feature, and a seller that doesn't know the field ignores it. The buy then starts serving before every creative is approved, which is the opposite of what the buyer asked for.

Repro (10.0.0b12, AdCP 3.3.0-beta.4)

import asyncio
from typing import Any

from adcp.server import ADCPHandler
from adcp.server.mcp_tools import create_tool_caller

seen: list[Any] = []


class Seller(ADCPHandler[Any]):
    """A 3.3 seller that does not declare media_buy.features.creative_start_policy."""

    async def update_media_buy(self, params, context=None):
        seen.append(params)
        return {"media_buy_id": params["media_buy_id"], "status": "active"}


async def main() -> None:
    call = create_tool_caller(Seller(), "update_media_buy")
    await call({
        "adcp_version": "3.3-beta.4",
        "idempotency_key": "a" * 16,
        "account": {"account_id": "acct_1"},
        "media_buy_id": "mb_1",
        "packages": [{"package_id": "pkg_1", "start_requires_all_creatives_approved": True}],
    })
    print("handler called:", bool(seen))   # True


asyncio.run(main())

What we do today

The Scope3 embedded sales agent refuses it itself. start_requires_all_creatives_approved is in its refused package-field table (src/core/media_buy_update_fields.py, a rule with honored_value=False), so true is UNSUPPORTED_FEATURE and false is accepted. Every seller on the SDK needs the same code.

Suggested fix

In the decisioning dispatcher, before the platform method runs: when the seller's capabilities do not declare media_buy.features.creative_start_policy: true, refuse any create_media_buy / update_media_buy package that sets start_requires_all_creatives_approved: true. Use UNSUPPORTED_FEATURE, recovery: "correctable", and a field pointing at the package (e.g. packages[0].start_requires_all_creatives_approved). Let false through. Apply it only when the handler runs on AdCP ≥ 3.3, as changed_paths enforcement does.

Activity

  1. bokelley commented on Nov 8, 2026

    @bokelley
    ContributorAuthor

    Re-scope to control_media_buy. Under the AdCP 3.3 closed-schema triage (adcontextprotocol/adcp#8501, PR adcontextprotocol/adcp#8767, which merges after the Nov 13 consent close), start_requires_all_creatives_approved is withdrawn from create_media_buy and update_media_buy packages, because those are deprecated compatibility tasks. It stays on control_media_buy (package control), alongside get_media_buys readback, update_creative_start_policy and media_buy.features.creative_start_policy.

    So the enforcement belongs on control_media_buy: a seller without media_buy.features.creative_start_policy: true MUST reject true with UNSUPPORTED_FEATURE before mutation. The rule is the same as the one this issue describes. On create_media_buy and update_media_buy the field disappears from the 3.3 schema, and an open schema ignores it (DR-0009), so there's nothing to enforce there.

    🤖 Generated with Claude Code

  2. added a commit that references this issue on Nov 8, 2026
    7baf947
  3. bokelley commented on Nov 8, 2026

    @bokelley
    ContributorAuthor

    Fixed on next by #1872 (7baf947), scoped to control_media_buy per the comment above. For a handler on AdCP 3.3 or later, the dispatcher refuses packages[].start_requires_all_creatives_approved: true with UNSUPPORTED_FEATURE (field packages[N].start_requires_all_creatives_approved, recovery: correctable, mutation_outcome: not_applied) unless the seller's own capabilities declare media_buy.features.creative_start_policy: true. false and omission always pass. create_media_buy / update_media_buy are not gated (adcontextprotocol/adcp#8767).

    🤖 Generated with Claude Code

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions