> ## 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.

# sync_creatives

> sync_creatives は一括アップロード、アップサートセマンティクス、ジェネレーティブクリエイティブサポートを使用して AdCP ライブラリのクリエイティブアセットをアップロード・管理します。

クリエイティブライブラリにクリエイティブアセットをアップロードして管理します。一括アップロード、アップサートセマンティクス、ジェネレーティブクリエイティブをサポートします。クリエイティブライブラリをホストするすべてのエージェント — クリエイティブエージェント（広告サーバー、クリエイティブ管理プラットフォーム）およびクリエイティブを管理するセールスエージェント — が実装します。

**レスポンスタイム**: 即時〜数日（`completed` を返すか、数時間/数日かかるレビューのために `submitted` を返す）

**リクエストスキーマ**: [`creative/sync-creatives-request.json`](https://adcontextprotocol.org/schemas/v3/creative/sync-creatives-request.json)
**レスポンススキーマ**: [`creative/sync-creatives-response.json`](https://adcontextprotocol.org/schemas/v3/creative/sync-creatives-response.json)

## クイックスタート

クリエイティブアセットをアップロードする:

<CodeGroup>
  ```javascript JavaScript theme={null}
  import { testAgent } from "@adcp/sdk/testing";
  import { SyncCreativesResponseSchema } from "@adcp/sdk";
  import { randomUUID } from "node:crypto";

  const result = await testAgent.syncCreatives({
    account: {
      brand: { domain: "acmecorp.com" },
      operator: "acmecorp.com",
      sandbox: true,
    },
    idempotency_key: randomUUID(),
    creatives: [
      {
        creative_id: "creative_video_001",
        name: "Summer Sale 30s",
        format_id: {
          agent_url: "https://creative.adcontextprotocol.org",
          id: "video_standard_30s",
        },
        assets: {
          video: {
            asset_type: "video",
            url: "https://cdn.example.com/summer-sale-30s.mp4",
            width: 1920,
            height: 1080,
            duration_ms: 30000,
          },
        },
      },
    ],
  });

  if (!result.success) {
    throw new Error(`Request failed: ${result.error}`);
  }

  // Validate response against schema
  const validated = SyncCreativesResponseSchema.parse(result.data);

  // Three-shape discriminated union: errors | submitted | creatives
  if ("errors" in validated && validated.errors && !("creatives" in validated) && !("status" in validated)) {
    throw new Error(`Operation failed: ${JSON.stringify(validated.errors)}`);
  }

  if ("status" in validated && validated.status === "submitted") {
    // Whole sync queued asynchronously — poll tasks/get with task_id or await webhook
    console.log(`Sync queued as task ${validated.task_id}: ${validated.message ?? ""}`);
  } else if ("creatives" in validated) {
    console.log(`Synced ${validated.creatives.length} creatives`);
    for (const c of validated.creatives) {
      // c.status carries review state: approved, pending_review, rejected, processing, archived
      if (c.status === "pending_review" || c.status === "processing") {
        console.log(`  ${c.creative_id}: awaiting review (${c.status})`);
      }
    }
  }
  ```

  ```python Python theme={null}
  import asyncio
  from adcp.testing import test_agent
  from uuid import uuid4

  async def main():
      result = await test_agent.simple.sync_creatives(
          account={
              'brand': {'domain': 'acmecorp.com'},
              'operator': 'acmecorp.com',
              'sandbox': True
          },
          idempotency_key=str(uuid4()),
          creatives=[{
              'creative_id': 'creative_video_001',
              'name': 'Summer Sale 30s',
              'format_id': {
                  'agent_url': 'https://creative.adcontextprotocol.org',
                  'id': 'video_standard_30s'
              },
              'assets': {
                  'video': {
                      'asset_type': 'video',
                      'url': 'https://cdn.example.com/summer-sale-30s.mp4',
                      'width': 1920,
                      'height': 1080,
                      'duration_ms': 30000
                  }
              }
          }]
      )

      # Three-shape discriminated union: errors | submitted | creatives
      if getattr(result, 'status', None) == 'submitted':
          # Whole sync queued asynchronously — poll tasks/get with task_id or await webhook
          print(f"Sync queued as task {result.task_id}: {getattr(result, 'message', '') or ''}")
          return

      if getattr(result, 'errors', None) and not getattr(result, 'creatives', None):
          raise Exception(f"Operation failed: {result.errors}")

      print(f"Synced {len(result.creatives)} creatives")
      for c in result.creatives:
          # c.status carries review state: approved, pending_review, rejected, processing, archived
          if getattr(c, 'status', None) in ('pending_review', 'processing'):
              print(f"  {c.creative_id}: awaiting review ({c.status})")

  asyncio.run(main())
  ```
</CodeGroup>

**注意:** クリエイティブごとの非同期レビューは、同期的な成功レスポンスの `creatives[].status`（例: `pending_review`）で表面化されます。*操作全体*がキューに入れられる場合（バッチ取り込み、同期をゲートするガバナンスレビュー）、レスポンスはトップレベルの `status: "submitted"` と `task_id` を持つ submitted エンベロープになります。[非同期承認ワークフロー](#非同期承認ワークフロー)を参照。

## 書き込み後読み取りの可視性

同期的な `sync_creatives` の成功レスポンスを介して受理されたクリエイティブは、レスポンスが返される前にクリエイティブライブラリにコミットされていなければなりません（MUST）。それらは、同じアカウントと認可された呼び出し元からの後続の `list_creatives` 呼び出しに対して即座に可視でなければならず（MUST）、レビューのライフサイクルステータスが `processing` または `pending_review` のクリエイティブも含みます。

同期的な成功の分岐でクリエイティブを確認応答しつつ、ライブラリへの書き込みを後のバックグラウンドコミットまでバッファリングする実装は非準拠です。同期操作全体が返却前にコミットできない場合は、代わりに submitted タスクエンベロープを使います。その場合、可視性の要件は、受理されたクリエイティブとともにタスクが完了した時点で適用されます。

## リクエストパラメータ

| パラメータ             | タイプ         | 必須  | 説明                                                                                                                                |
| ----------------- | ----------- | --- | --------------------------------------------------------------------------------------------------------------------------------- |
| `account`         | object      | Yes | この同期の広告主/ワークスペースを識別するアカウント参照（[account-ref](/docs/accounts/overview)）                                                              |
| `idempotency_key` | string      | Yes | リトライを安全にするクライアント生成のキー。リクエストごとに新鮮な UUID などの一意の値を使います。                                                                              |
| `creatives`       | Creative\[] | Yes | アップロード/更新するクリエイティブアセット（最大100）                                                                                                     |
| `creative_ids`    | string\[]   | No  | 同期スコープを特定のクリエイティブ ID に限定するオプションフィルター。これらのクリエイティブのみが影響を受け、その他はそのまま残る。部分更新とエラー復旧に有用。                                                |
| `assignments`     | array       | No  | 一括アサインメント用の `{creative_id, package_id}` オブジェクトの配列。アサインメントごとにオプションの `weight` と `placement_ids`。                                    |
| `dry_run`         | boolean     | No  | true の場合、変更を適用せずにプレビューする（デフォルト: false）                                                                                            |
| `validation_mode` | string      | No  | 検証の厳格さ: `"strict"`（デフォルト）または `"lenient"`                                                                                          |
| `delete_missing`  | boolean     | No  | true の場合、この同期に含まれないクリエイティブはアーカイブされます（デフォルト: false）。`creative_ids` と組み合わせることはできません。アクティブで一時停止されていないパッケージにアサインされているクリエイティブは削除できません。 |

### クリエイティブオブジェクト

| フィールド               | タイプ                 | 必須          | 説明                                                                                                                                      |
| ------------------- | ------------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| `creative_id`       | string              | Yes         | このクリエイティブの一意の識別子                                                                                                                        |
| `name`              | string              | Yes         | 人間が読める名前                                                                                                                                |
| `format_id`         | FormatId            | Conditional | レガシーの名前付きフォーマットのパス。`agent_url` と `id` を持つ構造化オブジェクト。`format_kind` を省略する場合は必須。`format_kind` と相互排他的。                                       |
| `format_kind`       | CanonicalFormatKind | Conditional | 3.1+ の正準的なフォーマットのパス。`format_id` を省略する場合は必須。`format_id` と相互排他的。                                                                          |
| `format_option_ref` | FormatOptionRef     | No          | プロダクトまたはパブリッシャーのフォーマットオプションへの任意の構造化された参照。ターゲットプロダクトが同じ `format_kind` を持つ複数のオプションを持つ場合に必須。                                               |
| `assets`            | object              | Yes         | ロール名をキーとしたアセット（例: `{video: {...}, thumbnail: {...}}`）。カタログは `asset_type: "catalog"` を持つアセットとして含まれます。[カタログ](/docs/creative/catalogs)を参照。 |
| `tags`              | string\[]           | No          | クリエイティブ整理のための検索可能なタグ                                                                                                                    |

レガシーの `format_id` か正準的な `format_kind` のパスのいずれかを提供し、両方は決して提供しません。新しい 3.1+ の統合では、ルーティングがプロダクトの宣言したフォーマットオプションに依存する場合、`format_kind` と `format_option_ref` を優先すべきです。

### build\_creative バリアントのプロモート

バイヤーが生成されたビルドリーフを保持する場合、正準的なプロモートは、保持した `build_variant_id` を新しい `creative_id` として使うことです。セラーは別個のリネージマッピングを保持する必要はありません。デリバリーレポートは通常の `creative_id` を通じてビルドリーフに結合できます。

```json test=false theme={null}
{
  "idempotency_key": "550e8400-e29b-41d4-a716-446655440000",
  "account": { "account_id": "acct_acmecorp" },
  "creatives": [
    {
      "creative_id": "bv_card01_a",
      "name": "Summer card - studio take",
      "format_id": {
        "agent_url": "https://creative.example.com",
        "id": "display_300x250"
      },
      "assets": {
        "headline_0_text": {
          "asset_type": "text",
          "content": "Summer Sale - 50% Off"
        },
        "image_0_url": {
          "asset_type": "image",
          "url": "https://cdn.example.com/beach-hero.jpg",
          "width": 300,
          "height": 250
        }
      }
    }
  ]
}
```

その後、[`get_creative_delivery`](/docs/creative/task-reference/get_creative_delivery) は `creative_id` を結合キーとして使います。保持した `build_variant_id` を使う代わりに別のライブラリ id を発行するワークフローは、将来のスコープ付きリネージフィールドが採用されない限り、このプロトコルで可視な結合を失います。

### アセット構造

アセットはロール名をキーとします。各ロールにはアセットの詳細が含まれます:

```json test=false theme={null}
{
  "assets": {
    "video": {
      "url": "https://cdn.example.com/video.mp4",
      "width": 1920,
      "height": 1080,
      "duration_ms": 30000
    },
    "thumbnail": {
      "url": "https://cdn.example.com/thumb.jpg",
      "width": 300,
      "height": 250
    }
  }
}
```

### アサインメント構造

アサインメントはリクエストレベルにあり、クリエイティブ ID をパッケージ ID にマッピングします。メディアバイを管理しないスタンドアロンのクリエイティブエージェントはこのフィールドを無視します。

```json test=false theme={null}
{
  "assignments": [
    { "creative_id": "creative_video_001", "package_id": "pkg_premium" },
    { "creative_id": "creative_video_001", "package_id": "pkg_standard" },
    { "creative_id": "creative_display_002", "package_id": "pkg_standard" }
  ]
}
```

公開済み投稿（published-post）参照プロダクトでは、アセットのロールは通常 `published_post` で、ペイロードにはアップロードされたメディアバイトではなく投稿 URL またはプラットフォームの投稿 ID が含まれます。バイヤーは、プラットフォーム固有の `format_id` を作成する代わりに、正準的なクリエイティブのパス（例: `format_kind: "video_hosted"` とプロダクトの `format_option_ref`）を通じてこれらのアセットを送信できます。セラーが投稿を解決できるが、投稿を所有するパブリッシャーのアイデンティティのような必要な下流のプラットフォーム接続を欠く場合、訂正可能なエラーは `AUTHORIZATION_REQUIRED` です。新しい実装は `error.details.missing_connections[]` を含めるべきで、呼び出し元は人を正しい接続フローに通し、認可が回復した後にリトライできます。

## レスポンス

レスポンスは識別共用体を使います——レスポンスは三つの形のうちちょうど一つを持ち、混在することはありません:

**1. 同期的な成功** — クリエイティブごとの結果:

* `creatives` - 処理された各クリエイティブの結果（成功と失敗の両方のアイテムを含む）
* `dry_run` - これがドライランだったかどうかを示すブール値（オプション）

**2. 終端のエラー** — 処理されたクリエイティブなし:

* `errors` - 操作レベルのエラーの配列（認証失敗、サービス利用不可）

**3. Submitted タスクエンベロープ** — 操作全体が非同期でキューに入れられた（バッチ取り込み、同期をゲートするガバナンスレビュー）:

* `status` - 常に `"submitted"`
* `task_id` - `tasks/get` によるポーリングまたは完了時のウェブフック受信のためのハンドル
* `message` - キューの状態を説明する任意の人が読めるテキスト

最終的なクリエイティブごとの `creatives` 配列は、submitted エンベロープではなくタスクの完了アーティファクトに載ります。アイテムごとの非同期レビュー（同期の残りが解決される間、一つのクリエイティブが `pending_review` になっている）は、ここではなく、その項目に `status: "pending_review"` を持つ同期的な成功の分岐に属します。

**成功レスポンスの各クリエイティブに含まれるもの**:

* すべてのリクエストフィールド
* `platform_id` - プラットフォームの内部 ID（`action` が `failed` でない場合）
* `action` - この同期が実行したライフサイクル操作: `created`、`updated`、`unchanged`、`failed`、`deleted`
* `status` - **助言的**なレビューライフサイクルの状態（[`CreativeStatus`](https://adcontextprotocol.org/schemas/v3/enums/creative-status.json)）: `processing`、`pending_review`、`approved`、`suspended`、`rejected`、`archived`。UI のヒントおよびポーリングスケジューリングのシグナルであり、支出の認可ゲートでは**ありません**。`action` と直交します——`action` は同期が何をしたかを、`status` はクリエイティブがレビューライフサイクルのどこにいるかを記述します。値は `CreativeStatus` のみに由来し、`CreativeAction` からは決して来ません（`created`/`updated`/`failed` を `status` に入れないでください）。非同期レビューのセラーは `processing` または `pending_review` を返します。同期レビューのセラーは、終端の値（`approved`/`rejected`）や、回復可能な依存関係/認可のゲートが配信を妨げる場合に `suspended` を返してもよい（MAY）。**バイヤーは、このレスポンスの `status: approved` に基づいて下流の支出やパッケージの有効化をゲートしてはなりません（MUST NOT）**——支出をコミットする前に `list_creatives` または署名付きのレビューウェブフックで突き合わせてください。権威ある状態は常に `list_creatives` を介します。`action` が `failed` または `deleted` の場合は**省略されなければなりません（MUST）**——失敗したアイテムには意味のあるレビュー状態がなく（`errors` を参照）、削除されたアイテムはライブラリから消えています。スキーマは条件付き制約によってこの省略ルールを強制します。
* `errors` - エラーメッセージの配列（`action: "failed"` の場合のみ）
* `warnings` - 非致命的な警告の配列（オプション）

**完全なフィールドリストについてはスキーマを参照**: [sync-creatives-response.json](https://adcontextprotocol.org/schemas/v3/creative/sync-creatives-response.json)

## 一般的なシナリオ

### 一括アップロード

1回の呼び出しで複数のクリエイティブをアップロードする:

<CodeGroup>
  ```javascript JavaScript theme={null}
  import { testAgent } from "@adcp/sdk/testing";
  import { SyncCreativesResponseSchema } from "@adcp/sdk";
  import { randomUUID } from "node:crypto";

  const result = await testAgent.syncCreatives({
    account: {
      brand: { domain: "acmecorp.com" },
      operator: "acmecorp.com",
      sandbox: true,
    },
    idempotency_key: randomUUID(),
    creatives: [
      {
        creative_id: "creative_display_001",
        name: "Summer Sale Banner 300x250",
        format_id: {
          agent_url: "https://creative.adcontextprotocol.org",
          id: "display_300x250",
        },
        assets: {
          image: {
            url: "https://cdn.example.com/banner-300x250.jpg",
            width: 300,
            height: 250,
          },
        },
      },
      {
        creative_id: "creative_video_002",
        name: "Product Demo 15s",
        format_id: {
          agent_url: "https://creative.adcontextprotocol.org",
          id: "video_standard_15s",
        },
        assets: {
          video: {
            url: "https://cdn.example.com/demo-15s.mp4",
            width: 1920,
            height: 1080,
            duration_ms: 15000,
          },
        },
      },
      {
        creative_id: "creative_display_002",
        name: "Summer Sale Banner 728x90",
        format_id: {
          agent_url: "https://creative.adcontextprotocol.org",
          id: "display_728x90",
        },
        assets: {
          image: {
            url: "https://cdn.example.com/banner-728x90.jpg",
            width: 728,
            height: 90,
          },
        },
      },
    ],
  });

  if (!result.success) {
    throw new Error(`Request failed: ${result.error}`);
  }

  const validated = SyncCreativesResponseSchema.parse(result.data);

  if ("errors" in validated && validated.errors) {
    throw new Error(`Operation failed: ${JSON.stringify(validated.errors)}`);
  }

  if ("creatives" in validated) {
    console.log(`Successfully synced ${validated.creatives.length} creatives`);
    validated.creatives.forEach((creative) => {
      console.log(`  ${creative.name}: ${creative.platform_id}`);
    });
  }
  ```

  ```python Python theme={null}
  import asyncio
  from adcp.testing import test_agent
  from uuid import uuid4

  async def main():
      result = await test_agent.simple.sync_creatives(
          account={
              'brand': {'domain': 'acmecorp.com'},
              'operator': 'acmecorp.com',
              'sandbox': True
          },
          idempotency_key=str(uuid4()),
          creatives=[
              {
                  'creative_id': 'creative_display_001',
                  'name': 'Summer Sale Banner 300x250',
                  'format_id': {
                      'agent_url': 'https://creative.adcontextprotocol.org',
                      'id': 'display_300x250'
                  },
                  'assets': {
                      'image': {
                          'url': 'https://cdn.example.com/banner-300x250.jpg',
                          'width': 300,
                          'height': 250
                      }
                  }
              },
              {
                  'creative_id': 'creative_video_002',
                  'name': 'Product Demo 15s',
                  'format_id': {
                      'agent_url': 'https://creative.adcontextprotocol.org',
                      'id': 'video_standard_15s'
                  },
                  'assets': {
                      'video': {
                          'url': 'https://cdn.example.com/demo-15s.mp4',
                          'width': 1920,
                          'height': 1080,
                          'duration_ms': 15000
                      }
                  }
              },
              {
                  'creative_id': 'creative_display_002',
                  'name': 'Summer Sale Banner 728x90',
                  'format_id': {
                      'agent_url': 'https://creative.adcontextprotocol.org',
                      'id': 'display_728x90'
                  },
                  'assets': {
                      'image': {
                          'url': 'https://cdn.example.com/banner-728x90.jpg',
                          'width': 728,
                          'height': 90
                      }
                  }
              }
          ]
      )

      # Check for operation-level errors first
      if hasattr(result, 'errors') and result.errors:
          raise Exception(f"Operation failed: {result.errors}")

      print(f"Successfully synced {len(result.creatives)} creatives")
      for creative in result.creatives:
          print(f"  {creative.name}: {creative.platform_id}")

  asyncio.run(main())
  ```
</CodeGroup>

### ジェネレーティブクリエイティブ

クリエイティブエージェントを使用してブランドアイデンティティデータからクリエイティブを生成します。完全なワークフローの詳細は[ジェネレーティブクリエイティブガイド](/docs/creative/generative-creative)を参照。

<CodeGroup>
  ```javascript JavaScript theme={null}
  import { testAgent } from "@adcp/sdk/testing";
  import { SyncCreativesResponseSchema } from "@adcp/sdk";
  import { randomUUID } from "node:crypto";

  const result = await testAgent.syncCreatives({
    account: {
      brand: { domain: "acmecorp.com" },
      operator: "acmecorp.com",
      sandbox: true,
    },
    idempotency_key: randomUUID(),
    creatives: [
      {
        creative_id: "creative_gen_001",
        name: "AI-Generated Summer Banner",
        format_id: {
          agent_url: "https://creative.adcontextprotocol.org",
          id: "display_300x250",
        },
        assets: {
          manifest: {
            url: "https://cdn.example.com/brand.json",
          },
        },
      },
    ],
  });

  if (!result.success) {
    throw new Error(`Request failed: ${result.error}`);
  }

  const validated = SyncCreativesResponseSchema.parse(result.data);

  if ("errors" in validated && validated.errors) {
    throw new Error(`Operation failed: ${JSON.stringify(validated.errors)}`);
  }

  if ("creatives" in validated) {
    console.log(
      "Generative creative synced:",
      validated.creatives[0].creative_id
    );
  }
  ```

  ```python Python theme={null}
  import asyncio
  from adcp.testing import test_agent
  from uuid import uuid4

  async def main():
      result = await test_agent.simple.sync_creatives(
          account={
              'brand': {'domain': 'acmecorp.com'},
              'operator': 'acmecorp.com',
              'sandbox': True
          },
          idempotency_key=str(uuid4()),
          creatives=[{
              'creative_id': 'creative_gen_001',
              'name': 'AI-Generated Summer Banner',
              'format_id': {
                  'agent_url': 'https://creative.adcontextprotocol.org',
                  'id': 'display_300x250'
              },
              'assets': {
                  'manifest': {
                      'url': 'https://cdn.example.com/brand.json'
                  }
              }
          }]
      )

      # Check for operation-level errors first
      if hasattr(result, 'errors') and result.errors:
          raise Exception(f"Operation failed: {result.errors}")

      print(f"Generative creative synced: {result.creatives[0].creative_id}")

  asyncio.run(main())
  ```
</CodeGroup>

### ドライラン検証

アップロードせずにクリエイティブ設定を検証する:

<CodeGroup>
  ```javascript JavaScript theme={null}
  import { testAgent } from "@adcp/sdk/testing";
  import { SyncCreativesResponseSchema } from "@adcp/sdk";
  import { randomUUID } from "node:crypto";

  const result = await testAgent.syncCreatives({
    account: {
      brand: { domain: "acmecorp.com" },
      operator: "acmecorp.com",
      sandbox: true,
    },
    idempotency_key: randomUUID(),
    dry_run: true,
    creatives: [
      {
        creative_id: "creative_test_001",
        name: "Test Creative",
        format_id: {
          agent_url: "https://creative.adcontextprotocol.org",
          id: "video_standard_30s",
        },
        assets: {
          video: {
            url: "https://cdn.example.com/test-video.mp4",
            width: 1920,
            height: 1080,
            duration_ms: 30000,
          },
        },
      },
    ],
  });

  if (!result.success) {
    throw new Error(`Request failed: ${result.error}`);
  }

  const validated = SyncCreativesResponseSchema.parse(result.data);

  if ("errors" in validated && validated.errors && validated.errors.length > 0) {
    console.log("Validation errors found:");
    validated.errors.forEach((error) => console.log(`  - ${error.message}`));
  } else {
    console.log("Validation passed! Ready to sync.");
  }
  ```

  ```python Python theme={null}
  import asyncio
  from adcp.testing import test_agent
  from uuid import uuid4

  async def main():
      result = await test_agent.simple.sync_creatives(
          account={
              'brand': {'domain': 'acmecorp.com'},
              'operator': 'acmecorp.com',
              'sandbox': True
          },
          idempotency_key=str(uuid4()),
          dry_run=True,
          creatives=[{
              'creative_id': 'creative_test_001',
              'name': 'Test Creative',
              'format_id': {
                  'agent_url': 'https://creative.adcontextprotocol.org',
                  'id': 'video_standard_30s'
              },
              'assets': {
                  'video': {
                      'url': 'https://cdn.example.com/test-video.mp4',
                      'width': 1920,
                      'height': 1080,
                      'duration_ms': 30000
                  }
              }
          }]
      )

      if hasattr(result, 'errors') and result.errors:
          error_messages = [error.message for error in result.errors]
          raise Exception(f"Validation errors: {error_messages}")

      print('Validation passed! Ready to sync.')

  asyncio.run(main())
  ```
</CodeGroup>

### creative\_ids フィルターを使用したスコープ更新

大きなライブラリから特定のクリエイティブのみを更新し、その他には影響を与えない:

<CodeGroup>
  ```javascript JavaScript theme={null}
  import { testAgent } from "@adcp/sdk/testing";
  import { SyncCreativesResponseSchema } from "@adcp/sdk";
  import { randomUUID } from "node:crypto";

  // ライブラリの 100+ のうち 2 つのクリエイティブのみを更新
  const result = await testAgent.syncCreatives({
    account: {
      brand: { domain: "acmecorp.com" },
      operator: "acmecorp.com",
      sandbox: true,
    },
    idempotency_key: randomUUID(),
    creative_ids: ["creative_video_001", "creative_display_001"],
    creatives: [
      {
        creative_id: "creative_video_001",
        name: "Summer Sale 30s - Updated",
        format_id: {
          agent_url: "https://creative.adcontextprotocol.org",
          id: "video_standard_30s",
        },
        assets: {
          video: {
            url: "https://cdn.example.com/updated-video.mp4",
            width: 1920,
            height: 1080,
            duration_ms: 30000,
          },
        },
      },
      {
        creative_id: "creative_display_001",
        name: "Summer Sale Banner - Updated",
        format_id: {
          agent_url: "https://creative.adcontextprotocol.org",
          id: "display_300x250",
        },
        assets: {
          image: {
            url: "https://cdn.example.com/updated-banner.jpg",
            width: 300,
            height: 250,
          },
        },
      },
    ],
  });

  if (!result.success) {
    throw new Error(`Request failed: ${result.error}`);
  }

  const validated = SyncCreativesResponseSchema.parse(result.data);

  if ("errors" in validated && validated.errors) {
    throw new Error(`Update failed: ${JSON.stringify(validated.errors)}`);
  }

  if ("creatives" in validated) {
    console.log(
      `Updated ${validated.creatives.length} creatives, others untouched`
    );
  }
  ```

  ```python Python theme={null}
  import asyncio
  from adcp.testing import test_agent
  from uuid import uuid4

  async def main():
      # ライブラリの 100+ のうち 2 つのクリエイティブのみを更新
      result = await test_agent.simple.sync_creatives(
          account={
              'brand': {'domain': 'acmecorp.com'},
              'operator': 'acmecorp.com',
              'sandbox': True
          },
          idempotency_key=str(uuid4()),
          creative_ids=['creative_video_001', 'creative_display_001'],
          creatives=[
              {
                  'creative_id': 'creative_video_001',
                  'name': 'Summer Sale 30s - Updated',
                  'format_id': {
                      'agent_url': 'https://creative.adcontextprotocol.org',
                      'id': 'video_standard_30s'
                  },
                  'assets': {
                      'video': {
                          'url': 'https://cdn.example.com/updated-video.mp4',
                          'width': 1920,
                          'height': 1080,
                          'duration_ms': 30000
                      }
                  }
              },
              {
                  'creative_id': 'creative_display_001',
                  'name': 'Summer Sale Banner - Updated',
                  'format_id': {
                      'agent_url': 'https://creative.adcontextprotocol.org',
                      'id': 'display_300x250'
                  },
                  'assets': {
                      'image': {
                          'url': 'https://cdn.example.com/updated-banner.jpg',
                          'width': 300,
                          'height': 250
                      }
                  }
              }
          ]
      )

      if hasattr(result, 'errors') and result.errors:
          raise Exception(f"Update failed: {result.errors}")

      print(f"Updated {len(result.creatives)} creatives, others untouched")

  asyncio.run(main())
  ```
</CodeGroup>

**creative\_ids フィルターを使用する理由:**

* スコープ更新: 指定されたクリエイティブのみが変更され、ライブラリに 100+ あっても同様
* エラー復旧: 一括同期の検証失敗後に失敗したクリエイティブのみをリトライ
* パフォーマンス: スコープが事前にわかっているとパブリッシャーが処理を最適化できます
* 安全性: 明示的なターゲティングにより意図しない変更のリスクを低減

## 非同期承認ワークフロー

二つの異なる非同期パターンがあります——エージェントの振る舞いに応じて正しいものを選んでください:

**クリエイティブごとの非同期レビュー**（一般的）: 同期操作自体は同期的に解決されますが、1 つ以上のクリエイティブが下流のレビュー（ブランドセーフティ、ポリシーコンプライアンス）を必要とします。レビュー中のアイテムは、`status: "pending_review"`（または取り込み中は `processing`）とともに同期的な成功レスポンスで返ってきます。バイヤーは `list_creatives` またはウェブフックを通じて終端の状態を突き合わせます。

**操作レベルの非同期**（あまり一般的でない）: 同期全体がキューに入れられます——取り込みがバッチ化されている、またはガバナンスレビューが同期全体をゲートしているため、セラーが応答前にアイテムごとの結果を返せません。レスポンスは submitted エンベロープです:

* トップレベルの `status: "submitted"` と `task_id`
* `message` — 任意の人が読める説明
* このエンベロープには `creatives` 配列なし

`tasks/get` をポーリングするか、ウェブフックを待ちます。完了アーティファクトが、アイテムごとの `action`/`status` の結果を持つ `creatives` 配列を運びます。操作レベルの失敗は、タスク上の `status: "failed"` として表面化します。

**参照:** ウェブフック設定については[ウェブフック](/docs/building/by-layer/L3/webhooks)を参照。

## 同期モード

### アップサート（デフォルト）

* `creative_id` で既存のクリエイティブを作成または更新します
* パッケージアサインメントをマージする（追加的）
* 提供されたフィールドを更新し、その他はそのままにします
* 特定のクリエイティブにスコープを制限するために `creative_ids` フィルターを使用します

### ドライラン

* 変更を加えずにリクエストを検証します
* エラーと警告を返す
* アセットを処理したりクリエイティブを作成したりしません
* プリフライト検証チェックに使用します

## エラー処理

| エラーコード                        | 説明                                                                       | 解決方法                                                                                 |
| ----------------------------- | ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------ |
| `INVALID_FORMAT`              | フォーマットがプロダクトでサポートされていない                                                  | `list_creative_formats` でプロダクトのサポートフォーマットを確認する                                       |
| `ASSET_PROCESSING_FAILED`     | アセットファイルが破損しているか無効                                                       | アセットがフォーマット要件（コーデック、ディメンション、デュレーション）を満たしているか確認する                                     |
| `PACKAGE_NOT_FOUND`           | パッケージ ID がメディアバイに存在しない                                                   | `package_id` を確認する。レガシーなパッケージの相関には `get_media_buys` + パッケージの `context.buyer_ref` を使う |
| `BRAND_SAFETY_VIOLATION`      | クリエイティブがブランドセーフティスキャンに失敗                                                 | パブリッシャーのブランドセーフティガイドラインに対してコンテンツをレビューする                                              |
| `FORMAT_MISMATCH`             | アセットがフォーマット要件に一致しない                                                      | アセットタイプと仕様がフォーマット定義と一致しているか確認する                                                      |
| `CREATIVE_IN_ACTIVE_DELIVERY` | クリエイティブがアクティブで一時停止されていないパッケージにアサインされている（更新と `delete_missing` による削除をブロック） | まずパッケージを一時停止するか、新しいクリエイティブバージョンを作成する                                                 |

## ベストプラクティス

1. **アップサートセマンティクスを使用する** — 同じ `creative_id` で既存のクリエイティブを更新し、重複を作成しません。これにより反復的なクリエイティブ開発が可能。注意: アクティブな配信中のクリエイティブは更新がブロックされます（#7 を参照）。

2. **まず検証する** — `dry_run: true` を使用して実際のアップロード前にエラーをキャッチします。帯域幅と処理時間を節約できます。

3. **アサインメントをバッチ処理する** — 更新間の競合状態を避けるために、すべてのパッケージアサインメントを1回の同期呼び出しに含めます。

4. **CDN ホストのアセット** — 高速処理のために公開アクセス可能な CDN URL を使用します。プラットフォームはプロキシ遅延なしに直接アセットをフェッチできます。

5. **ブランドアイデンティティ** — ジェネレーティブクリエイティブの場合、処理失敗を避けるために同期前にブランドアイデンティティスキーマを検証します。

6. **フォーマットサポートを確認する** — アップロード前に `list_creative_formats` を使用してプロダクトがクリエイティブフォーマットをサポートしているか確認します。

7. **アクティブ配信の保護** — アクティブで一時停止されていないパッケージにアサインされているクリエイティブは、`delete_missing` で更新または削除できません。まずパッケージを一時停止するか、`update_media_buy` でクリエイティブのアサインを解除するか、別の `creative_id` で新しいクリエイティブを作成します。

## 関連タスク

* [`list_creative_formats`](/docs/creative/task-reference/list_creative_formats) - アップロード前にサポートフォーマットを確認します
* [`list_creatives`](/docs/creative/task-reference/list_creatives) - ライブラリ内のクリエイティブをブラウズ・フィルタリングします
* [`build_creative`](/docs/creative/task-reference/build_creative) - ライブラリクリエイティブからマニフェストをビルドするか、ゼロから生成します
* [`preview_creative`](/docs/creative/task-reference/preview_creative) - クリエイティブマニフェストのプレビューを生成します
* [クリエイティブアセットタイプ](/docs/creative/asset-types) - アセットの技術要件
