Skip to main content
accept_proposal is the single terminal action for the compact proposal lifecycle. It accepts exactly one committed immutable proposal snapshot. committed retains the AdCP 3.1 reservation guarantee: the seller has locked the proposal’s commercial terms and reserved its inventory until expires_at. Finalization creates that hold; acceptance consumes it and creates, amends, or cancels the MediaBuy. A committed proposal whose effective hold policy is terms-only locks terms only: expires_at is an acceptance window, and the inventory guarantee below does not apply. An experimental ranked hold reserves a position in the seller’s queue, and the guarantee applies only at first position. That is a committed proposal whose hold_policy has available: false, whatever the agent default, or one without hold_policy from a seller declaring hold_semantics: "terms_only" in get_adcp_capabilities. Depending on proposal_kind, acceptance:
  • creates a new MediaBuy;
  • applies an amendment to an existing MediaBuy; or
  • applies a negotiated cancellation.
The proposal already carries the commercial terms, so the request does not repeat packages, dates, targeting, or creatives. Request schema: /schemas/3.3.0-beta.0/media-buy/accept-proposal-request.json
For scalable proposals, include total_budget. Acceptance may also set a hard aggregate daily_budget_cap and optional shared budget_cap_timezone; these constrain execution without changing negotiated pricing. If the proposal carries a signature-required insertion order, include io_acceptance. If the proposal belongs to an opportunity, acceptance closes that planning cycle with accepted_with_seller; callers may repeat the matching opportunity ID explicitly, while the accepted proposal retains the association. Success returns the resulting MediaBuy identity, the proposal with proposal_status: "accepted", and purchase_bindings[] mapping proposal purchase positions to package IDs for later creative assignment. Acceptance is idempotent. The seller atomically verifies proposal_id, proposal_terms_digest, expiry, and any base_media_buy_revision; stale or mismatched acceptance never applies different terms. The optional name is buyer-authored MediaBuy display metadata, not a commercial proposal term. A supplied request value wins over proposal.name, is persisted unchanged, and is echoed outside accepted_proposal; it is not covered by proposal_terms_digest or the accepted snapshot’s terms_digest. When an acceptance creates a MediaBuy and the request omits name, the seller may seed it from proposal.name only when that value already satisfies the MediaBuy constraints: non-whitespace and no longer than 255 characters. The seller must not silently truncate or rewrite a proposal name to make it fit. A seeded name is then persisted and reported like any other name. When acceptance creates a new MediaBuy, the request MAY carry predecessor_media_buy_id with lineage_reason (renewal, terms_change, migration, sunset, or replacement) to record that the new buy succeeds an earlier buy of the same seller and account. Lineage is experimental (media_buy.lineage in experimental_features). The link is forward-only and set once here; the seller does not modify the predecessor. The seller rejects an inaccessible predecessor with MEDIA_BUY_NOT_FOUND, and rejects either field without the other, or either field on a proposal whose proposal_kind is media_buy_update or media_buy_cancellation, with INVALID_REQUEST. The fields are outside proposal_terms_digest. See Media Buy Lineage. To receive scheduled reports, a reporting_webhook registered on this task needs a buyer-supplied operation_id (see Reporting webhooks); sellers MUST NOT send media_buy_delivery reports to a registration that omits it. A completed acceptance MAY include warnings[] for non-blocking observations at the commitment boundary. Each warning identifies its package through the returned media_buy_id and purchase_bindings[].package_id; a continuing condition is also readable as an indicator through get_media_buys. The seller may instead return input-required before consuming the proposal. Warnings never appear on failed or submitted arms. A submitted arm MAY carry expected_confirmation_at (added in 3.3) when confirmation depends on a scheduled event such as a booking round or manual review window; buyers SHOULD schedule a status check for that time instead of polling tightly. It is an estimate, not a commitment. See Expected confirmation time. Under inventory_hold semantics (the default), an unexpired committed proposal cannot be rejected merely because its reserved inventory was sold elsewhere. A ranked hold (experimental, hold_policy.rankable: true) has that guarantee only at first position: while another buyer’s hold ahead of it is live or has been accepted, the seller MAY decline with PRODUCT_UNAVAILABLE or return submitted while it settles the holds ahead, and the proposal stays committed until expires_at. Under terms_only semantics (including hold_policy.available: false) no inventory was reserved. A seller that books the inventory itself on accepting such a terms-only committed proposal MAY decline with PRODUCT_UNAVAILABLE when it can no longer be booked at the committed terms; the proposal then stays committed until expires_at, with no MediaBuy created. It MUST NOT silently substitute products or reprice. Once expires_at passes, the hold lapses (or the acceptance window closes) and the seller returns PROPOSAL_EXPIRED; the buyer must request and finalize a fresh proposal.

Definition pins

Before mutating any state, the seller re-resolves every entry in the proposal’s commercial_terms.definition_pins[] and compares its current digest with the pin’s content_digest (see Definition pins). The proposal ID and proposal_terms_digest can verify while a published definition has changed, because terms_digest covers the pin and not the definition it names. A failing pin returns REFERENCE_DEFINITION_CHANGED with no MediaBuy created or amended, one error per drifted pin in errors[]. Each error looks like this:
The buyer calls refine_proposals to obtain fresh pins, reviews the changed definition, and finalizes again. A definition withdrawn from a successfully fetched document is changed and omits current_content_digest; a document that cannot be fetched or parsed is transient, so the seller returns SERVICE_UNAVAILABLE without mutating state. An idempotent replay of an acceptance that already completed returns the stored result without re-verifying pins.