> ## Documentation Index
> Fetch the complete documentation index at: https://adcp-docs-ja.pier1.co.jp/llms.txt
> Use this file to discover all available pages before exploring further.

# preview_creative（高度な活用）

クリエイティブプレビューを統合するための高度なワークフロー、キャッシュ戦略、実装上の注意点を紹介します。

基本的な使い方は [preview\_creative](/docs/creative/task-reference/preview_creative) を参照してください。

## Common Workflows

### フォーマットショーケースページ

利用可能なフォーマットを閲覧できるカタログを構築します。

```typescript theme={null}
// 1. List all formats from creative agent
const formats = await creative_agent.list_creative_formats();

// 2. Generate format card previews (batch + HTML)
const formatPreviews = await creative_agent.preview_creative({
  request_type: "batch",
  output_format: "html",
  requests: formats.formats.map(format => ({
    format_id: format.format_id,
    creative_manifest: format.format_card.manifest
  }))
});

// 3. Render in a grid
function FormatCatalog({ formatPreviews }) {
  return (
    <div className="format-grid">
      {formatPreviews.results.map((result, idx) => (
        result.success && (
          <div
            key={idx}
            className="format-card"
            dangerouslySetInnerHTML={{
              __html: result.response.previews[0].renders[0].preview_html
            }}
          />
        )
      ))}
    </div>
  );
}
```

### キャンペーンレビュ―用グリッド

配信前にすべてのクリエイティブを確認します。

```typescript theme={null}
const campaignCreatives = await getCreativesForCampaign(campaignId);

const previews = await creative_agent.preview_creative({
  request_type: "batch",
  output_format: "html",
  requests: campaignCreatives.map(c => ({
    format_id: c.format_id,
    creative_manifest: c.manifest
  }))
});

function CampaignReview({ previews }) {
  return (
    <div className="review-grid">
      {previews.results.map((result, idx) => (
        <div className="creative-card">
          <div dangerouslySetInnerHTML={{
            __html: result.response.previews[0].renders[0].preview_html
          }}/>
          <button onClick={() => approve(idx)}>Approve</button>
          <button onClick={() => reject(idx)}>Reject</button>
        </div>
      ))}
    </div>
  );
}
```

### Web コンポーネントとの統合

遅延読み込みを行う本番アプリケーション向けの例です。

```html theme={null}
<script src="https://creative.adcontextprotocol.org/static/rendered-creative.js"></script>

<div class="grid">
  <rendered-creative
    src="https://creative-agent.example.com/preview/abc123"
    width="300"
    height="400"
    lazy="true">
  </rendered-creative>
</div>
```

**メリット:**

* CSS 分離のための Shadow DOM
* ビューポートに入ったときだけ読み込む遅延読み込み
* フレームワークに依存しません

## 出力形式の選択

**以下のケースでは `output_format: "url"`（デフォルト）を使用します。**

* セキュリティが最優先（サードパーティ製クリエイティブなど）
* インタラクティブなプレビューツールを構築する場合
* iframe での分離が必要な場合

**以下のケースでは `output_format: "html"` を使用します。**

* 10 件以上のフォーマットカタログを構築する場合
* 20 件以上のクリエイティブを並べるキャンペーンレビューグリッドを作る場合
* サーバーサイドレンダリングを行う場合
* 信頼できるクリエイティブエージェントのみを扱う場合

## キャッシュ戦略

`format_id` とマニフェストのハッシュの組み合わせで個別のプレビュー結果をキャッシュします。

```typescript theme={null}
function cachePreviewResults(results, formatIds, manifests) {
  results.forEach((result, idx) => {
    if (result.success) {
      const cacheKey = `${formatIds[idx]}:${hashManifest(manifests[idx])}`;
      cache.set(cacheKey, result.response, result.response.expires_at);
    }
  });
}

async function getPreviewsWithCache(formatIds, manifests) {
  const cached = [];
  const toFetch = [];

  formatIds.forEach((id, idx) => {
    const cacheKey = `${id}:${hashManifest(manifests[idx])}`;
    const cachedResult = cache.get(cacheKey);

    if (cachedResult && !isExpired(cachedResult.expires_at)) {
      cached[idx] = cachedResult;
    } else {
      toFetch.push({ idx, id, manifest: manifests[idx] });
    }
  });

  // Batch fetch only missing previews
  if (toFetch.length > 0) {
    const fetched = await client.preview_creative({
      request_type: "batch",
      output_format: "html",
      requests: toFetch.map(f => ({
        format_id: f.id,
        creative_manifest: f.manifest
      }))
    });

    fetched.results.forEach((result, i) => {
      cached[toFetch[i].idx] = result.response;
    });
  }

  return cached;
}
```

**ポイント:**

* バッチではなく format\_id + マニフェストハッシュごとにキャッシュします
* \[A,B,C] をリクエストしたらそれぞれ個別にキャッシュします
* 後で \[B,C,D] をリクエストしたら D だけ取得します
* キャッシュしたプレビューを使う前に必ず `expires_at` を確認します

## プレビュー URL のストレージ

プレビュー URL は、単なるトランスポートの利便性ではなく、レビューのリソースです。バイヤー、ブラウザ、または MCPUI ホストは、元の `preview_creative` 呼び出しが返った後、ポッドの再起動後、またはレンダーを作成したのとは異なるポッドから、`preview_url` をフェッチする場合があります。

本番エージェントでは、表明されたライフタイムを通じてすべての `preview_url` を解決するのに十分なプレビュー状態を永続化してください:

