Skip to main content
Once a creative is on a media buy with a publisher, the publisher may need to review it before it goes live. There are two separate gates a creative can move through, and each surfaces its own status on get_campaign.

When a creative reaches the publisher

A creative goes to a publisher as soon as it is on a media buy with that publisher, including a draft buy you have not submitted yet. You do not need to call sync_creatives. Any of these sends it:
  • saving a creative to a campaign that already has a draft media buy with the publisher;
  • editing a creative that is on such a buy, which sends the new version;
  • creating a draft media buy, or changing a draft buy’s creatives or products, which sends the buy’s creatives to its publisher.
A publisher with manual creative review receives the creative in its review queue, so review can finish before you submit the buy. Any other publisher receives it at its ad server or sales agent. A creative that is not on any media buy with a publisher is not sent to that publisher. Sends are queued and normally reach the publisher within about two minutes. Apostra retries a send that fails for a temporary reason. When you submit the buy, Apostra does not send again a creative the publisher has already accepted in its current version.

The two approval gates

1. Storefront operator review

When a publisher has configured their storefront for manual creative review (creativeApproval: manual), each submitted creative lands in the publisher’s queue. Their operations team reviews it and records a decision. The decision belongs to the immutable creative content version, not to one media buy. The same unchanged creative can reuse that decision on later buys. Changing the reviewed content creates a new review version and requires a new decision, even if the buyer reuses the same creative identifier. The review status is returned as storefrontReviewStatus on each creative entry: When storefrontReviewStatus is null, the publisher runs automatic creative review (creativeApproval: auto) and there is no manual gate.

Taking a creative off a draft buy

A creative on a publisher’s draft buy goes to that publisher’s review queue before you submit the buy. If you then take the creative off every draft buy with that publisher, Apostra withdraws it from the queue so the publisher stops reviewing a creative you no longer want. You take a creative off a draft buy by removing it from the campaign, archiving it, or editing the draft buy so it no longer carries it.
  • The publisher’s pending review closes as archived, AdCP’s own status for a creative the buyer withdrew, and leaves the publisher’s pending queue. You get no rejection and no callback, and storefrontReviewStatus never returns archived.
  • Apostra waits about a minute before withdrawing, so a creative you put straight back keeps its review.
  • Only a pending review is withdrawn. A creative the publisher has already approved, rejected, or forwarded to its sources stays as it is.
  • A creative that is still on another draft buy with the same publisher keeps its review.
  • If you add the same creative again, the same review reopens as pending.
For an exact own-supply campaign managed by the Media Company that owns the Storefront, the authenticated operator’s upload is itself the Storefront decision. Apostra does not create a second review task for that operator, even when the Storefront’s external-buyer setting is manual. This exception does not apply to external or sponsored buyers, and it does not approve the Creative in the source system.

2. Source system review

After a creative passes operator review (or when the storefront uses automatic review), the platform syncs it to the publisher’s ad server. The ad server may then accept or reject it independently. The result appears per source on sourceSyncStatus[]. This source gate includes provider review such as Google Ad Manager creative approval. It is separate from the storefront operator decision. Embedded sales agents and other adapters transport the request, but they do not own or replace either approval decision. An empty sourceSyncStatus[] means the creative has not yet been synced to any source. approvalStatus: "pending" means the source has not yet returned terminal approval. A completed request, an accepted asynchronous task, or assignment to only some of the requested packages is not enough to report the creative as approved. sourceSyncStatus[].agentId is the legacy internal AdCP agent database ID, preserved for compatibility. sourceSyncStatus[].sourceId is the public AdCP agent or inventory source identifier captured for that sync route. For a legacy agent route, this is the protocol-level agent ID, never the numeric database ID. It is null only when retained history predates a captured public source identity.

Sellers whose own team traffics creatives

Some sellers have no ad server connection that accepts creatives directly. Their operations team, or a system they connect, traffics each creative into the ad server after the media buy is booked. For these sellers, the source gate happens after booking:
  • A creative attached to the campaign before launch does not hold the launch. The media buy is booked, and the creative goes to the seller’s trafficking queue for that buy.
  • While the seller traffics it, the media buy’s status is pending_creatives and the creative’s approvalStatus stays "pending".
  • When the seller confirms the creative is trafficked, the media buy moves to pending_start.
Storefront operator review (gate 1) still comes first. A creative the operator rejects never reaches the seller’s trafficking queue. A creative the operator approves waits in that queue for up to 7 days, and its approvalStatus becomes "approved" once the seller confirms it is trafficked. If the seller has not confirmed or declined it within 7 days, the delivery fails with the reason source_task_sla_exceeded.

Format refusal before send

