Argued Agentic Ad Lab

Will Agents Replace Media Buyers? Six Roles, Task by Task

Six advertising roles read against the operation pointed at each task: which ones a schema carries and decides, which it carries and leaves to a person, and what the sort assumes.

18 min read 10 chapters

A judgement, signed. The evidence is checkable; the conclusion is mine to defend.

Agents are not going to replace media buyers, and the useful question is which half of your week they take. An agent will hold a floor. It won’t tell you the floor is wrong.

Almost everything you do is already a field in somebody’s schema, and only some of those fields have a decision procedure behind them. That is the sort: a task is going when the protocol carries the value and decides it, and it stays yours when the protocol carries the value and a person still has to supply it.

Two schema sets cover most of a media buyer’s week: AdCP, the Ad Context Protocol, which lists 64 task entries at release 3.1.13, and AAMP, Agentic Advertising Management Protocols, whose reference seller agent publishes 74 OpenAPI paths at v2.4.1 — different units on different scopes, so neither count is a coverage score and no verdict below turns on which is larger.

An r/adops commenter wrote the sort out in practitioner terms after Vox Media ran a sell-side deal negotiated agent to agent:

“The 2-minute deal automated trafficking and IO paperwork. That was never the job with a lot of value.

Someone still needs to decide what packages exist, what the exclusivity is worth, and which first-party data is exposed to agents. Agents execute deals. They don’t decide which deals deserve to exist.

We saw this movie with RTB. Everyone said programmatic would kill sales teams. It killed fax machines. The sellers who moved up the stack did OK.”

Nothing in the thread says which side of the market that commenter sells on, and it is the lowest-scored of three comments: the other two read “AI can not replace human interaction” and “Nothing is left for the humans.”

Which of your own tasks is on the path?

Take your week and sort it: does a protocol carry the value and decide it, or carry the value and wait for you?

RoleYour taskOperation pointed at itVerdict
TraffickerUploading and assigning creativessync_creatives (AdCP only)Going
TraderBudget, pacing and bid changesupdate_media_buy / package-updateGoing
Account executiveBuilding the IO and the deal recordPOST /api/v1/quotes, /deals, /ordersGoing
Account executiveSending a counter-offerPOST /proposals/{id}/counterGoing
TraderPulling delivery and pacing reportsget_media_buy_delivery, get_media_buysGoing
Yield analystAssembling a product against a specific briefis_custom products, priced by the sellerGoing
Yield analystApplying tier discounts and volume adjustmentsapply_tiered_pricing, five literals that never read the cardGoing, wrongly
Yield analystSetting the base rate card and the floorsPUT /api/v1/rate-card, values supplied by youYours
Yield analystDeciding what a package contains and what exclusivity is worthnone in either setYours
Yield analystDeciding which first-party signals agents may targetsignal_targeting_allowed, default falseYours
TraffickerApproving a creative, and holding the queue when one is pulled backstops at pending_review; impairment on reversalYours
PlannerJudging whether a returned product actually matches the briefbrief_relevance, which nothing verifiesYours
Ad ops managerHandover, ownership, escalation, makegoodsnone in either setYours
TraderDeciding whether the campaign was worth runningperformance_index, which nothing computesYours

Two rows are decided by absence rather than by pointing. none in either set means no operation anywhere in AdCP 3.1.13 or the AAMP corpus addresses the task, which is a weaker inference than a named field and one to recheck at each release, not a permanent result. One row reads Going, wrongly, and the label is doing real work: the tier-discount code runs unattended and prices podcast inventory at $12 against its own $15 card, so the keystrokes are gone and the checking is not. Going means a machine does the task, never that it does it correctly.

Every Going verdict is a claim about a schema, not about the seller you deal with. AdCP publishes enums/media-buy-action-mode.json with three values, self_serve, conditional_self_serve and requires_approval, declared on each entry of a product’s allowed_actions[] and on a buy’s available_actions[]. Whether “budget, pacing and bid changes” is Going on a given buy is a seller-side declaration, action by action, and requires_approval means an asynchronous human queue. What that adds up to for one buy is a rung on the autonomy ladder.

