Skip to content

Simplify PreviewCreativeResponse for the single-render case #3268

Description

@bokelley

Issue: simplify PreviewCreativeResponse for the single-render case

Type: schema ergonomics / nesting depth
Severity: medium (every creative-template adopter hits this on day 1)
Target: 3.x minor (additive — keep the union, hoist common fields)

Problem

PreviewCreativeResponse is a 3-variant discriminated union by response_type (single | batch | variant) where the single variant — the common case for stateless creative-template platforms — requires four levels of nesting to surface a single preview URL:

{
  response_type: 'single',
  previews: [
    {
      preview_id: 'pv_1',
      input: { name: 'default' },                  // echo-back boilerplate
      renders: [
        {
          render_id: 'r1',
          output_format: 'url',                    // discriminator
          role: 'primary',                         // semantic role for multi-piece
          preview_url: 'https://...',
        },
      ],
    },
  ],
  expires_at: '...',
}

For a watermarking platform, TTS service, or any single-output creative-template, the four nesting levels exist for shapes the platform doesn't use (preview-variants, multi-piece renders, macro echo-back). Adopters write the boilerplate or guess wrong.

Empirical: a controlled "be Emma" test of an AudioStack adapter against the v6.0 @adcp/client SDK + skill produced this verbatim feedback:

The skill's example returns { preview_url, expires_at } directly, but the generated PreviewCreativeResponse is a response_type-discriminated union with previews: [{ preview_id, renders: [...], input }]. I followed the generated shape; if the skill is intentionally lossy here it should be updated.

The adopter's instinct ({ preview_url, expires_at }) matched the actual common case; the wire shape forced the boilerplate.

Proposal

Two paths, in order of preference:

A. Hoist preview_url for single-render single-variant responses.

{
  response_type: 'single',
  preview_url: 'https://...',          // when previews.length === 1 && renders.length === 1
  expires_at: '...',
  previews?: [...],                    // OPTIONAL — present for batch/variant only
}

The previews[] array remains the source of truth for batch/variant responses; for single-render-single-variant it becomes optional. Validators that find both preview_url and previews[] use preview_url as the canonical answer.

B. Add a preview_url shorthand at the response root regardless of variant.

For any response, if exactly one render exists, preview_url carries it. The full previews[] structure is still emitted for completeness and audit.

Backwards compatibility

  • 3.x minor: emit preview_url alongside previews[] when there's a single render. Existing consumers reading the nested shape are unaffected.
  • 4.0: optional — promote preview_url to "preferred" and demote previews[] to "advanced multi-render contract."

Files affected

  • schemas/v3/creative/preview-creative-response.json
  • docs/v3/creative/preview_creative.md
  • llms.txt § preview_creative entry

Source

Surfaced during AdCP @adcp/client v6.0 SDK be-Emma test (audiostack-adapter test harness, round 2). The 4-level nesting is the most-cited DX friction point for stateless creative-template adopters.

Metadata

Metadata

Assignees

No one assigned

    Labels

    claude-triagedIssue has been triaged by the Claude Code triage routine. Remove to re-triage.creativerfcProtocol change — auto-adds to roadmap boardschemaJSON Schema source-of-truth: definitions, codegen artifacts, validation, hygiene

    Type

    No type

    Projects

    Status
    No status

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions