Skip to main content
A v3 tool can return a successful MCP transport response with isError: true. Always inspect the tool result, then use structuredContent.code and the associated fields rather than parsing rendered prose. Common recovery patterns include:
  • correct a validation field and call again;
  • call get_status after a wrong-account response;
  • re-read a versioned noun after a revision conflict;
  • preserve per-seller or per-source outcomes from a partial result; and
  • re-read after partialWrite: true before deciding whether to retry.
A required input must be included even when its schema allows null. Pass null explicitly for a blank value on a nullable input; omitting the input still returns a validation error. Inputs that do not allow null reject it. An INVALID_REQUEST with the message “Request declined by platform policy.” and details.reason of safety_refused means the platform safety policy declined the request. Revise it and retry, or contact support to ask for a review. See Request declined by platform policy.

A seller changed the media buy

When you update or cancel a media buy, the seller can refuse the change because it changed the buy after the version you last read. Apostra reads the buy again and compares it with the version your change was based on:
  • If the seller changed only fields your update does not set, Apostra sends your update once more on the current version. You see a normal result and no conflict.
  • If the seller changed a field your update sets, or Apostra cannot tell, you get a CONFLICT with recovery correctable. The seller that refused did not apply the change. Apostra never retries a change that overlaps, and it retries at most once.
A CONFLICT tells you about the sellers that refused the change. It is not a statement about every seller. A change to a media buy that spans several sellers is not atomic, so the other sellers may have applied it, refused it, or failed in a way that leaves the outcome unknown, such as a timeout. Read details.per_source for each seller’s outcome, and read the current media buy, before you send anything again. Resend the change only for sellers that did not apply it. Do not resend the whole request unless per_source shows that every seller refused it. When some sellers applied the change and another refused, the result is not a CONFLICT: it is a SERVICE_UNAVAILABLE error with details.reason of partial_update and the same per_source list. After a CONFLICT, read the media buy again, decide whether your change still applies to what the seller now holds, and send it again if it does. When an Apostra storefront answers an AdCP update_media_buy directly, the error’s details.snapshot is usually the media buy after the storefront read it again, so you can decide without a second call. The snapshot is optional: when it is absent, read the media buy yourself before you decide. A cancel that an Apostra storefront queued on your behalf sends itself again on its own with the current version, and it is done if the seller already holds the buy as canceled, rejected or completed. A CONFLICT whose details.error_code names another cause is a different state conflict. Read the message and the per-source details for what to do. NOT_FOUND can intentionally hide whether an object exists outside the active account. Do not treat it as evidence about another account. An ACCESS_DENIED with details.reason of switch_account_required means the object, such as an advertiser, belongs to another account you are a member of, not the account the call acted in. details.owningCustomerId is that account’s id. Send the call again with it as accountId; on v2, call customer_switch with it first. An object in an account you do not belong to is refused without naming the account. A buyer agent refused on an advertiser gets the reason in details.reason. Most refusals also use the reason as the error’s message, kept for clients that match on it. The suggestion is the human-readable text: in details.suggestion, suggestion on an MCP tool error, and the first of suggestions through api_call. LIVE_PROVIDER_UNAVAILABLE from get_delivery with report: "live_campaign_delivery" means the campaign exists but has no media buy live at a provider, including a campaign whose buys are still waiting to start. See Reporting pipeline for the buy states it lists, and for the SERVICE_UNAVAILABLE a provider’s refusal of a started campaign’s read returns instead of a validation error.

Full error reference

Review the canonical error envelope, status mapping, retry behavior, and troubleshooting guidance shared with stable v2.