If your week is search and paid social

One audience gets nothing out of that grid. The r/adops poster asking whether Claude Code will “eventually replace performance marketers, or will they just make great performance marketers even more powerful?” is asking about software neither body specifies. AdCP’s channels enum carries search and social, enums/specialism.json defines sales-social as a “Social media advertising platform with self-service flows”, and package-update carries keyword_targets_add, all of it addressed to a publisher’s sales agent. The self-service auction accounts where a performance marketer works are not sales agents and speak no MCP; the AdCP agent registry’s sell side is publishers, SSPs and ad networks, with nothing named Google, Meta, Amazon or TikTok listed. Whatever automates that job ships from inside those platforms, on their schedule, with no schema you get to read first.

The test still transfers. Inside Performance Max or Advantage+, the tasks with a decision procedure behind them went years ago. What is left is the brief, the budget split, the creative call and the judgement about whether the reported number means anything, which is the same list as everyone else’s.

Six roles carry those rows, named in the grid’s first column: trafficker, trader, planner, ad ops manager, account executive, yield analyst. The six sections below take them in that order.

Trafficking is the strongest case, and it stops at pending_review

The trafficker is the most exposed role here and I’d say so to anyone hiring into one, though not on AAMP’s account. AAMP’s shared wire contract models creatives as first-class objects: Creative.json, CreativeApproval.json (“A durable seller-side creative approval decision”) and Assignment.json, which “Binds a creative to a line with rotation rules”. The seller agent’s core models define Creative, CreativeManifest, CreativeAsset, a ReviewStatus of pending, approved and rejected, and a RotationMode. Creative.json even says creative_id is issued “when the buyer registers the creative through the seller’s API”. No such path exists. Neither the seller agent’s 74 paths nor the buyer agent’s 13 mention a creative. That part stays in email.

AdCP is where the job goes. sync_creatives requires idempotency_key, account and creatives, takes up to 100 creatives per call, and adds assignments, delete_missing, dry_run and validation_mode, which is bulk upload, package assignment and library reconciliation in one retryable request. The tag-sheet reformatting an r/adops poster built a GPT for, because doing it in Excel “would also introduce the risk of manual errors”, is the task it was designed to delete.

Where the two stacks split is on whether creative approval is human work at all. AdCP’s creative-status enum has six values, and the ones that cost you staff are the reverse transitions: an approved creative can go back to pending_review, to suspended on recoverable authorization loss, or to rejected on post-approval revocation, and on the last two sellers MUST surface an impairment on every active media buy referencing it. The creative event and that impairment are, in AdCP’s words, “paired but distinct signals” with no ordering guarantee, reconciled by comparing list_creatives and get_media_buys snapshots. A creative can be pulled back into the queue mid-flight, on somebody else’s policy call, while money is running.

AAMP answers the same question in code. models/change_request.py maps ChangeType.CREATIVE to ChangeSeverity.MINOR, services/order_service.py sets any minor change to APPROVED with an approved_by of "system:auto-approve", and docs/api/change-requests.md prints the rule in a table with “creative swap” as its example. One protocol treats a creative change as a live risk to an in-flight buy; the other waves it through as low impact. Which one your counterparty implemented decides whether you staff a review queue at all. One qualification: no path on either AAMP agent moves a creative, so whether that auto-approve branch is reachable in a live deployment is not something the repository settles.

Boostr’s announcement of the Vox deal quotes Lauren Winter, Vox Media’s head of advertising operations, saying her team “simply accepted the buy and verified it in their ad server to launch the campaign.” That is a vendor press release about the vendor’s own launch deal: evidence the deal happened, and a record of what the parties chose to claim. It never says how the creative reached the ad server, which is the step a trafficker is asking about.

The trader’s daily loop