* `expires_at` が存在する場合、そのタイムスタンプまでレンダーを利用可能に保ちます。
* `expires_at` が省略された場合、URL をプロトコル層では期限切れにならないものとして扱い、明示的な帯域外の失効またはパージまで利用可能に保ちます。
* マルチプロセスまたはマルチポッドのデプロイでは共有ストレージを使います: データベースのメタデータ + オブジェクトストレージ、共有キャッシュ層、または耐久性のあるセッション状態からレンダーを回復できる認証済みプレビュールート。
* プロセスローカルの `Map` または LRU ストレージは、単一プロセスのデモ、ローカル開発、または表明されたライフタイムが実際に保証できるプロセスライフタイムより短い URL に限定してください。

## エラーハンドリング

```typescript theme={null}
const response = await client.preview_creative({
  request_type: "batch",
  requests: formatRequests
});

const succeeded = response.results.filter(r => r.success);
const failed = response.results.filter(r => !r.success);

if (failed.length > 0) {
  console.log(`${failed.length} previews failed`);
  failed.forEach((result) => {
    console.error(`  - ${result.error.code}: ${result.error.message}`);
  });
}

// Display successful previews, show error states for failures
function displayPreviews(results) {
  return results.map((result, idx) => {
    if (result.success) {
      return <Preview html={result.response.previews[0].renders[0].preview_html} />;
    } else {
      return <PreviewError
        code={result.error.code}
        message={result.error.message}
        onRetry={() => retryPreview(idx)}
      />;
    }
  });
}
```

## 単一リクエストからバッチへの移行

**以前（逐次）:**

```python theme={null}
previews = []
for format in formats:
    preview = await client.preview_creative(
        request_type="single",
        format_id=format.format_id,
        creative_manifest=format.format_card.manifest
    )
    previews.append(preview)
# Total time: N × 250ms = 5000ms for 20 formats
```

**移行後（バッチ）:**

```python theme={null}
response = await client.preview_creative(
    request_type="batch",
    output_format="html",
    requests=[
        {
            "format_id": fmt.format_id,
            "creative_manifest": fmt.format_card.manifest
        }
        for fmt in formats
    ]
)
# Total time: ~500ms for 20 formats
```

## ユースケースパターン

### デバイス別バリエーション

```json theme={null}
{
  "inputs": [
    { "name": "Desktop", "macros": { "DEVICE_TYPE": "desktop" } },
    { "name": "Mobile", "macros": { "DEVICE_TYPE": "mobile" } },
    { "name": "CTV", "macros": { "DEVICE_TYPE": "ctv" } }
  ]
}
```

### 地域別バリエーション

```json theme={null}
{
  "inputs": [
    { "name": "NYC", "macros": { "CITY": "New York", "DMA": "501" } },
    { "name": "LA", "macros": { "CITY": "Los Angeles", "DMA": "803" } }
  ]
}
```

### プライバシー対応テスト

```json theme={null}
{
  "inputs": [
    { "name": "Full consent", "macros": { "GDPR": "1", "GDPR_CONSENT": "CPc7TgP..." } },
    { "name": "No consent", "macros": { "GDPR": "1", "GDPR_CONSENT": "" } },
    { "name": "LAT enabled", "macros": { "LIMIT_AD_TRACKING": "1" } }
  ]
}
```

### AI 生成コンテンツのバリエーション

```json theme={null}
{
  "inputs": [
    { "name": "Morning commute", "context_description": "User commuting to work" },
    { "name": "Evening relaxation", "context_description": "User relaxing at home" }
  ]
}
```

## 実装メモ

### クリエイティブエージェント向け

**必須:**

1. `preview_url` から完全な HTML ページを返す
2. すべてのメディアタイプ（画像・動画・音声・インタラクティブ）を処理します
3. 入力パラメーターをレスポンスにエコーします
4. レンダリング前にマニフェストを検証します
5. マクロ値を適用する（またはデフォルトを使用）
6. プレビュー URL を、ロードバランシングと再起動を URL の表明されたライフタイムにわたって生き延びるストレージで裏付けます
7. セキュリティサンドボックスを実装します
8. 無期限に保持すべきでないプレビューに適切な有効期限を設定する（24–48 時間）

**オプションの拡張:**

* `hints` オブジェクトを提供する（メディアタイプ、寸法、時間など）
* `embedding` メタデータを提供する（サンドボックス方針、CSP）
* レスポンシブデザインをサポートします
* アクセシビリティ要素を含めます

### バイヤー向け

1. `preview_url` を iframe で表示するだけで特別なレンダリングは不要
2. 特定シナリオでは `inputs` 配列を利用します
3. `input` フィールドを確認しマクロ適用を検証します
4. 承認のためプレビュー URL をクライアントと共有します
5. 高度なテストには `interactive_url` を活用します

### パブリッシャー向け

1. プレビュー URL から一貫した HTML を返す
2. レスポンシブなプレビューページを実装します
3. フォーマット内の `supported_macros` で対応マクロを明記します
4. プレビューと本番の違いを明確にします
5. テスト用に `interactive_url` の提供を検討します

## 関連ドキュメント

* [preview\_creative](/docs/creative/task-reference/preview_creative) - 基本的な使い方とパラメーター
* [Creative Manifests](/docs/creative/creative-manifests) - マニフェスト構造
* [Universal Macros](/docs/creative/universal-macros) - 使用可能なマクロ値
