Optionalbuild_variant_id?: stringOptionalrecipe_hash?: stringOptional agent-computed, opaque, agent-scoped identity for the build-determining inputs that produced this creative. Stable for identical inputs as defined by the agent, and comparable only within the same agent. ETag-style semantics: the protocol defines the field and contract, not the hash algorithm or canonical input set. Identifies generative-input identity, not output equality, legal/disclosure equivalence, or the build-to-delivery join.
Optionalsandbox?: booleanWhen true, this response contains simulated data from sandbox mode.
Optionalexpires_at?: stringISO 8601 timestamp when generated asset URLs in the manifest expire. Set to the earliest expiration across all generated assets. Re-build the creative after this time to get fresh URLs.
Optionalpreview?: {Preview renders included when the request set include_preview to true and the agent supports it. Contains the same content fields as a preview_creative single response (previews, interactive_url, expires_at) minus the response_type discriminator, so clients can reuse the same preview rendering logic.
Array of preview variants. Each preview corresponds to an input set from preview_inputs, or a single default preview if no inputs were provided.
Optionalinteractive_url?: stringOptional URL to an interactive testing page that shows all preview variants with controls to switch between them.
ISO 8601 timestamp when preview URLs expire. May differ from the manifest's expires_at.
Optionalpreview_error?: {Error code for programmatic handling. The error-code vocabulary is open: error.code is wire-typed string (not a closed enum), the standard codes published in enums/error-code.json are documentary, and senders MAY emit codes outside that set (platform-specific codes, or codes introduced in a later AdCP version). Receivers MUST decode unknown codes — treat the response as well-formed, read error.recovery for the recovery classification, and fall back to transient when recovery is absent. See error-handling.mdx#forward-compatible-decoding-normative for the full forward-compat contract — this rule is what lets future maintenance lines ship new codes additively.
Human-readable error message
Optionalfield?: stringField path associated with the error in JSONPath-lite format (e.g., 'packages[0].targeting'). When issues[] is also present, sellers MUST set this to issues[0].pointer translated from RFC 6901 to JSONPath-lite (e.g., '/packages/0/targeting' → 'packages[0].targeting') so pre-3.1 consumers reading field only get deterministic behavior. Will be deprecated in a future major version in favor of issues[].pointer.
Optionalsuggestion?: stringSuggested fix for the error
Optionalretry_after?: numberSeconds to wait before retrying the operation. Sellers MUST return values between 1 and 3600. Clients MUST clamp values outside this range.
Optionalissues?: {Structured list of validation failures. Primary use is VALIDATION_ERROR, where multi-field rejections are common and field (singular) cannot carry the full pointer map. MAY appear on other error codes that reject multiple fields at once. When issues is present, sellers MUST also populate field from issues[0] for backward compatibility with pre-3.1 consumers that read field only — translating the RFC 6901 pointer format to the JSONPath-lite format field uses (e.g., /packages/0/targeting → packages[0].targeting). MUST (not SHOULD) so consumers reading field get deterministic behavior across sellers — the cost is one line of dual-write per seller; the cost of SHOULD is a long tail of seller-A-vs-seller-B inconsistency. Future major versions will deprecate field in favor of issues[].pointer.
Optionaldetails?: Omit<Omit<{}, "bank">, "authentication"> & { authentication?: unknown }Additional task-specific error details. Sellers MAY mirror issues[] here as details.issues for backward compatibility with pre-3.1 consumers reading from details; new consumers SHOULD prefer the top-level issues field.
Canonical rejection-set shape (3.1+). When the error reports a rejected value against a closed set of accepted values (e.g., enum mismatch, unsupported pricing option, invalid signal id), sellers SHOULD use the canonical key accepted_values: <array> under details rather than seller-specific variants observed in the wild (available, allowed, accepted_values at the error root, etc.). The canonical shape:
{
"code": "INVALID_PRICING_MODEL",
"message": "Pricing option not found: po_prism_abandoner_cpm",
"field": "pricing_option_id",
"details": {
"rejected_value": "po_prism_abandoner_cpm",
"accepted_values": ["po_prism_cart_cpm", "po_prism_view_cpm"]
}
}
rejected_value (optional): the offending value the buyer supplied, echoed for buyer-side diagnostic clarity (especially when the offending field is nested or transformed before validation).accepted_values (optional): the closed set the seller would have accepted at this field on this call. Sellers MUST NOT enumerate the full ecosystem-wide accepted set if it differs from what's accepted for this caller in this context (account, brand, scope) — leaking ecosystem-wide accepted sets to a per-caller rejection turns the error into an enumeration oracle.This is SHOULD-level guidance, not MUST: details remains additionalProperties: true and pre-3.1 sellers using available / allowed / accepted_values at the error root remain conformant. The canonical shape lets buyer-side diagnostic tooling (SDK runner hints, dashboards, error classifiers) reliably surface the accepted-set without per-seller pattern matching. SDKs SHOULD accept any of the legacy variants and normalize on read; the canonical shape is what new sellers and 3.1+ adopters should emit going forward.
Optionalrecovery?: "transient" | "correctable" | "terminal"Agent recovery classification. transient: retry after delay (rate limit, service unavailable, timeout). correctable: fix the request and resend (invalid field, budget too low, creative rejected). terminal: requires human action (account suspended, payment required, account not found). Senders SHOULD populate recovery on every error from 3.1 onward — it is the normative carrier of recovery semantics across version skew. A receiver that does not recognize error.code (a newer code, or a platform-specific code) MUST still be able to classify the error from recovery. The enumMetadata.recovery block in enums/error-code.json is the documentary mirror for known codes; error.recovery on the wire is authoritative.
Optionalsource?: "producer" | "sdk"Who emitted this error entry. producer (default when absent): emitted by the response's authoring agent (the seller for get_products, the creative agent for build_creative, etc.). sdk: augmented by a consuming SDK that detected a non-fatal advisory condition on consumption (e.g., FORMAT_PROJECTION_FAILED when the buyer SDK couldn't project a v1 format to a canonical, or FORMAT_DECLARATION_DIVERGENT when the SDK detected a producer bug on read). SDK-augmented entries SHOULD also set sdk_id so downstream consumers can identify which intermediate processor inserted the entry.
Multi-hop propagation (normative). AdCP is a federated agent network — responses commonly traverse multiple SDKs (e.g., sales agent → interchange → DSP → buyer). When an SDK augments errors[] with a consumption-detected entry, the augmented response carries the entry forward to subsequent hops. Each hop that detects the same condition independently SHOULD deduplicate by (code, field) rather than re-emit; the existing entry's sdk_id identifies which earlier processor saw it first. Producer entries (those without source: "sdk") are authoritative for what the response's authoring agent self-detected; SDK entries are observations made on top.
Replay/audit safety. Persisted or replayed responses carry source and sdk_id so the audit trail can distinguish seller-emitted entries from SDK-augmented ones. Without source, a downstream consumer can't tell whether a code came from the seller or an intermediate SDK, which corrupts attribution.
Optionalsdk_id?: stringOptional identifier for the SDK that augmented this error entry. Format: <sdk_package_name>@<version> (e.g., @adcontextprotocol/adcp@7.3.0, adcontextprotocol-adcp-python@1.2.0). MUST be set when source: "sdk"; MUST be absent when source: "producer" or absent. Lets downstream consumers identify which intermediate processor inserted the entry, useful for debugging cross-SDK divergence (e.g., one SDK detects a projection failure that another SDK's registry version doesn't).
Optionalpricing_option_id?: stringWhich rate card pricing option was applied for this build. Present when the creative agent charges for its services. Pass this in report_usage to identify which pricing option was applied.
Optionalvendor_cost?: numberCost incurred for this build, denominated in currency. May be 0 for CPM-priced creatives where cost accrues at serve time rather than build time.
Optionalcurrency?: stringISO 4217 currency code for vendor_cost.
Optionalconsumption?: {Optionaltokens?: numberLLM or generation tokens consumed during creative generation.
Optionalimages_generated?: numberNumber of images produced during generation.
Optionalrenders?: numberNumber of render passes performed (video, animation).
Optionalduration_seconds?: numberProcessing time billed, in seconds. For compute-time pricing models.
Optionalcontext?: Omit<Omit<{}, "bank">, "authentication"> & { authentication?: unknown }Optionalext?: Omit<Omit<{}, "bank">, "authentication"> & { authentication?: unknown }Optionalsummary: string
Leaf handle for this produced creative — present when the agent supports refinement (creative.supports_refinement). Pass it as refine_from_build_variant_id to refine this build. Same namespace as BuildCreativeVariantSuccess leaves; distinct from served variant_id / preview_id. On the canonical promotion path, this value becomes the creative_id when the produced leaf is trafficked or added to the library.