Where the trafficker’s exposure ends at a queue, the trader’s ends at a field list. update_media_buy carries a packages array of package-update objects, and one of those objects has budget, pacing, bid_price, impressions, start_time, end_time, paused, optimization_goals, targeting_overlay, keyword_targets_add and negative_keywords_add, plus canceled, catalogs, creatives and creative_assignments. Read that list next to what a trader does between nine and six. It’s a complete description of daily optimisation, it fits in one payload, and the request carries an optimistic-concurrency revision that sellers must reject on mismatch, so two agents can’t clobber each other. That guarantee binds the seller only once the buyer sends the field: revision is “Optional for backward compatibility,” and a request omitting it gets last-write-wins. get_media_buy_delivery and get_media_buys cover the reading half of the loop.

An r/adops commenter used to spend two hours a day “manually tweaking CPMs and budgets across 40+ line items to hit delivery targets”, which is budget and bid_price on a loop. The only reply asks whether “used to” means they solved it or left ad ops, and gets no answer. I don’t think that survives, and I wouldn’t spend career capital defending it.

The counterexample sits in the same protocol. provide_performance_feedback lists performance_index among its required fields, and nothing in AdCP computes that index or decides whether the campaign it summarises was worth running. Carried, not decided. Emily Proctor of OMD, at Digiday’s Programmatic Marketing Summit, named the awkward part for anyone two years into the job: the skill being automated is the one that qualifies you to supervise it, so “those manual skills, or at minimum understanding what it takes to get it done manually” stay essential.

What a planner still owes the response

The planner’s equivalent of that loop runs once, at the front: one brief out, a shortlist back. The operation underneath is real capability. get_products requires only buying_mode, accepts a natural-language brief and a structured filters object together, and returns products with brief_relevance, forecast and pricing_options, so a planner can put one campaign in prose to a dozen sellers and get structured availability back in fields.

What comes back is a claim. Three of the 23 listings in the public AdCP registry returned a product array to an anonymous caller on 12 August 2026 — Cora AI, Equativ and No Fluff Advisory — and Cora AI returned the same four products to a US CPG connected-TV brief, to a German-language B2B podcast brief specifying audio only, and to a brief one character long. The four are Korean FAST CTV and Korean display carrying countries: ["KR","US"], and each brief_relevance, defined in core/product.json as “Explanation of why this product matches the brief,” read “Matched against buyer brief:” followed by the brief echoed back. These are small catalogues, one to five products, two of them self-labelled demo or interop, and on a catalogue of four, returning everything and matching everything are indistinguishable until the brief is meaningless. A structured request is a different mechanism with a different result: filters.channels: ["audio"] to that same endpoint returned zero products, which is the vendor-questions page’s measurement rather than this one. A planner who reads it as a shortlist has been handed someone else’s inventory.

Amy Porter of RPA, an agency that has run an agentic buy, told Digiday there is a “real risk they could also obscure critical decision-making if advertisers rely too much on AI.” She is describing a governance problem and I’d call it a QA problem, which is a demotion and also a promotion: nobody writes a policy that catches this, and one person running one check does. So make the check standing rather than occasional. Send every agent you are asked to trust a brief its inventory cannot possibly match, then read countries, publisher_properties and delivery_type on what comes back; an agent that returns its whole catalogue to a one-character brief is one you gate or drop, not one you put in front of a planner. Opacity is what happens when nobody runs that check before a response becomes a plan.

Ad ops management is the role with no operations at all

Each of the three roles so far had an operation pointed at it. The fourth does not. An ad ops lead at a mid-sized publisher posted their team’s workflow to r/adops: pre-sale feasibility, campaign handover, chasing assets and approvals, QA across sites and devices, delivery troubleshooting, post-campaign wrap and makegoods. The stated pain was “a lot of time lost to ‘who owns this bit’ and ‘where do I find the source of truth’.” The quick wins they asked for were escalation paths and a “definition of ready” before trafficking starts, and the top reply told them to name one or two people as documentation owners, which is the opposite of a protocol surface.

I’d put this role at the bottom of the exposure list, because the coordination an ad ops manager does all day is what both stacks assume has already happened by the time a request goes out. AdCP’s governance layer has check_governance, sync_plans, get_plan_audit_logs and report_plan_outcome, and those record whether a spend was authorised against a declared plan. Ownership, escalation, handover and makegood policy have no operation anywhere in either corpus. The nearest proposal, adcp#6381, asked that buyer-side human approval travel as a claim in the governance token and was closed as not planned on 13 August 2026, on the ground that audit_log_pointer and get_plan_audit_logs already carry the human-review chain.

