Skip to main content
Use Apostra to plan and manage advertising, sell inventory, or understand results. You can work in the application or connect an assistant to the V3 MCP endpoint. You do not need to build software first.

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.
Account permissions and workflow readiness still determine what you can do. Connecting an assistant does not grant additional access or approve spending. Some shared guides retain /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 with open_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:
The Page loads its own data through 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 with save_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.
Read the saved advertiser with:
Notes accept up to 2,000 characters. Omitting 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:
The file must be reachable by automated requests; a site that blocks bots needs to allow this path. If the brand cannot host a file, it can publish the same portfolio as a hosted profile in the AdCP brand registry run by AgenticAdvertising.org, which checks that the brand owns the domain. Once the brand publishes its sub-brands, create one advertiser per sub-brand with 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 both brand and brand_id.
  • A brand that publishes its own brand.json on its own domain reads as that domain alone ({ "domain": "example-insurance.com" }), so send only brand.
The house domain on its own, with no brand ID, is still a valid advertiser brand. The list shows at most 100 brands; 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 with save_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=300x250 in the ad-serving URL, or a fixed pixel width and height on <body>.
  • You pass formatParams with a fixed width and height.
  • You declare it responsive with formatParams that carry any of min_width, max_width, min_height, or max_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 with formatKind, 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 15 headlines 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.
With a 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 search creative_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 image creative has no brand_name slot, so social.brand_name on one is refused. An asset sent without a slot must be of a type some slot takes: a video on an image creative 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 (for image, 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 search and get with kind: "creative_format" return both on the slot. max_chars is the longest copy the seller’s API accepts. save_creative refuses copy over it, naming the slot, the limit and how long your copy is. recommended_chars is 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 over max_chars reaches a sync, for example a creative saved before the limit changed, we do not send it. The creative’s link to that seller reads failed with the reason creative_refused and you as the party who must act (see Creative sync outcomes). V2 reads carry the slot, the limit and your length in the link’s rejection_reason; V3 shows the reason only, so check the slot against max_chars. Copy over recommended_chars is 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, a formatParams size or duration you declare is the limit for the main asset.
Updating a creative’s copy is checked the same way: copy sent to a slot the creative’s format does not declare is refused, and so is text longer than the limit the creative’s own format option declares for that slot. Media is create-only, so an update never changes what the creative carries.

Edit a creative’s copy and send it to its media buys

To correct the copy of a creative that already exists, call save_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 with campaignId 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, call save_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.
The new creative is always named 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, call save_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.
Restore puts the creative and its files back in the advertiser’s Creative Library. It does not put the creative back on anything it was on:
  • It is not on any campaign or media buy. Attach it again with save_creative, creativeId, advertiserId and campaignIds.
  • 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.
Restoring a creative that is not archived changes nothing and succeeds. A creative that does not exist, or that belongs to another of your advertisers, returns 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:
The new name must also be free, or the restore is refused the same way and nothing changes. You can also rename or archive the other creative first and restore without 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.
Each creative goes only to the packages whose declared format and size it fits. A 300x600 creative is not sent to a 320x50 package, and a creative that fits no package on a media buy is reported on its own instead of stopping the others. The call sends to every media buy the campaign routes to, live ones included, and it does not change which campaigns a creative belongs to. The result carries an 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. Pass industry_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.
An update replaces the whole list, so send every identifier you want to keep. A type outside the four above is refused with a 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 with save_creative, using the advertiser and no campaign:
The creative becomes complete and stays in the Library. You can then attach it to a campaign, and the campaign’s products check its format when you do, as for any Library creative. A draft cannot be attached to a campaign until it is complete. 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, use save_creative to give an existing advertiser Creative a durable libraryRole. Send only the creative, advertiser, state revision, role, and a stable idempotency key:
Choose 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

Use save_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.
The preview lists 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:
The stored Creative changes commit together. If a selected Creative changes after preview, the token expires, or the digest does not match, nothing is written and you must preview again. Reusing the same key with the same request returns the saved result with 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 reports deliverySync: "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 reports deliverySync: "queued" and its deliveryOperationIds; others report "not_required". Queued means the update is recorded and will be retried, not that the seller has accepted it. Follow it with get(kind: "media_buy", id: "…", include: ["operations"]), where the operation appears with kind creative.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, send advertiserId in place of campaignId. Send exactly one of them; sending both, or neither, is refused.
The preview selects the advertiser’s Library Creatives: those that are not archived and are not tied to a single campaign. Drafts are included, as they are in a campaign selection. The selectors, 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:
  • formatOptionRef is refused, because a Library has no product that declares the option. Preview with a campaignId to apply one.
  • formatKind and formatParams are refused, because a Library Creative changes format only by replacing its media. This matches a single advertiser-level update.
  • A clickUrl change that selects an HTML Creative is refused and names it, exactly as for a campaign.
The change reaches live media buys the same way as a campaign update. The preview marks each Creative 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:
A creative removed from a media buy the seller has already booked appears here as an operation of kind 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.