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

# log_event

> log_event タスク — コンバージョンおよびマーケティングイベントをバッチで AdCP セラーに送信します。アトリビューション、キャンペーン最適化、ROAS 計測、テストイベントをサポート。

アトリビューションと最適化のためにコンバージョンまたはマーケティングイベントを送信します。バッチ送信、テストイベント、および部分的な失敗レポートをサポートしています。

**レスポンス時間**: 約1秒（イベントは処理キューに追加されます）

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

## クイックスタート

購入イベントをログに記録する:

<CodeGroup>
  ```javascript JavaScript theme={null}
  import { testAgent } from "@adcp/sdk/testing";
  import { LogEventResponseSchema } from "@adcp/sdk";

  const result = await testAgent.logEvent({
    event_source_id: "website_pixel",
    events: [
      {
        event_id: "evt_purchase_12345",
        event_type: "purchase",
        event_time: "2026-01-15T14:30:00Z",
        action_source: "website",
        event_source_url: "https://www.example.com/checkout/confirm",
        user_match: {
          click_id: "abc123def456",
          click_id_type: "gclid",
        },
        custom_data: {
          value: 149.99,
          currency: "USD",
          order_id: "order_98765",
          num_items: 3,
        },
      },
    ],
  });

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

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

  // Check for operation-level errors first (discriminated union)
  if ("errors" in validated && validated.errors) {
    throw new Error(`Operation failed: ${JSON.stringify(validated.errors)}`);
  }

  if ("events_received" in validated) {
    console.log(`Received: ${validated.events_received}, Processed: ${validated.events_processed}`);
    if (validated.match_quality !== undefined) {
      console.log(`Match quality: ${(validated.match_quality * 100).toFixed(0)}%`);
    }
  }
  ```

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

  async def main():
      result = await test_agent.simple.log_event(
          event_source_id='website_pixel',
          events=[{
              'event_id': 'evt_purchase_12345',
              'event_type': 'purchase',
              'event_time': '2026-01-15T14:30:00Z',
              'action_source': 'website',
              'event_source_url': 'https://www.example.com/checkout/confirm',
              'user_match': {
                  'click_id': 'abc123def456',
                  'click_id_type': 'gclid'
              },
              'custom_data': {
                  'value': 149.99,
                  'currency': 'USD',
                  'order_id': 'order_98765',
                  'num_items': 3
              }
          }]
      )

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

      print(f"Received: {result.events_received}, Processed: {result.events_processed}")
      if hasattr(result, 'match_quality') and result.match_quality is not None:
          print(f"Match quality: {result.match_quality * 100:.0f}%")

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

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