The AE’s negotiation is a state machine, and a retry can spend your rounds

The account executive has the opposite problem: an operation for every step. Priced negotiation lives on AAMP’s seller agent. POST /proposals/{id}/counter evaluates a buyer’s offer, appends a round to a persisted NegotiationHistory and emits a NEGOTIATION_ROUND event, bounded by a max_rounds budget of three to six depending on the buyer’s tier; POST /api/v1/quotes, /deals, /orders and /api/v1/change-requests carry the rest of the paper trail. The IO is an API call now, and if your week is IO throughput, that week is going.

That budget is not on the wire. STRATEGY_LIMITS in models/negotiation.py gives a public-tier buyer 3 rounds and an 8% cumulative concession cap, an advertiser-tier buyer 6 and 20%, while the shared Negotiation.json says the seller’s floor, base price, strategy and concession limits are “deliberately absent from the shared schema”. A buyer learns rounds_remaining from a response, mid-negotiation. Which budget you got is decided by whether your API key is recognised: the negotiations router caps a claimed tier at a verified ceiling and records in its own comment that “anonymous self-asserted identity claims floor to PUBLIC”, so an unrecognised key is negotiated against at 3 rounds and 8% with nothing in the exchange saying so.

The machinery is young in a way that matters commercially. The canonical entry point, POST /api/v1/negotiations/messages, takes the shared NegotiationMessage, whose schema requires idempotency_key and action and returns a 422 without it. seller-agent#51 showed nothing reads the key: two identical posts, same key, same body, minted two rounds and spent two units of a budget that starts at three. That issue closed on 11 August 2026, and at v2.4.1 the negotiation service, the negotiation engine and the negotiations router contain no occurrence of the string idempotency between them. A closed issue and an unread field live in the same repository, so the way anyone finds out is a client retrying a timed-out counter and walking a real deal into a rejection it never intended.

Greg Langer of Havas Media Network, at the same Digiday summit, on what an agent brings to a negotiation: “There’s no human emotion in it. They don’t know the relationship.” What that leaves an AE is the half of the week that was never in the paperwork, plus an obligation nobody has staffed: watching a state machine spend rounds on your behalf.

Yield analysis: the rate card is a PUT nobody prices from

The yield analyst’s split is the cleanest of the six. The tiering and the discount ladder are code. The base card and the floors are a human PUT.

The AAMP seller agent exposes GET and PUT /api/v1/rate-card, the PUT gated behind an operator API key and documented like this: “Publishers should update this when their ad server rate cards change. The pricing engine uses these values as base prices before applying tier discounts and volume adjustments.” The PUT writes a storage key, rate_card:current. The flow that prices a deal, flows/deal_request_flow.py, contains no occurrence of the string rate_card. Its apply_tiered_pricing step holds a five-entry table instead, display 12.0, video 25.0, ctv 35.0, mobile_app 18.0 and native 10.0, with discounts topping out at 15% for an advertiser-tier buyer. The step feeding it picks a product type by lowercased substring, under the comment “Simple parsing for demo”: a request for podcast inventory matches no branch, falls to display and prices at $12, while the rate-card endpoint’s own default table lists audio at $15.

The consequence has a public reproduction. In seller-agent#57, still open, POST /api/v1/deals/curated takes a product_id with no existence check, falls through to base_cpm = 12.0 # Default and mints a persisted deal with status: "confirmed"; the reporter typos one letter of a real product priced at $45 base and $35 floor and gets a live deal at $13.20 total CPM. Two qualifications the issue title leaves out: the promise it breaks, “never a fabricated price”, is an inline comment two lines above that default rather than the docstring, and the route takes no API key but still 404s on an unregistered curator, so the fabricated deal is open to a registered curator rather than to anyone. All of it is read out of IABTechLab/seller-agent at v2.4.1. Whether a transacting seller runs that pricing path, a fork of it, or its own service behind the same OpenAPI document appears in neither corpus nor in the trade press, so the failure is verified in the repository and unquantified in the market.