Before a creative is sent, Apostra must be able to pick exactly one format the seller declared for the selected product. When it cannot, creative sync stops before the seller receives a request, and the buyer sees a rejectionReason that starts with the reason the format could not be chosen and then names who must act:
  • The buyer must declare the creative’s size. The product offers several formats of the creative’s kind that differ by size or duration (for example one HTML5 format per banner size), and the creative does not state which one it fits. The message lists the sizes or durations the product offers and the fact to declare. Apostra reads an image’s size, a hosted video’s size and duration, and hosted audio’s duration from the creative’s source file, so this happens when the file carries no such measurement. Set the creative’s width and height (an image can declare one fixed size in formatParams), or its duration for video and audio, and sync again. The seller’s declaration is valid, so there is nothing for the seller to change.
  • The buyer must use a format the product offers. None of the product’s formats fits the creative: the product offers no format of the creative’s kind, or none in its size or duration (for example a 320x50 HTML5 banner on a product whose HTML5 formats are all larger). The message lists the sizes or durations the product offers for that kind. Sync a creative in one of them, or assign this creative to a product that offers its size or duration. An image, hosted video or hosted audio creative sent to a product that offers exactly one format of its kind goes under that format whatever its size, and the seller adapts it.
  • The seller must declare a format its creative agent defines. The product declares no format, or declares identifiers the named creative agent does not define. Give that error to the seller. After the seller corrects the declaration, resubmit or retry creative sync for the media buy.
  • Apostra must choose. The creative states every fact the product’s formats differ by, or the product lists one format under several identifiers, and Apostra still cannot choose one. Neither the buyer nor the seller needs to change anything; Apostra has to fix its mapping.

Checking approval status

Call get_campaign and read mediaBuys[].creatives[] on the relevant media buy. Both review gates appear side by side on each creative entry:
A creative must clear both gates before it is eligible to serve: the publisher must approve it (or skip the queue via auto-review), and every required source route must explicitly approve the creative with the current media-buy package assignments. Status is derived separately for each media buy, so using the same creative on another buy cannot make that new assignment appear approved.

What to do if a creative is rejected

Storefront rejection (storefrontReviewStatus: "rejected")

The publisher’s operations team declined the creative. Check the reviewer note that was returned in the webhook callback or is visible in the publisher’s review queue. Fix the creative, re-upload it as a new manifest, and re-map it to the campaign. The creative’s status reads “Creative was rejected by the storefront operator.” in the rejectionReason on that publisher’s sourceSyncStatus[] entry (on the creative itself, platform_links[].rejection_reason) and in the message of its creative_rejected error. When Apostra closes the review itself rather than the publisher, those fields carry Apostra’s reason instead. Today that happens for one case: a creative that declares no single format, which can never be delivered, reads “Expired: this creative declares no single format, so it can’t be delivered. Resubmit it with a format.”

Automatic rejection by the storefront’s reviewer

A storefront that approves creatives automatically and has written a Creative Policy lets an automated reviewer decide creatives against that policy instead of a person. It approves a creative when it is definite and every check passes. It rejects one only when a separate policy check confirms a broken rule. Otherwise it leaves the creative in the seller’s review queue, so a creative you submit to such a storefront can wait for a person. When it rejects a creative, the rejectionReason (v2 platform_links[].rejection_reason, and the message of the creative_rejected error) starts with Rejected automatically: and tells you whether the creative can be fixed. The message in the webhook callback is the same text.
  • You can fix it. The reason says what to change and ends “You can fix this in the creative and resubmit it.” For example: “Rejected automatically: The audio is mono; this storefront requires stereo. You can fix this in the creative and resubmit it.” Change the creative as described, re-upload it as a new manifest, and re-map it to the campaign.
  • The storefront does not accept this kind of ad. The reason says so, and that resubmitting will not help. For example: “Rejected automatically: this storefront does not accept this kind of ad, so resubmitting it will not help. Gambling ads are not accepted.” Do not resubmit a changed version of the same ad. Choose another storefront, or ask the seller.
  • Not known. If the reviewer cannot say which, the reason gives what it found and makes no claim about resubmitting. If it can say nothing useful you see “Rejected automatically: the creative does not meet this storefront’s Creative Policy.”
The reason may quote or paraphrase the seller’s Creative Policy, and when the reviewer matched a rule of it, the reason ends with that rule as Policy rule: "...". The reason is plain text of at most 700 characters. It is the only reviewer text you receive: a person’s own review notes are not part of it.
Remapping a creative to a new placement does not re-submit it for storefront review. If a revised creative needs approval, re-upload it as a new manifest and map the new one.

Source system rejection (sourceSyncStatus[].approvalStatus indicates rejection)

The publisher’s ad server declined the creative after operator review. Read sourceSyncStatus[].rejectionReason for Apostra’s buyer-facing reason. It is a fixed summary unless Apostra set a more specific reason; it does not expose the publisher’s free-text note. Fix the creative based on that reason, re-upload, and re-map. A creative can be approved by the storefront operator but still fail at the source if the ad server applies its own rules (format constraints, brand safety filters, or trafficking restrictions). Treat a source rejection as its own distinct problem, separate from the operator review decision. When a storefront approves a creative but cannot deliver it to the publisher’s ad server, that publisher’s entry is marked rejected with the reason “Seller approved this creative but could not deliver it (delivery_failed).” This does not mean the publisher declined the creative. Once the cause is fixed, call save_creative with the creative’s creativeId, advertiserId, and resync: true to send it again.

When a creative has not reached a seller

A creative is sent to each seller separately, and a creative the seller never saw is not a creative the seller rejected. On get_creative and list_creatives, each entry in platform_links reports where the creative stands on that seller: rejection_reason is only the seller’s own note, so a not_sent creative has none. Treat a creative as rejected by the seller only when sync_reason is seller_rejected_content or approval_status is rejected. Every reason, with its stage and who must act, is listed in Creative sync outcomes. If Apostra cannot read a booked product needed to project a legacy seller’s creative, the creative is not sent and reads sync_reason: booked_products_not_readable, sync_stage: failed, and must_act: apostra. The per-creative error names the unread product IDs. This is not a seller rejection and does not require a creative change; Apostra must restore the catalog record before the creative can sync.