| パラメータ             | 型                                                      | 必須  | 説明                                       |
| ----------------- | ------------------------------------------------------ | --- | ---------------------------------------- |
| `event_source_id` | string                                                 | はい  | `sync_event_sources` でアカウントに設定されたイベントソース |
| `events`          | [Event](/docs/media-buy/conversion-tracking/#event)\[] | はい  | ログに記録するイベント（最小1件、最大10,000件）              |
| `test_event_code` | string                                                 | いいえ | 本番データに影響を与えずに検証するためのテストイベントコード           |

### Event オブジェクト

| フィールド               | 型                                                                   | 必須  | 説明                                                                             |
| ------------------- | ------------------------------------------------------------------- | --- | ------------------------------------------------------------------------------ |
| `event_id`          | string                                                              | はい  | 重複排除のための一意の識別子（event\_type + event\_source\_id のスコープ内）。最大256文字。                |
| `event_type`        | [EventType](/docs/media-buy/conversion-tracking/#event-types)       | はい  | 標準イベントタイプ（例：`purchase`、`lead`、`add_to_cart`）                                   |
| `event_time`        | date-time                                                           | はい  | イベントが発生した時刻の ISO 8601 タイムスタンプ                                                  |
| `user_match`        | [UserMatch](/docs/media-buy/conversion-tracking/#user-match)        | いいえ | アトリビューションマッチング用のユーザー識別子                                                        |
| `custom_data`       | [CustomData](/docs/media-buy/conversion-tracking/#custom-data)      | いいえ | イベント固有のデータ（金額、通貨、アイテムなど）                                                       |
| `action_source`     | [ActionSource](/docs/media-buy/conversion-tracking/#action-sources) | いいえ | イベントが発生した場所（`website`、`app`、`in_store` など）                                     |
| `surface`           | [EventSurface](/docs/media-buy/conversion-tracking/#event-surfaces) | いいえ | イベントの構造化されたサーフェスコンテキスト（自社チャンネル、プロフィール、フィード、ポッドキャスト、ニュースレターリスト、ウェブサイト、アプリ、店舗など） |
| `event_source_url`  | uri                                                                 | いいえ | イベントが発生した URL（action\_source が `website` の場合は必須）                               |
| `custom_event_name` | string                                                              | いいえ | カスタムイベントの名前（event\_type が `custom` の場合に使用）                                     |

### User Match オブジェクト

`uids`、`hashed_email`、`hashed_phone`、`click_id`、または `client_ip` + `client_user_agent` のうち少なくとも1つが必須:

| フィールド               | 型      | 説明                                                         |
| ------------------- | ------ | ---------------------------------------------------------- |
| `uids`              | UID\[] | ユニバーサル ID の値（`rampid`、`id5`、`uid2`、`euid`、`pairid`、`maid`） |
| `hashed_email`      | string | 小文字・トリミング済みのメールアドレスの SHA-256 ハッシュ（64文字の16進数）               |
| `hashed_phone`      | string | E.164 形式の電話番号の SHA-256 ハッシュ（64文字の16進数）                     |
| `click_id`          | string | プラットフォームのクリック識別子（fbclid、gclid、ttclid など）                   |
| `click_id_type`     | string | クリック識別子の種類                                                 |
| `client_ip`         | string | 確率的マッチング用のクライアント IP アドレス                                   |
| `client_user_agent` | string | 確率的マッチング用のクライアントユーザーエージェント文字列                              |

**ハッシュ化:** ハッシュ化された識別子は SHA-256 の16進数文字列（64文字、小文字）でなければなりません。ハッシュ化前に正規化すること: メールアドレスは小文字かつ前後の空白をトリミング、電話番号は E.164 形式（例：`+12065551234`）にします。

### Custom Data オブジェクト

| フィールド              | 型          | 説明                                                                                                                                                                    |
| ------------------ | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `value`            | number     | イベントの金額                                                                                                                                                               |
| `currency`         | string     | ISO 4217 通貨コード（例：`USD`、`EUR`、`GBP`）                                                                                                                                   |
| `order_id`         | string     | 注文またはトランザクションの一意の識別子                                                                                                                                                  |
| `content_ids`      | string\[]  | 商品またはコンテンツの識別子。カタログ駆動型キャンペーンでは、カタログの `content_id_type`（SKU、GTIN、求人 ID など）に対応します。[カタログアイテムアトリビューション](/docs/media-buy/conversion-tracking#catalog-item-attribution)を参照。 |
| `content_type`     | string     | コンテンツのカテゴリ（商品、サービスなど）                                                                                                                                                 |
| `num_items`        | integer    | イベント内のアイテム数                                                                                                                                                           |
| `progress_percent` | number     | 到達したコンテンツ進捗の割合。主に `watch_milestone` イベント向け                                                                                                                            |
| `progress_seconds` | number     | 到達したコンテンツ進捗の長さ（秒）。主に時間ベースの `watch_milestone` イベント向け                                                                                                                   |
| `contents`         | Content\[] | アイテムごとの詳細（id、数量、価格、ブランド）                                                                                                                                              |

## レスポンス

**成功レスポンス:**

* `events_received` - 受信したイベント数
* `events_processed` - 正常にキューに追加されたイベント数
* `partial_failures` - バリデーションに失敗したイベント（event\_id、コード、メッセージを含む）
* `warnings` - 非致命的な問題（マッチ品質が低い、フィールドが欠落しているなど）
* `match_quality` - 全体的なマッチ品質スコア（0.0 〜 1.0）

**エラーレスポンス:**

* `errors` - オペレーションレベルのエラーの配列（無効なイベントソース、認証失敗など）

**注意:** レスポンスは判別共用体（discriminated union）を使用しており、成功フィールドまたは errors のどちらか一方のみが返されます。部分的な失敗は、成功レスポンス内にイベントごとに報告されます。

## よくあるシナリオ

### クリエイターエンゲージメントイベント

クリエイターまたはコンテンツのエンゲージメントイベントを、設定済みの自社プロパティのソースに対してログします。プロパティを識別するには `surface` を使い、視聴のしきい値は `custom_data.progress_percent` または `custom_data.progress_seconds` に入れます。

```json test=false theme={null}
{
  "event_source_id": "creator_channel",
  "events": [
    {
      "event_id": "evt_watch_001",
      "event_type": "watch_milestone",
      "event_time": "2026-01-16T11:05:00Z",
      "action_source": "system_generated",
      "surface": {
        "category": "owned_property",
        "property_type": "channel",
        "namespace": "video_platform",
        "property_id": "channel_123"
      },
      "user_match": {
        "uids": [{ "type": "uid2", "value": "CreatorViewer456" }]
      },
      "custom_data": {
        "content_ids": ["episode_001"],
        "progress_percent": 75
      }
    }
  ]
}
```

無料で継続的なオプトインには `event_type: "follow"` を使います。`event_type: "subscribe"` は有料の購読または有料のメンバーシップに限定します。

### バッチイベント

複数のイベントを1つのリクエストで送信します:

<CodeGroup>
  ```javascript JavaScript theme={null}
  import { testAgent } from "@adcp/sdk/testing";
  import { LogEventResponseSchema } from "@adcp/sdk";

  const result = await testAgent.logEvent({
    event_source_id: "website_pixel",
    events: [
      {
        event_id: "evt_purchase_001",
        event_type: "purchase",
        event_time: "2026-01-15T10:00:00Z",
        action_source: "website",
        user_match: {
          hashed_email: "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2",
          uids: [{ type: "uid2", value: "AbC123XyZ..." }],
        },
        custom_data: {
          value: 89.99,
          currency: "USD",
          order_id: "order_001",
        },
      },
      {
        event_id: "evt_lead_002",
        event_type: "lead",
        event_time: "2026-01-15T11:30:00Z",
        action_source: "website",
        user_match: {
          hashed_email: "f6e5d4c3b2a1f6e5d4c3b2a1f6e5d4c3b2a1f6e5d4c3b2a1f6e5d4c3b2a1f6e5",
          click_id: "abc123def456",
          click_id_type: "fbclid",
        },
      },
      {
        event_id: "evt_cart_003",
        event_type: "add_to_cart",
        event_time: "2026-01-15T12:15:00Z",
        action_source: "app",
        user_match: {
          uids: [{ type: "rampid", value: "Def456Ghi..." }],
        },
        custom_data: {
          content_ids: ["SKU-1234", "SKU-5678"],
          num_items: 2,
          value: 45.00,
          currency: "USD",
        },
      },
    ],
  });

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

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

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

  if ("events_received" in validated) {
    console.log(`${validated.events_processed}/${validated.events_received} events processed`);
    if (validated.partial_failures?.length) {
      for (const failure of validated.partial_failures) {
        console.log(`  Failed: ${failure.event_id} - ${failure.message}`);
      }
    }
  }
  ```

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

  async def main():
      result = await test_agent.simple.log_event(
          event_source_id='website_pixel',
          events=[
              {
                  'event_id': 'evt_purchase_001',
                  'event_type': 'purchase',
                  'event_time': '2026-01-15T10:00:00Z',
                  'action_source': 'website',
                  'user_match': {
                      'uids': [{'type': 'uid2', 'value': 'AbC123XyZ...'}]
                  },
                  'custom_data': {
                      'value': 89.99,
                      'currency': 'USD',
                      'order_id': 'order_001'
                  }
              },
              {
                  'event_id': 'evt_lead_002',
                  'event_type': 'lead',
                  'event_time': '2026-01-15T11:30:00Z',
                  'action_source': 'website',
                  'user_match': {
                      'click_id': 'abc123def456',
                      'click_id_type': 'fbclid'
                  }
              },
              {
                  'event_id': 'evt_cart_003',
                  'event_type': 'add_to_cart',
                  'event_time': '2026-01-15T12:15:00Z',
                  'action_source': 'app',
                  'user_match': {
                      'uids': [{'type': 'rampid', 'value': 'Def456Ghi...'}]
                  },
                  'custom_data': {
                      'content_ids': ['SKU-1234', 'SKU-5678'],
                      'num_items': 2,
                      'value': 45.00,
                      'currency': 'USD'
                  }
              }
          ]
      )

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

      print(f"{result.events_processed}/{result.events_received} events processed")
      if hasattr(result, 'partial_failures') and result.partial_failures:
          for failure in result.partial_failures:
              print(f"  Failed: {failure.event_id} - {failure.message}")

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

### テストイベント

本番データに影響を与えずにイベント連携を検証する:

<CodeGroup>
  ```javascript JavaScript theme={null}
  import { testAgent } from "@adcp/sdk/testing";
  import { LogEventResponseSchema } from "@adcp/sdk";

  const result = await testAgent.logEvent({
    event_source_id: "website_pixel",
    test_event_code: "TEST_12345",
    events: [
      {
        event_id: "test_evt_001",
        event_type: "purchase",
        event_time: new Date().toISOString(),
        action_source: "website",
        event_source_url: "https://www.example.com/checkout",
        user_match: {
          click_id: "test_click_abc",
          click_id_type: "gclid",
        },
        custom_data: {
          value: 99.99,
          currency: "USD",
        },
      },
    ],
  });

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

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

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

  if ("events_received" in validated) {
    console.log("Test event sent successfully");
    if (validated.warnings?.length) {
      console.log("Warnings:", validated.warnings);
    }
  }
  ```

  ```python Python theme={null}
  import asyncio
  from datetime import datetime, timezone
  from adcp.testing import test_agent

  async def main():
      result = await test_agent.simple.log_event(
          event_source_id='website_pixel',
          test_event_code='TEST_12345',
          events=[{
              'event_id': 'test_evt_001',
              'event_type': 'purchase',
              'event_time': datetime.now(timezone.utc).isoformat(),
              'action_source': 'website',
              'event_source_url': 'https://www.example.com/checkout',
              'user_match': {
                  'click_id': 'test_click_abc',
                  'click_id_type': 'gclid'
              },
              'custom_data': {
                  'value': 99.99,
                  'currency': 'USD'
              }
          }]
      )

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

      print('Test event sent successfully')
      if hasattr(result, 'warnings') and result.warnings:
          print(f"Warnings: {result.warnings}")

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

テストイベントはセラーのテストイベント UI に表示されるが、本番のアトリビューションやレポートには影響しません。

### 店舗内コンバージョン

CRM データを使用してオフラインコンバージョンを報告する:

<CodeGroup>
  ```javascript JavaScript theme={null}
  import { testAgent } from "@adcp/sdk/testing";
  import { LogEventResponseSchema } from "@adcp/sdk";

  const result = await testAgent.logEvent({
    event_source_id: "crm_import",
    events: [
      {
        event_id: "store_txn_20260115_001",
        event_type: "purchase",
        event_time: "2026-01-15T16:45:00Z",
        action_source: "in_store",
        user_match: {
          uids: [{ type: "rampid", value: "XyZ789AbC..." }],
        },
        custom_data: {
          value: 250.0,
          currency: "USD",
          order_id: "POS-2026-0115-001",
          contents: [
            { id: "SKU-JACKET-L", quantity: 1, price: 189.0 },
            { id: "SKU-SCARF-01", quantity: 1, price: 61.0 },
          ],
        },
      },
    ],
  });

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

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

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

  if ("events_received" in validated) {
    console.log(`In-store events processed: ${validated.events_processed}`);
  }
  ```

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

  async def main():
      result = await test_agent.simple.log_event(
          event_source_id='crm_import',
          events=[{
              'event_id': 'store_txn_20260115_001',
              'event_type': 'purchase',
              'event_time': '2026-01-15T16:45:00Z',
              'action_source': 'in_store',
              'user_match': {
                  'uids': [{'type': 'rampid', 'value': 'XyZ789AbC...'}]
              },
              'custom_data': {
                  'value': 250.00,
                  'currency': 'USD',
                  'order_id': 'POS-2026-0115-001',
                  'contents': [
                      {'id': 'SKU-JACKET-L', 'quantity': 1, 'price': 189.00},
                      {'id': 'SKU-SCARF-01', 'quantity': 1, 'price': 61.00}
                  ]
              }
          }]
      )

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

      print(f"In-store events processed: {result.events_processed}")

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

## イベントの重複排除

イベントは `event_id` + `event_type` + `event_source_id` の組み合わせで重複排除されます。同じイベントを複数回送信しても安全で、重複は無視されます。

リトライをまたいで安定した `event_id` の値を選ぶこと:

* トランザクション ID: `"order_98765"`
* 複合キー: `"purchase_user123_20260115"`
* UUID: `"550e8400-e29b-41d4-a716-446655440000"`

## エラーハンドリング

| エラーコード                | 説明                                                              | 対処方法                                                                                           |
| --------------------- | --------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| `REFERENCE_NOT_FOUND` | イベントソースが設定されていない、またはアクセスできない（`error.field` = `event_source_id`） | 先に `sync_event_sources` を実行する                                                                  |
| `INVALID_EVENT_TYPE`  | 認識されていない、または許可されていないイベントタイプ                                     | イベントソースの `event_types` 設定を確認する                                                                 |
| `INVALID_EVENT_TIME`  | イベント時刻が過去または未来に遠すぎる                                             | セラーのアトリビューションウィンドウ内のタイムスタンプを使用する                                                               |
| `MISSING_USER_MATCH`  | ユーザー識別子が提供されていない                                                | uids、hashed\_email、hashed\_phone、click\_id、または client\_ip + client\_user\_agent のうち少なくとも1つを含める |
| `BATCH_TOO_LARGE`     | イベントが10,000件を超えている                                              | 小さいバッチに分割する                                                                                    |
| `RATE_LIMITED`        | リクエストが多すぎる                                                      | 指数バックオフで待機してリトライする                                                                             |

## ベストプラクティス

1. **先にソースを設定する** — イベントを送信する前に必ず `sync_event_sources` を実行します。設定されていないソースへのイベントは拒否されます。

2. **user\_match を含める** — ユーザー識別子のないイベントはアトリビューションできません。利用可能な最も強力な識別子を提供すること: ハッシュ化されたメール・電話番号 > UID > クリック ID > IP/UA。マッチ率を最大化するために、複数の識別子タイプを可能な限り送信します。

3. **最初はテストイベントを使用する** — 連携の検証時は `test_event_code` を設定し、本番データに影響を与えずにイベントが正しく表示されることを確認します。

4. **可能な限りバッチ処理する** — API 呼び出し回数を減らすために、1リクエストあたり最大10,000件のイベントを送信します。バッチ内のイベントはそれぞれ独立して処理されます。

5. **value と currency を含める** — 購入イベントでは、ROAS レポートと最適化を有効にするために常に `custom_data.value` と `custom_data.currency` を含めます。

6. **安定したイベント ID を使用する** — ランダムな UUID ではなく、決定論的なイベント ID（注文番号、トランザクション ID）を使用します。これにより重複カウントなしに安全なリトライが可能になります。

7. **イベントを迅速に送信する** — できる限りリアルタイムに近いタイミングでイベントをログに記録します。セラーのアトリビューションウィンドウを超えたイベントはマッチングされない場合があります。

## 次のステップ

* [コンバージョントラッキング](/docs/media-buy/conversion-tracking/) — データモデル、最適化目標、エンドツーエンドのフロー
* [sync\_event\_sources](/docs/media-buy/task-reference/sync_event_sources) — イベントをログに記録する前にイベントソースを設定します
* [create\_media\_buy](/docs/media-buy/task-reference/create_media_buy#campaign-with-conversion-optimization) — パッケージに最適化目標を設定します
* [get\_media\_buy\_delivery](/docs/media-buy/task-reference/get_media_buy_delivery) — デリバリーレポートでコンバージョン指標を監視します
