Skip to main content
Three read-only surfaces answer the operating questions “what buys do I have, where is this one stuck, and what needs attention” — without a ticket and without platform support looking at a database. Each is available as a REST endpoint and as an in-chat surface (ask Murph, or open it from the rail’s Operate section). All examples use the storefront base URL:
Authenticate every request with 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 ArchiveOrders is 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.
Only storefront account admins can resolve these tasks. Archiving requires explicit confirmation naming the GAM order, and ESA repeats every safety check immediately before archiving. An already-archived response completes the task. Marking an order manually cleaned requires the matching order ID and an audit note; it is recorded distinctly and never claims ESA archived it.

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 the optimization_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:
Accepted means the source accepted the transaction boundary. It does not mean trafficked or delivered. Creative processing, modular trafficking, and other seller work can remain pending after acceptance; the timeline and 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: true means no revision has arrived by the expected time.
  • A successful revision with rowCount: 0 is a real zero-row report, not a missing report.
  • kind distinguishes snapshot, official, and restatement revisions.
  • coverage can be full, partial, or none; it is null when no coverage value was observed. Partial coverage is never presented as whole-buy coverage.
  • dataThrough, nextExpectedAt, and freshness describe the reporting data, not the media buy’s delivery status.
  • obligationsTruncated, mediaBuyIdsTruncated, and relatedMediaBuyIdsTruncated explicitly mark bounded responses. When the obligation-row limit is exceeded, aggregate health and freshness fail closed to null instead 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 exact reviewRef used 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. mediaBuyId is null for a creative-library delivery.
  • manualSourceWork — modular booking, creative-sync, and final-reporting tasks, grouped by the same buyerCustomerId + mediaBuyId + sourceId. Every group and item repeats buyerCustomerId; every item carries its exact workItemId, 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, or advisory) 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.
Every item in 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.party is seller (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), or scope3 (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 reads seller.
  • waitingOn.since is when the wait on that party began. Age never decides whether something is pending: an item is listed from its first minute.
  • waitingOn.target names what is being waited for as a kind and id: an approval (the media buy id), a creative_review, a manual work_item, the source’s task id, or the creative still to be delivered. It is null when there is no single thing to name.
A failed forward carries 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.
If your source consistently produces transient timeout failures on create_media_buy or update_media_buy — visible in source diagnostics and as persistent failedForwards here — the long-term fix is to implement AdCP asynchronous acceptance on your sales agent. Return a submitted response (with a stable task_id) immediately when the platform’s request arrives, complete the work out-of-band, then POST the result to the push_notification_config webhook URL included in the original request. This decouples your processing time from the platform’s call timeout and eliminates the failure class entirely.See the AdCP async operations reference for the protocol and payload details. Contact your operator or Apostra support to discuss implementation.

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-sorted

Get a buy's timeline

GET /media-buys/{mediaBuyId}/timeline — the seller-scoped exchange record

Get a buy's reporting state

GET /media-buys/{mediaBuyId}/reporting — canonical obligations, health, and retained revisions for one buyer-scoped buy

List pending operations

GET /pending-operations — everything waiting on someone

Retry a failed forward

POST /media-buy-approvals/{mediaBuyId}/retry-forward — transient, non-terminalized failures only