Connect Claude or Cowork
Add the hosted MCP connector, sign in, and verify your account with a read.
Install the buyer plugins
Add Apostra’s buyer workflow skills to Claude, Cowork, or Claude Code.
Buy media
Set up your account, prepare campaigns, and review what is needed to launch.
Sell media
Set up a Seller Account, connect inventory, and manage incoming buying work.
Read results
Understand delivery and reporting for your account.
/v2/ in their URL; follow their stated API and
workflow requirements.
If you want to build a marketing assistant, automate a report, connect business
data, or write an advertising agent, follow Build software.
The developer quickstart installs apostra-developer in Codex or Claude Code.
Open and read campaigns with an assistant
An assistant opens the Campaigns Page withopen_campaigns_page. To find a
campaign or read its details, it uses search and get with kind: "campaign".
These reads work whether or not the host can render the Page.
For example, after selecting your Buyer Account, use the advertiser ID from
your account and the campaign ID returned by search:
buyer_campaigns_page_search and
buyer_campaigns_page_get. Those tools are app-only: MCP hosts keep them out
of the assistant’s tool list, and the widget calls them with its signed Page
capability. Using the generic campaign reads is the expected agent workflow.
See the V3 tool reference for current schemas
and campaign read options.
Private advertiser notes
Buyers can keep private notes on an advertiser they own, including context about working with a particular agency or operator. Save them withsave_advertiser.privateNotes and read them with get(kind: "advertiser")
from the owning Buyer Account. Your existing advertiser permissions still
apply. Another buyer account cannot read or change these notes.
There is no additional plan requirement or charge.
privateNotes leaves its value
unchanged; sending null clears it. You can also supply notes when creating
an advertiser. Existing advertisers start with privateNotes: null.
Sellers cannot read or change these notes, including through sponsored-client
or own-supply workflows. Notes are not sent to sellers, brand registries, or
seller agents, and do not set prices, discounts, or shared commercial terms.
They are saved context for you and your authorised buying agent to retrieve;
saving a note does not automatically apply it to campaigns or buying decisions.
Activity history keeps diagnostic identifiers and outcomes without retaining
the note. Advertiser calls omit audit copies of request, response and error
text. Assistant requests omit this text from the start, because they may
use private advertiser context later. This also protects requests cancelled
before the advertiser work begins.
save_advertiser_instructions is a separate, seller-only tool for the seller’s
own CRM notes about an operator-and-brand pair. To save your buyer context,
use save_advertiser in your Buyer Account.
Read an advertiser’s channels and brand countries
get(kind: "advertiser") returns the channels and brand countries that
save_advertiser writes, so you can check what you saved. search(kind: "advertiser") and the advertiser that save_advertiser returns carry the same
fields.
An advertiser created without
channels reads back channels: [] and
channelsKnown: false. Sending channels: [] to save_advertiser is an
answer, so the same read then returns channelsKnown: true.
The advertiser’s free-text description is not part of V3: it is not returned
and save_advertiser does not take it.
Advertisers for sub-brands of one brand
In one Buyer Account, two active advertisers cannot share a brand in the same country, whatever their currencies.save_advertiser refuses a second
advertiser on a brand that another active advertiser already uses in a country
it also covers; an advertiser with no countries covers every country. The
refusal reason is brand_identity_exists or brand_identity_overlap.
This is deliberate. Sellers key the account they hold for you on the brand,
following AdCP: the brand domain, an optional brand ID, and the countries. Two
advertisers with the same brand would land in the same account at every
seller, and their spend and seller invoicing would merge.
When one company runs separate lines of business under one domain, for
example energy and insurance invoiced separately, AdCP models each line as a
sub-brand with its own brand ID. Each sub-brand is a different brand to a
seller, so each gets its own advertiser and its own seller accounts.
Apostra uses only sub-brands the brand itself publishes. A brand ID nobody
published is never sent to a seller. To publish sub-brands, put a house
portfolio at https://<brand domain>/.well-known/brand.json. Each brand ID is
lowercase letters, digits and underscores, and the brand chooses it:
save_advertiser: send the brand domain in brand and the sub-brand’s
ID in brand_id, for example brand: "example.com" and
brand_id: "insurance". The advertiser’s brandIdentity.brandRef then reads
back with that brand_id. An advertiser already on the domain with no brand
ID keeps working; it does not conflict with a sub-brand advertiser.
To see which brands a house publishes, preview the domain with
save_advertiser({ resolveBrand: "example.com" }). When the domain’s own
brand.json or hosted profile is a house portfolio, the preview’s brand lists
them in houseBrands, each with its AdCP brandRef, a name and a logo when
the house publishes one. Create the advertiser with that brandRef:
- A brand kept inside the house’s file reads as the house domain plus its
brand ID (
{ "domain": "example.com", "brand_id": "insurance" }), so send bothbrandandbrand_id. - A brand that publishes its own brand.json on its own domain reads as that
domain alone (
{ "domain": "example-insurance.com" }), so send onlybrand.
houseBrandsTruncated: true means
the house publishes more, and you can still create an advertiser for any brand
ID it publishes. A domain whose record is a community record lists no brands.
Many large houses publish no list of their own brands. For a house like that,
the preview suggests the brands the
AgenticAdvertising.org registry records
under it, in suggestedBrands. Each suggestion is a brand domain
({ "domain": "tide.com" }), with a name and a logo when the registry has
them, and houseLink, which says how the registry knows the brand belongs to
the house:
Only
confirmed means the house has confirmed the brand. Treat every other
suggestion as a lead to check. To use one, preview or create an advertiser on
its own domain (brand: "tide.com", with no brand_id). A suggestion never
lets you use the house’s own brand IDs. The list shows at most 100
suggestions, and suggestedBrandsTruncated: true means the registry records
more. Links that only a community record makes are left out, because anyone can
edit one. When the house publishes its own brands, the preview shows
houseBrands and no suggestions. If the registry does not answer in time, the
preview comes back without suggestions; preview again to retry.
A brand_id the brand’s own brand.json or hosted profile does not list is
refused with VALIDATION_ERROR and details.reason brand_id_not_published,
and nothing is created. A community record in the registry, which anyone can
edit, does not count. brand_id is set when you create an advertiser; sending
it to change an existing advertiser is refused with NOT_IMPLEMENTED. The
Apostra assistant can draft the brand.json for you.
In the Create advertiser task, Look up brand on a house domain shows a
Brand picker with the brands the house publishes. Pick one to create the
advertiser for that brand, or keep the whole house. The advertiser name fills
in from the brand you pick, and you can edit it.
Save an HTML creative with its size
An HTML5 file or HTML tag creative must have a size when you save it withsave_creative. A creative saved without one is refused with a
VALIDATION_ERROR error that says what is missing, and nothing is saved. A
seller that lists one format per banner size turns an unsized creative away
later, after the buy is staged.
The creative has a size when any one of these holds:
- Its HTML declares one, for example
<meta name="ad.size" content="width=300,height=250">, a Campaign Manager<ins class="dcmads" style="width:300px;height:250px">tag, a Campaign Manager ad tag’s single<iframe width="300" height="250">or its one;sz=300x250in the ad-serving URL, or a fixed pixelwidthandheighton<body>. - You pass
formatParamswith a fixedwidthandheight. - You declare it responsive with
formatParamsthat carry any ofmin_width,max_width,min_height, ormax_height.
formatParams carries the same keys as an AdCP canonical format’s params, but
it is an Apostra field: AdCP does not yet let an HTML creative state its own
size, and an
upstream proposal
would. When AdCP ships it, the size moves there and formatParams is removed
for HTML creatives, with notice in the changelog. Use it on a create that carries assets, with no creativeFormatId and no
formatOptionRef. Name formatKind: "html5" for a file, or "display_tag" for
a tag:
formatParams takes one size mode: a fixed width and height, or a
responsive range. A width without a height, a mix of the two, or a sizes
list does not count as a size. A responsive creative has no fixed size to send a
seller, so a product that requires one fixed size still refuses it when you
launch the buy. A size in the HTML takes precedence over formatParams.
save_creative accepts every parameter defined by the AdCP schema for the
chosen formatKind, alongside the established compatibility parameters. If a
parameter defined by that schema has an invalid value, the call returns
VALIDATION_ERROR that identifies the parameter and expected type. Established
catalogue-only compatibility parameters remain accepted and are logged.
Use the declared asset slots for a canonical creative
When you save a creative withformatKind, each text entry in social
must use a slot declared by that canonical format. save_creative returns
VALIDATION_ERROR before saving a text component in an undeclared slot.
For video_vast and audio_vast, send the VAST tag as a typed VAST asset at
the vast_tag slot. Do not send the tag as text in social.components, such
as a video_url text slot. A typed VAST asset remains accepted.
Save a creative with several headlines or descriptions
Some formats take a list of text items in one slot, such as a Google responsive search ad with 3 to 15headlines and 2 to 4 descriptions. Send
one social.components entry per item and name each by its position:
headlines[0], headlines[1], and so on. Naming the same slot twice, or
sending headlines without an index, is refused.
formatOptionRef, the campaign’s product defines the slots: how many
items each list takes, the longest item (30 characters for a Google headline,
90 for a description), and which slots are required. save_creative returns
VALIDATION_ERROR naming the slot when a list has too few or too many items, an
item is too long, or a slot is not declared. Without a formatOptionRef, the
canonical responsive_creative format’s own lists, headlines (up to 15) and
descriptions (up to 5), take indexed items.
Find connected Meta formats
When you searchcreative_format for an advertiser, Meta’s declared formats
appear when your buyer account has an active selected Meta ad account. You do
not need to map that account to the advertiser first. Without an active
selected Meta account, the search does not return Meta formats.
Checked against its format when you save
save_creative checks a creative against its format when it saves, not only
when a seller receives it. With a formatOptionRef, the format is the
seller’s option. Without one, it is AdCP’s default for the formatKind: the
slots the pinned AdCP version gives that kind, such as image_main,
headline, primary_text, cta and landing_page_url for image.
- Slots. An asset or copy sent to a slot the format does not declare is
refused, naming the slot and the slots the format does declare. An
imagecreative has nobrand_nameslot, sosocial.brand_nameon one is refused. An asset sent without a slot must be of a type some slot takes: a video on animagecreative has none and is refused. - Required slots. A complete creative must fill every required slot. Copy a
seller’s option requires, such as a headline, is enforced once you pin the
option. Without a
formatOptionRef, the required slot is the kind’s main asset (forimage,image_main), and it is required once the creative carries any media. A draft (mode: "draft") may leave required slots empty, and an unpinned creative with only copy, a brief or a click URL is saved with a readiness warning. - Copy length. A text slot can declare two lengths, and
searchandgetwithkind: "creative_format"return both on the slot.max_charsis the longest copy the seller’s API accepts.save_creativerefuses copy over it, naming the slot, the limit and how long your copy is.recommended_charsis the length past which the seller may truncate the copy in some placements. Copy over it is saved as written and the save returns a warning for the slot. OpenAI Ads chat cards are an example: the API accepts a headline of 3 to 50 characters and a description of up to 100, and OpenAI Ads Manager flags a headline over 32 and a description over 54 as liable to be truncated. The Creative Library shows a live count against both numbers as you type. If copy overmax_charsreaches a sync, for example a creative saved before the limit changed, we do not send it. The creative’s link to that seller readsfailedwith the reasoncreative_refusedand you as the party who must act (see Creative sync outcomes). V2 reads carry the slot, the limit and your length in the link’srejection_reason; V3 shows the reason only, so check the slot againstmax_chars. Copy overrecommended_charsis sent as written, never truncated by us. - Media limits. The size, duration and file size measured from each asset
must meet the limits of the slot it fills. The refusal names the slot and the
limit. A fact that could not be measured does not cause a refusal. Without a
formatOptionRef, aformatParamssize or duration you declare is the limit for the main asset.
Edit a creative’s copy and send it to its media buys
To correct the copy of a creative that already exists, callsave_creative with
creativeId and social (headline, and components for slots such as
body_text), with advertiserId for a Library creative or campaignId for a
campaign’s. Each slot you name replaces its text and the rest is left as it is.
The creative’s revision advances, so a seller can tell the copy changed. The
call does not contact any seller. Send save_creative again with creativeId,
advertiserId and resync: true to re-send the creative to the media buys it
is on, live ones included. A seller whose live ad can be edited in place, such
as OpenAI Ads, has that ad updated rather than a second one created. A social
URL component, such as the click destination or a tracker, replaces its slot the
same way, on a Library creative or a campaign’s. A URL slot the creative’s
format does not declare is refused. A click destination change on a creative
made from a single HTML file is taken only with campaignId, because it
reprocesses the markup.
The same checks apply to uploaded files you bind in sourceAssets and to an
image saved with sourceAssetRef, including images saved into a campaign. For
an image saved into a campaign with sourceAssetRef, save_creative also
refuses a save that omits copy every image format of the campaign’s products
requires. upload_creative_asset returns that list as
campaign_composition.required_authored_fields, so the upload task can show it.
If a campaign’s image format requires copy in a slot of the seller’s own, outside those fields, a save with sourceAssetRef is refused naming the slot. Save the same upload with sourceAssets (an asset_ref binding), the option’s formatOptionRef, and the slot named in social.components. upload_creative_asset returns an error, not an empty list, when it cannot read the campaign’s formats. Do not send required_authored_fields yourself: the field is output only.
Fit the formats of the campaign’s products
A creative you save into a campaign withcampaignId must fit at least one
format option declared by the products in that campaign’s media buys or
selected products. Attaching an existing creative to a campaign is checked the
same way. save_creative compares the creative’s formatKind, its
formatParams, and the size and duration measured from its main asset with
each declared option. When none fits, the call returns VALIDATION_ERROR on
format_params. The message names the difference and the closest declared
option, and nothing is saved.
A fact that could not be measured, such as the size of a file that could not be
read, does not cause a refusal. If no product in the campaign declares format
options yet, the creative is saved without this check. To see a product’s
options before you save, search with kind: "creative_format",
filter.advertiserId, and filter.productId. To bind a creative to one option,
pass it as formatOptionRef.
Duplicate a creative
To make an independently editable copy of a campaign creative, callsave_creative with campaignId, duplicateFromCreativeId, and an
idempotencyKey. The source must be visible in the selected Buyer Account,
belong to the same customer, be active in the named campaign, and have a
canonical formatKind.
Copy of <source name>. Do not send name
or any other creative field with this shape. A successful retry with the same
key returns the original copy with replayed: true; reusing that key for a
different campaign or source is refused. The copy has a new identity and does
not keep seller delivery state, approval, Library role, managed-source adoption
identity, or campaign membership outside the named campaign.
A creative saved before this rule stays readable and is not changed. It is
refused when you save it again without a size, including a change that has
nothing to do with size, such as renaming it. save_creative on an existing
creative cannot add a size, so create the creative again with formatParams or
HTML that declares its size. The V2 buyer API can replace the creative’s HTML in
place; see bring your own creative.
Archive and restore a creative
To archive a Library creative, callsave_creative with creativeId,
advertiserId and isArchived: true. The creative leaves every campaign and
media buy it was on, and its name is free to reuse.
To bring it back, call save_creative with creativeId, advertiserId and
isArchived: false. Send nothing else: any other field is refused with
NOT_IMPLEMENTED, so a change is never dropped.
- It is not on any campaign or media buy. Attach it again with
save_creative,creativeId,advertiserIdandcampaignIds. - A creative that was made inside a campaign comes back as a Library creative of the advertiser, not of that campaign.
- Sellers are not told. Attaching it to a campaign sends it to the sellers again.
NOT_FOUND. A restore through an advertiser you cannot access is refused
with ACCESS_DENIED before the creative is looked up, so the answer is the same
whether or not the creative exists. Restore is refused with CONFLICT when the
advertiser is archived (restore the advertiser first).
Archiving frees a creative’s name, so another creative may have taken it since.
Restoring it then is refused with CONFLICT on name. Send name with
isArchived: false to restore it under a new name in the same call:
name. A name sent for a creative that is not archived is
refused, because nothing is restored; rename that creative with an ordinary
update.
Send a campaign’s creatives again
When a campaign’s creatives never reached a seller, or you fixed something a seller needs, send them all again with one call:save_creative with
campaignId and resync: true. Send no creativeId, advertiserId, or any
other field with it.
outcome:
synced: every media buy took its creatives and none was left unassigned or skipped.partial: at least one media buy took its creatives, and another failed, was skipped, or left a creative unassigned.failed: no media buy took any creative. That includes a campaign with no media buy routed to a seller yet, and one whose routed media buys took none because every creative was unassigned or held. The call then returns an error that carries the same details.
mediaBuys gives each media buy’s status, with the error code and message when
it failed. skipped lists media buys that have no route yet or are not
submitted. creatives lists every creative and, for each media buy, whether it
was sent, failed, unassigned or held, with the reason. An unassigned
creative has a size or format that no package on that media buy declares: save a
creative in a size the media buy’s packages list. A held creative came from a
tag sheet and has no deliverable version yet.
Some sellers take creatives into their library but refuse to assign them to
packages in the same call. Apostra then sends the creatives without the
assignment and assigns them to the media buy’s packages in a second step, so the
send still reports sent when both succeed. The second step sends each package
its whole creative list, keeping the creatives already on it. If the seller
takes the creatives but refuses the package assignment, that media buy’s result
is an error with the code CREATIVE_PACKAGE_ASSIGNMENT_FAILED: the creatives
are with the seller but are not live on the buy. You get the same error when
Apostra cannot tell which creatives a package already holds. It then sends
nothing rather than a list that would remove them, and the package keeps the
creatives the seller has.
When a seller refuses one field of the send for every creative, that media
buy’s result is an error with the code UNSUPPORTED_FEATURE, and its message
names the field, for example “The seller does not support assignments on
creative sync, so the creatives were not sent.” The code and the field are the
seller’s own; the sentence is Apostra’s.
You can only send the creatives of a campaign you can act on. A campaign you
cannot reach reads as not found.
The Campaigns page offers the same call as Re-send creatives in a
campaign’s Creatives section, shown to people who can edit the campaign when it
has creatives. It reports how many creatives were sent, failed, unassigned or
held.
Add industry identifiers to a broadcast creative
Broadcast sellers identify a spot by its industry identifier and often refuse a creative that has none. Passindustry_identifiers to save_creative, on a
create or an update, as a list of { "type": ..., "value": ... } entries. The
field is AdCP’s own industry_identifiers, so the names and values match what
the seller receives. type is one of:
ad_id: the Ad-ID code, used for US advertising.isci: the ISCI code.clearcast_clock: the Clearcast clock number, used in the UK.idcrea: the IDcrea code, used in France.
VALIDATION_ERROR error and
nothing is saved. You can send industry_identifiers when you create a creative
with assets, with sourceAssets, or with sourceAssetRef. Replacing a
draft’s media from uploaded files (sourceAssets with creativeId) keeps the
identifiers the creative already has unless you send a new list. get with
kind: "creative" returns the saved list as industry_identifiers.
Finish an uploaded HTML5 ZIP or VAST draft
An HTML5 ZIP or a VAST file you upload to an advertiser’s Creative Library is saved as a draft creative. Its format is already fixed by the file the upload checked, so it needs no format option to finish. Give it its click URL and mark it complete withsave_creative, using the advertiser and no campaign:
mode: "complete" on its own finishes only a draft made from an uploaded HTML5
ZIP or VAST file. Any other draft is refused with VALIDATION_ERROR on mode,
and nothing is saved; finish it by replacing it with sourceAssets,
expectedRevision and its format. Sending mode: "complete" for a creative
that is already complete changes nothing.
landing_page_url also replaces the click URL of a complete Library creative,
with or without mode. A creative that is already on media buys keeps its old
click URL there until you resend it with resync: true. The click URL of a
creative made from a single HTML file is changed only on a campaign, because the
change reprocesses its markup: send it with the campaignId of a campaign the
creative is attached to.
Set a Creative Library role
In a Buyer Account, usesave_creative to give an existing advertiser Creative
a durable libraryRole. Send only the creative, advertiser, state revision,
role, and a stable idempotency key:
evergreen for a reusable Creative that remains eligible for delivery.
Choose reference for reusable reference material that cannot be attached for
delivery. Setting reference is refused while the Creative is attached for
delivery; setting evergreen does not detach it. The response has
action: "library_role_updated", the saved Creative role and its new state
revision. Retrying the same request and key returns that original result with
replayed: true; using the same key for a different request returns
CONFLICT.
The role stays in your Creative Library. Apostra does not send it to sellers.
You can read it as libraryRole on a Creative; the established role read and
search filter remain available during the transition.
Preview a bulk creative update
Usesave_creative.previewBulkUpdate to check a set before changing it. The
set is one campaign’s Creatives, or an advertiser’s whole Library (see
Change a click URL across an advertiser’s Library).
The preview combines every supplied selector, then removes
excludeCreativeIds. It returns at most 200 Creatives, an opaque preview token,
and a digest. A preview expires after 15 minutes and does not change any
Creative.
deliveryAttached for each selected Creative. This means the
Creative is attached to a campaign media buy, so changing it changes what
sellers deliver.
Preview also refuses, and names the Creatives involved, when a clickUrl
change selects an HTML Creative, because its HTML would need reprocessing, or
when formatOptionRef differs from an option a selected Creative already pins.
A pinned option cannot change after upload. Exclude those Creatives with
excludeCreativeIds and change them individually.
To apply the preview, pass the returned token and digest to
save_creative.applyBulkUpdate with a stable, required idempotencyKey:
replayed: true; using it for a different request
is refused. Completed applies retain their idempotency receipt for seven days
independently of the 15-minute preview, so a same-key retry can replay the
saved result after preview cleanup.
Approve a bulk update of delivery-attached creatives
When no selected Creative is attached to delivery, apply changes the Creatives at once. Each Creative in the result reportsdeliverySync: "not_required",
because no seller holds a copy to update.
When any selected Creative is attached to delivery, apply changes nothing and
returns action: "approval_required" with an approval link. An administrator
of your account must approve that exact preview on our site. While the request
is open, its preview stays valid until the approval expires, 24 hours later.
Call applyBulkUpdate again with the same idempotency key to check the
outcome:
- While the request is waiting, the call returns the same approval.
- After approval, the stored Creative changes commit together, and in the same
transaction Apostra queues a seller update of each delivery-attached
Creative for every current media buy that holds it. The call returns the
saved result with
replayed: true. A Creative with queued seller work reportsdeliverySync: "queued"and itsdeliveryOperationIds; others report"not_required". Queued means the update is recorded and will be retried, not that the seller has accepted it. Follow it withget(kind: "media_buy", id: "…", include: ["operations"]), where the operation appears with kindcreative.delivery. - If the request is rejected or expires, or a selected Creative changed after the preview, nothing changes. Preview again and use a new idempotency key.
Change a click URL across an advertiser’s Library
To change Creatives that belong to an advertiser rather than to one campaign, sendadvertiserId in place of campaignId. Send exactly one of them; sending
both, or neither, is refused.
excludeCreativeIds, the 200-Creative
limit, the preview token, the digest and the 15-minute expiry work as they do
for a campaign. Apply with applyBulkUpdate, the same advertiserId and an
idempotencyKey. A preview belongs to the scope it was made in. A campaign
preview cannot be applied as an advertiser one, and an advertiser preview cannot
be applied to a campaign.
In the Library, the update may change only clickUrl:
formatOptionRefis refused, because a Library has no product that declares the option. Preview with acampaignIdto apply one.formatKindandformatParamsare refused, because a Library Creative changes format only by replacing its media. This matches a single advertiser-level update.- A
clickUrlchange that selects an HTML Creative is refused and names it, exactly as for a campaign.
deliveryAttached. A Creative that is not attached
to delivery changes in the Library only, no media buy is touched, and its result
says deliverySync: "not_required". A Creative that is attached to a media buy,
live ones included, changes only after an administrator of your account approves
the exact preview, as described above. Once approved, Apostra queues the updated
Creative for the sellers of every current media buy that holds it. The response
to the preview and to the apply both say which case applies. A Creative you
attach to a campaign later carries the new click URL.
Check seller operation progress for a media buy
When a seller operation exists for a media buy,get(kind: "media_buy")
returns a compact operations summary on the media buy. It tells you what
work Apostra has durably recorded for each seller destination. Existing reads
without operation data are unchanged.
Use include: ["operations"] when you need the bounded list of recent
operation records as well as the summary.
An operation that waits on a correction is closed, rather than left blocked,
once nothing can use the correction. Apostra closes it three days after it was
blocked, or at once on a read, when any of these is true.
closedReason names
which:
A closed operation has no
nextAction. Re-running it is never the fix: create
or re-execute the media buy instead.
When a seller operation is blocked for a correction you can make, Apostra
sends you a media buy notification saying it needs a correction. A refusal that
an ad platform made in its own words is not something you can correct, so it
goes to Apostra support instead, and the operation’s nextAction is
contactSupport.
For example, ask your connected assistant to read a media buy with operation
detail:
media_buy.update. It is pending while Apostra retries it, for up to five attempts, and failed with next step contactSupport after the last attempt. The creative stays on the media buy until the seller accepts the removal, and it may still be serving at the seller while the operation is failed.
Treat pending, blocked, failed, and closed as progress information. Re-read the
media buy after the seller work changes state; do not describe a queued or
sending operation as accepted, complete, or live.