The yield analyst’s failure mode is not a floor that gets missed. It is a floor that is confidently wrong and enforced at machine speed, on every deal, until somebody reads the number.

Product authoring is the other half of the yield analyst’s week, and it is moving faster. core/product.json carries pricing_options as a required array, plus exclusivity and signal_targeting_allowed, which defaults to false and bundles signals into the product terms while it stays there. Nothing in 3.1.13 is a buyer-facing write against a product. is_custom marks a product generated for one brief, paired with an expires_at; AdCP’s documentation calls these dynamically generated products, and its worked example custom_abc123 carries two pricing options rather than one headline price, a CPM option with a floor_price of $5.00 and guidance of p50 $8.00, p75 $12.00, and a CPC option at $0.50. Machines do author priced products, on demand, today. What they do not do is decide the yield policy they price against. Custom products move product authoring from per-deal to per-policy, and the per-policy version is the better-paid one.

A schema is not a deployment

Every verdict above is read out of a schema or a reference implementation. AdCP 3.1.13 is a shipped release with every operation named here in the box. A schema is not a deployment, and the distance between the two is most of your risk calculation. The 12 August 2026 sweep of the AdCP agent registry found 23 listings, 13 completing an anonymous MCP handshake, 6 exposing get_products to an unauthenticated caller and 3 returning a product array.

Read the middle number carefully, because it is easy to over-read. Gating discovery behind authentication is a defensible choice and plenty of sellers will make it deliberately, so the funnel measures how open the registry is rather than how real it is. A catalogue its own operator labels a demo is a different matter. Equativ’s one product calls itself “Synthetic data for demo purposes only.”

Transacting is a third count. Eight named deployments have run a buy since December 2025, nearly every number attached to them published by a company selling agentic software, and Magnite chief executive Michael Barrett’s 2027 forecast of roughly $700m is a rounding error against one SSP’s annual volume, as the ledger works out. The direction is settled and the timing is not, and anyone quoting you a number of months is guessing.

The agents added a shift

Sorting tasks into going and staying assumes the agents are net subtractive from a workload. For the people posting about them, they aren’t. The r/programmatic thread that asked “Are we all burnt out from AI yet or is it actually helping anyone or anything?” opens on internal rollouts that are “actually exhausting and time consuming and feels like a second job,” and the poster asks the career question directly: “Will my new job be creating/maintaining/managing AI agents instead of completing the work to service clients myself?” Its highest-scored reply, at 17, is not about tooling: “AI has enabled me to do a LOT more than I was doing a few years ago, which lead to massive increases in my salary… But it doesn’t help me at all with the stress of being responsible for more. Lately my best days at work are the ones where I’ve stopped caring.” The same commenter, further down, on what building the automation cost: “a crushing workload that I can’t sustain long term.”

That is the failure mode the protocols make more likely. Watching a round budget spend itself is new work. So is holding a creative queue that can reopen while money is running, and both land on the same people whose manual work was removed, with no published headcount model in which the two cancel.

The headcount question is not settled here either, and it will not be settled by a schema. If the agents remove the hours and your scope of work is priced in hours, the answer gets written into a remuneration negotiation between a client and a holding company, which is why the holding companies have the strongest incentive of anyone to make this work and the least incentive to publish what it did to their staffing.

Across the five Reddit threads cited here, one comment names money: programmatic roles that paid $160,000 to $190,000 a couple of years ago now advertised at $110,000 to $140,000. It is a single anonymous data point and the only salary figure anywhere in these threads. The same comment describes a mechanism that is easier to check: roles consolidating into titles like “principal of integrated media”, teams of one or two covering full channel performance, and senior-director interviews weighted toward hands-on AI tooling over client experience.

So if most of your week sits in the Going block of that table, spend this quarter buying yourself one task from the Yours block: take the floor-setting off your yield lead, or own the QA pass on what a sales agent returns. Getting faster at the Going block is worth very little, because the thing getting faster is the thing being automated.