Authorization: Bearer $SCOPE3_API_KEY.
Recover a failed GAM order safely
If GAM created an order but line-item trafficking failed, Pending Operations shows a separate cleanup task. Apostra uses ESA’s structured cleanup result; it never interprets vendor exception text or exposes GAM credentials.- If
ArchiveOrdersis missing, grant it in GAM and re-check, or archive the order manually. - If authentication expired, reconnect the ad-server connection before re-checking.
- Review and archive appears when cleanup is verified retryable. Permission and authentication failures instead offer a confirmation-gated re-check, and archive proceeds only if the repeated safety check passes.
- Unknown or unsafe orders require GAM review and cannot be archived from Apostra.
Media buys — every buy on the storefront
GET /media-buys returns every buy forwarded across connected sources plus buys
managed directly by an ad-server source. The API retains kind: routed | esa as
legacy wire values; those values describe implementation provenance, not
campaign mode or settlement. Filter by status, buyerCustomerId, sourceId,
or flight-start window.
Pass activeOnly=true to drop buys that are finished and cannot need anyone:
completed, canceled, rejected. It defaults to false, so the list is
your whole inventory. Set it on any “what needs me now” view, because the list
is newest-first and a buy that settled yesterday otherwise outranks an older
one still running. Leave it off when you filter by one of those three
statuses, or the list comes back empty.
The default sort is urgency: buys still waiting on someone whose flight starts within 48 hours (or has already started) come first, ordered by flight start; everything else follows newest-first.
Pass sort=attention to get the first page ordered by what needs you:
money the flight will not deliver, then work waiting on you (including a
delivery problem you have to act on: a source that cannot report, a running
flight nothing has reported for, or spend running ahead of the flight), then a
goal being missed, then everything else. That ranking depends on delivery and goal
verdicts that can only be judged once a page is loaded, so it is accepted at
skip=0 only and the response says attentionRanked. It ranks a page, never
your whole storefront — page past the first and you are back in the stable
urgency order. Your storefront home opens in this order, and so does the
Media buys entry in the rail — they are the same page over the same buys, and
ordering them differently by how you arrived would rearrange the list with
nothing to explain it.
Each row carries:
What needs you
Each buy says what needs you, in three families that answer different questions. They are deliberately kept apart: “spend 200 a day at 80% viewable” is two answers, and a single score would tell you to act without saying what to act on.level is the platform’s status colour language:
critical(red) — the flight is running and the promise is not being kept. Red is earned by the flight having started: a buy that has not started is never red, however far behind its budget it looks, because nothing was promised yet.attention(yellow) — worth acting on, promise intact: a task waiting on you, reporting delayed, a source that cannot report.pending(neutral) — not failure. The flight has not started, or the evidence does not support a verdict yet.
A withheld verdict is never red. Too few observations, a metric your source
has not reported, no flight recorded — all read “not enough data yet”. If red
appeared for things that are merely unknown, red would stop meaning stop.
Delivery and pacing
Every buy on the page carries delivery, whether or not you filtered the list to one buyer relationship. Delivery arrives in one of two fields, and never both.delivery is what the
storefront reporting pipeline reported for a routed buy, and it always names
the reporting day it covers. adServerDelivery is the cumulative total your
ad server holds for a buy it manages, and it never names a day — an ad server
states a total without saying which day it runs through, and we will not stamp
one on. The fields below describe both except where a row says otherwise:
lastDeliveryDate and sourcesReported/sourcesRouted are on delivery
alone, because a day and a routing are things only a routed buy has.
An ad server that has not granted us reporting access cannot report delivery no
matter how long you wait. That buy reads
reporting.state: "blocked" with a
detail naming what to fix, rather than sitting on “awaiting first report”
forever. Buys managed by your ad server carry no clicks, views or
completedViews yet — the ad-server list does not report them, and an ad
server that reports only one of impressions and spend reports neither here:
a spend of zero beside real impressions is not a measurement, and pacing it
would invent a confident verdict.The buyer’s goal
You see the same goal verdict the buyer sees, judged by the same code over the delivery your own sources reported. It is on every row of the list and on the per-buy timeline. You see it only from what the buyer told you: the commitment recorded against the buy when it was booked, and theoptimization_goals on the AdCP
request the buyer sent you. Nothing here reads the buyer’s campaign record. A
buy whose goal was never disclosed to you carries goal: null.
optimizedBy.actor === "seller" is the case to watch. It means the ad server
is not working towards the buyer’s goal — either it has no optimizer, or your
vendor has not granted us the access optimization needs. Hitting the goal is
your ad operations’ job, and this row is the only place that says so.Per-buy timeline — what was sent, when, and where it is stuck
GET /media-buys/{mediaBuyId}/timeline is the seller-scoped projection of the buy’s exchange record. Because routed media-buy ids are buyer-scoped, pass the row’s buyerCustomerId query parameter when it is available; the Media Buys Page does this automatically. Id-only requests remain supported for callers whose ids are unambiguous. It also carries the same goal block the list row does, so a drill-in answers
“is this buy hitting what the buyer asked for” alongside “where is it stuck”.
Stages follow the exchange lifecycle, failure branches included:
nextAction name that remaining boundary honestly.
Update exchanges reuse the same stages. Inbound source webhooks appear as evidence inside stages, never as a stage of their own — many sources never send webhooks, so their absence means nothing. The screened stage is reserved for the acceptance-policy pre-screen record and is not emitted today; do not wait for it.
What you can see, and what you cannot. The forwarded payload is yours to inspect — you are the counterparty to that message — minus platform-internal fields: webhook/push-notification configuration and any signing or credential material are removed server-side. Platform-internal diagnostics (resolution cache internals, worker polling state, cross-tenant audit entries) never appear; failures surface as structured error codes with a recovery class instead. A payload shown as {"pruned": true} means retention removed the stored bytes (payload bodies are kept for 90 days; the slim event record is kept indefinitely).
Each source leg carries a trafficker-grade summary of the request (flight, budget, packages, targeting dimensions) with the raw JSON behind a disclosure.
Canonical reporting — what is due and what arrived
GET /media-buys/{mediaBuyId}/reporting?buyerCustomerId={buyerCustomerId}
returns the canonical reporting obligations, current health, and retained
revision evidence for one seller-visible buy. buyerCustomerId is required
because different buyers can reuse the same media-buy ID. The endpoint first
verifies the buy against the current storefront and buyer-account grant; a
foreign or missing buy returns 404 rather than a plausible-looking empty
report.
The top-level health is one of waiting, healthy, delayed,
action_required, or complete. Each obligation keeps these cases distinct:
missingFirstReport: truemeans no revision has arrived by the expected time.- A successful revision with
rowCount: 0is a real zero-row report, not a missing report. kinddistinguishessnapshot,official, andrestatementrevisions.coveragecan befull,partial, ornone; it isnullwhen no coverage value was observed. Partial coverage is never presented as whole-buy coverage.dataThrough,nextExpectedAt, andfreshnessdescribe the reporting data, not the media buy’s delivery status.obligationsTruncated,mediaBuyIdsTruncated, andrelatedMediaBuyIdsTruncatedexplicitly mark bounded responses. When the obligation-row limit is exceeded, aggregate health and freshness fail closed tonullinstead of being calculated from a partial ledger view.
sourceProvenance: acquired means an immutable acquisition plan attributes
the reporting evidence to a Source. null means that proof was not observed;
it does not mean the Agent cannot report. The response is read-only. Its three
action descriptors remain unavailable with reason: adapter_pending until
their current-read and retained-revision adapters are connected.
Whose reference to quote to whom
Every leg carries two references, and they are for different counterparties:
While a source is still moderating (no upstream buy exists yet), their task id is the handle; once accepted, their media-buy id is.
Pending operations — everything waiting on someone
In Apostra, open this Page from Approvals & operations in the seller rail.GET /pending-operations returns the union of work in flight, grouped; a group appears only when it is non-empty:
approvals— media buys (creates and updates) waiting on your review.creativeReviews— creatives waiting on your review. Each item carries the exactreviewRefused by the Page action, so repeated versions of one buyer creative never open or decide a different review.failedForwards— approved buys that could not be sent to their source, grouped by structured error code so a source outage reads as one row, not N. Each group and item carries its gated action (below).awaitingSource— buys a source accepted asynchronously and is still moderating, each with “waiting since” and the source’s task id.updatesAwaitingSource— changes to live buys that a source accepted with a task and has not applied yet.creativesAwaitingSource— approved creatives not yet accepted at a source: the source is reviewing them, Apostra is still delivering them, or they wait for their media buy to be accepted there.mediaBuyIdisnullfor a creative-library delivery.manualSourceWork— modular booking, creative-sync, and final-reporting tasks, grouped by the samebuyerCustomerId+mediaBuyId+sourceId. Every group and item repeatsbuyerCustomerId; every item carries its exactworkItemId, seller owner, actionable/blocked state, and a typed deep-link to that exact source work item.sourceDegradations— seller-owned source-health diagnoses: an ad-server or sales-agent source you operate that is degraded, each with its severity (blocking,attention, oradvisory) and a one-line summary. Only diagnoses you own appear — an issue Apostra or a vendor must fix never shows up as your task. Each row links to the source diagnostics surface scoped to that source.gamCleanups— failed GAM order cleanups that require an operator decision. The Page offers automatic archive only when the server marks the cleanup safe; recording a manual cleanup requires a note.
approvals, creativeReviews, awaitingSource,
updatesAwaitingSource, creativesAwaitingSource, and manualSourceWork
carries waitingOn: who it is waiting on, since when, and the exact thing being
waited for. It is the same answer the
source health object
gives in sourceHealth.operations, with the same meanings:
waitingOn.partyisseller(you must approve, review, or do manual work),operator(the party that runs the Agent behind the source holds a task it has not finished),buyer(the buyer must attach creatives), orscope3(Apostra must make the next move, such as retrying a forward or delivering a creative). Behind Apostra’s own managed sales agent there is no separate operator, so a buy it holds waits on your own review in your ad server and readsseller.waitingOn.sinceis when the wait on that party began. Age never decides whether something is pending: an item is listed from its first minute.waitingOn.targetnames what is being waited for as akindandid: anapproval(the media buy id), acreative_review, a manualwork_item, the source’staskid, or thecreativestill to be delivered. It isnullwhen there is no single thing to name.
waitingOn too: scope3 while Apostra is still
retrying it, and null once it has stopped (terminalized), because the
failure is settled and the next step is the group’s recovery action.
The same Page works in Apostra, Claude, ChatGPT, and other MCP-app hosts. Its buttons open the exact portable Approvals, timeline, retry, or source-diagnostics surface directly—there is no generated chat prompt or second assistant turn. GAM cleanup is a two-step human confirmation inside the Page and uses an app-only commit that is not exposed to the model.
Manual modular-source work is bounded to 200 tasks per response. Its envelope
keeps full-backlog count and mediaBuyCount, and returns offset,
returnedCount, truncated, and nextOffset. Continue with
manualWorkSkip=nextOffset (and optionally manualWorkTake, max 200). The
Pending Operations Page does this with Load more, appending tasks without
deduplicating different buyers that reuse the same media-buy id.
If completed tasks make a requested offset fall past the live backlog, the
server rebases offset to 0; clients should replace their loaded manual-work
rows when the returned offset moves backward. The Page handles that reset.
Recovery classes: why some failures have no retry
Every forwarding failure carries a recovery class, and the class — not the operator’s optimism — decides which action is offered:
A
transient failure whose retry window has lapsed is treated as terminalized and also becomes escalate-only.
Transition notifications
You do not have to keep this surface open: a forward failure on an approved buy and a source-moderation wait that ages past its deadline each push one notification to your storefront’s notification stream (in-app, email, Slack — per your delivery settings). These fire on the transition only, never repeatedly, and carry the error code and recovery class.Task reference
List media buys
GET /media-buys — every buy across the storefront and connected sources,
urgency-sortedGet a buy's timeline
GET /media-buys/{mediaBuyId}/timeline — the seller-scoped exchange recordGet a buy's reporting state
GET /media-buys/{mediaBuyId}/reporting — canonical obligations, health,
and retained revisions for one buyer-scoped buyList pending operations
GET /pending-operations — everything waiting on someoneRetry a failed forward
POST /media-buy-approvals/{mediaBuyId}/retry-forward — transient, non-terminalized failures only