イベントトランスフォーマーガイド event-transformer-guide

目次

ロイヤルティに関する課題を解決

お客様の取引をロイヤルティチャレンジに適用する前に、チャレンジサービスが理解できる​ Adobe ロイヤルティイベント ​形式である必要があります。 POS システム、モバイルアプリ、e コマースプラットフォームなどの顧客イベントでは、通常、顧客自身のデータスキーマを使用します。 イベント トランスフォーマ​は、アップストリーム システムに変更を加えることなく、このギャップを埋めます。

概要

イベント定義​は、プラットフォームに2つのことを伝えます。

  • 要求するイベント – 受信イベントがこの定義に属していることを認識する方法(一致)
  • リシェイプする方法 – 顧客のフィールドをロイヤルティイベント形式(変換)にマッピングするJSONata

組織ごとに複数のイベント定義を設定できます。 プラットフォームはそれらを順番に評価し、一致する最初のものを適用します。 どの定義にも一致しないイベントは、ネイティブ取り込みに失敗します(​ フォールバック – ネイティブロイヤルティイベント ​を参照)。

Adobeロイヤルティイベントのフォーマット

すべてのイベント定義では、次の形式でJSON オブジェクトを生成する必要があります。 これは、チャレンジサービスが処理する入力です。

{
  "_id":              "string — optional; used for duplicate detection if enabled",
  "event_name":       "string — used for internal metrics and reporting only (e.g. 'purchase', 'visit')",
  "timestamp":        "ISO 8601 date-time string — when the event occurred",
  "utc_offset":       "string — UTC offset of the store or device (e.g. '-07:00'); required for daypart matching",
  "location_id":      "string — optional; store or location identifier",
  "transaction_id":   "string — optional; dedup key for the transaction",
  "loyalty_identity": {
    "id": "string — the member's loyalty ID"
  },
  "item_list": [
    {
      "item_set":   ["string", "..."],  // one or more identifiers — SKU, category, event code, etc.
      "item_name":  "string — optional human-readable label",
      "quantity":   1,                  // integer; how many units
      "unit_price": 4.99,               // float; price per unit
      "sub_total":  4.99                // float; line total (quantity × unit_price)
    }
  ]
}

フィールドノート

フィールド
必須
メモ
loyalty_identity
id — メンバーのロイヤルティ IDを含める必要があります。
item_list
≥1個の項目が必要です。空のitem_listは拒否されます。
item_set
はい (項目ごと)
リストの含める/除外の識別子タスクが一致しました。
timestamp
日付ウィンドウの評価に使用します。 ISO 8601である必要があります。
utc_offset
推奨
デイパートマッチングとストリークデイ数に必要です。
_id
×
組織で重複検出が有効になっている場合に重複排除に使用します。
sub_total
×
支出しきい値タスクはこれを使用します。省略すると、支出がゼロになります。

イベント定義フィールド

フィールド
タイプ
必須
説明
guid
文字列
なし(システム割り当て)
システムに割り当てられた一意のID。読み取り専用。
name
文字列
人が判読できるラベル (例:"Starbucks POS Purchase"
xdmSchemaId
文字列
XDM スキーマ IDでイベントを照合します(「照合の仕組み」を参照)。
schema
文字列
×
受信イベントを検証するためのJSON スキーマ ​ (文字列として)。
transformer
文字列
イベントをロイヤルティ形式にマッピングするJSONata式。

マッチングの仕組み

データ収集コアサービス(DCCS)を介して到着したイベントでは、エンベロープにXDM スキーマ参照が含まれます。 プラットフォームは/body/xdmMeta/schemaRef/idからスキーマ IDを読み取り、各定義のxdmSchemaIdと比較します。

プラットフォームは、組織のイベント定義​ を順番 ​に歩き、最初の一致を適用します。 一致が見つかると、xdmEntity本文がトランスフォーマに渡されます。

トランスフォーマの書き方

transformer フィールドはJSONata式です。 受信イベント JSONを入力として受け取り、有効なAdobe Loyalty Event オブジェクトを返す必要があります。

基本マッピングパターン

ターゲット形式の各トップレベルフィールドを、ソースイベントの対応するパスにマッピングします。

code language-jsonata
{
  "_id":            sourceEvent._id,
  "event_name":     sourceEvent.eventType,
  "timestamp":      sourceEvent.timestamp,
  "utc_offset":     sourceEvent.storeInfo.utcOffset,
  "location_id":    sourceEvent.storeInfo.storeId,
  "transaction_id": sourceEvent.transaction.id,
  "loyalty_identity": {
    "id": sourceEvent.member.loyaltyId
  },
  "item_list": sourceEvent.transaction.items.{
    "item_set":   [itemSku, itemCategory],
    "item_name":  itemDescription,
    "quantity":   quantity,
    "unit_price": unitPrice,
    "sub_total":  lineTotal
  }
}
イベント名のハードコーディング

この定義に一致するすべてのイベントが同じ論理アクティビティを表す場合、event_nameをハードコードします。

code language-jsonata
{
  "event_name": "in-store-purchase",
  ...
}

event_nameは内部指標とレポートに使用されます。 タスクフィルターとして使用されません。タスクの選定は、イベント名ではなくitem_set個のコンテンツによって決定されます。

DCCS/XDM イベントのID マッピング

DCCS ルートを介して到着するイベントの場合、メンバーのIDは通常、カスタムテナントプロパティではなく、標準のXDM identityMap フィールドに格納されます。 identityMapは名前空間でキー設定されたマップです。キー自体は名前空間名で、値はID オブジェクトの配列です。

code language-jsonata
"loyalty_identity": {
  "id": identityMap.Email[0].id
}
  • 名前空間置換: Emailを、組織がロイヤルティメンバーに使用する名前空間に置き換えます – LoyaltyECIDCRMIDなど。プライマリロイヤルティプロファイル IDを保持する名前空間から常に読み取ります。

  • 常に[0]を使用: identityMap.Emailは配列です。 インデックスを使用しない場合、複数のIDが存在し、loyalty_identity.idがリストになる場合、JSONataは1つの値ではなくシーケンスを返します。 [0]を持つ最初の要素にピン留めします。

  • IDのカスタムテナントフィールドを使用しない: カスタムフィールドグループでは、電子メールのようなフィールドが公開されることがあります(例:_yourtenant.identification.core.email)。 サンプルデータでは、これは値を返し、正しく見えますが、実稼動イベントでは頻繁に空になります。 信頼できるID ソースは常にidentityMapです。

item_setを作成しています

item_setは文字列識別子の配列です。 チャレンジタスクがフィルタリングされる可能性のあるすべてのフィールドを含めます。

code language-jsonata
"item_set": [itemSku, productCategory, departmentCode]

非トランザクションイベント(チェックイン、アンケート完了、カスタムトリガー)の場合は、1つのIDで十分です。

code language-jsonata
"item_set": [eventName]
マッピング unit_price

unit_priceは単価にする必要があります。 一部のソーススキーマでは、代わりにラインの合計(価格×数量)が保存されます。 ソースフィールドがラインの合計である場合は、数量で割って単価を取得します。

code language-jsonata
"unit_price": priceTotal / quantity

ソースフィールドが行合計の場合にのみ分割します。 既に単価を保管している場合は、それを直接マッピングします。単価を数量で割ると、誤った値が生成されます。

派生中transaction_id

ソースイベントにトランザクション IDが含まれていない場合は、タイムスタンプから安定したIDを取得できます。

code language-jsonata
"transaction_id": "txn_" & $string($toMillis(timestamp))

これにより、ISO タイムスタンプがエポックミリ秒に変換され、特定のイベントに対して決定論的な値が生成されます。 利用可能な場合は、プラットフォーム独自のID生成関数を使用します。

JSONata関数の使用

完全なJSONata関数ライブラリを使用できます。 便利な例:

code language-jsonata
/* String concatenation */
"item_set": [skuId & ':' & categoryId]

/* Number formatting */
"item_set": ["spend:" & $formatNumber(totalAmount, '0.00')]

/* Conditional field */
"event_name": eventType ? eventType : "unknown"

/* Array transformation */
"item_list": items.{ "item_set": [sku], "quantity": qty, "sub_total": price * qty }

例1:単純なカスタムイベント(非トランザクション)

シナリオ: モバイルアプリがチェックインイベントを送信します。 行項目はありません。イベント自体が対象アクティビティです。

受信イベント:

code language-json
{
  "_id":       "evt-001",
  "eventName": "store-checkin",
  "timestamp": "2025-10-15T14:22:00Z",
  "storeId":   "STORE-042",
  "member": {
    "loyaltyId": "LM-8827361"
  }
}

イベント定義:

code language-json
{
  "name":        "Mobile Store Check-In",
  "xdmSchemaId": "https://ns.adobe.com/yourtenant/schemas/store-checkin-v1",
  "transformer": "{\"_id\": _id, \"event_name\": eventName, \"timestamp\": timestamp, \"location_id\": storeId, \"loyalty_identity\": {\"id\": member.loyaltyId}, \"item_list\": [{\"item_set\": [eventName], \"quantity\": 1}]}"
}

書式設定されたトランスフォーマ (読みやすくするため):

code language-jsonata
{
  "_id":        _id,
  "event_name": eventName,
  "timestamp":  timestamp,
  "location_id": storeId,
  "loyalty_identity": {
    "id": member.loyaltyId
  },
  "item_list": [
    {
      "item_set": [eventName],
      "quantity": 1
    }
  ]
}

Output Adobe ロイヤルティイベント:

code language-json
{
  "_id":        "evt-001",
  "event_name": "store-checkin",
  "timestamp":  "2025-10-15T14:22:00Z",
  "location_id": "STORE-042",
  "loyalty_identity": { "id": "LM-8827361" },
  "item_list": [{ "item_set": ["store-checkin"], "quantity": 1 }]
}

含める/除外の制限のないチャレンジタスクは、このイベントを対象となる訪問としてカウントします。単一のitem_set エントリ ["store-checkin"]は、すべての項目を許可する任意のタスクと一致します。

例2 – 行項目を使用したPOS購入

シナリオ:​販売時点情報管理システムは、トランザクションペイロードを送信します。 各行項目にはSKUがあり、カテゴリに属しています。 チャレンジタスクでは、SKUとカテゴリーを使用して何が適しているかを判断します。

受信イベント:

code language-json
{
  "_id":       "txn-20251015-4492",
  "timestamp": "2025-10-15T14:35:00Z",
  "storeInfo": {
    "storeId":   "STORE-042",
    "utcOffset": "-07:00"
  },
  "transaction": {
    "transactionId": "4492",
    "items": [
      { "sku": "COFFEE-001", "category": "BEVERAGE", "qty": 2, "unitPrice": 4.50, "lineTotal": 9.00 },
      { "sku": "MUFFIN-007", "category": "FOOD",     "qty": 1, "unitPrice": 3.25, "lineTotal": 3.25 }
    ]
  },
  "member": {
    "loyaltyId": "LM-8827361"
  }
}

イベント定義:

code language-json
{
  "name":        "Retail POS Purchase",
  "xdmSchemaId": "https://ns.adobe.com/yourtenant/schemas/retail-pos-purchase-v1",
  "transformer": "{\"_id\": _id, \"event_name\": \"purchase\", \"timestamp\": timestamp, \"utc_offset\": storeInfo.utcOffset, \"location_id\": storeInfo.storeId, \"transaction_id\": transaction.transactionId, \"loyalty_identity\": {\"id\": member.loyaltyId}, \"item_list\": transaction.items.{\"item_set\": [sku, category], \"quantity\": qty, \"unit_price\": unitPrice, \"sub_total\": lineTotal}}"
}

書式設定されたトランスフォーマ:

code language-jsonata
{
  "_id":            _id,
  "event_name":     "purchase",
  "timestamp":      timestamp,
  "utc_offset":     storeInfo.utcOffset,
  "location_id":    storeInfo.storeId,
  "transaction_id": transaction.transactionId,
  "loyalty_identity": {
    "id": member.loyaltyId
  },
  "item_list": transaction.items.{
    "item_set":   [sku, category],
    "quantity":   qty,
    "unit_price": unitPrice,
    "sub_total":  lineTotal
  }
}

Output Adobe ロイヤルティイベント:

code language-json
{
  "_id":            "txn-20251015-4492",
  "event_name":     "purchase",
  "timestamp":      "2025-10-15T14:35:00Z",
  "utc_offset":     "-07:00",
  "location_id":    "STORE-042",
  "transaction_id": "4492",
  "loyalty_identity": { "id": "LM-8827361" },
  "item_list": [
    { "item_set": ["COFFEE-001", "BEVERAGE"], "quantity": 2, "unit_price": 4.50, "sub_total": 9.00 },
    { "item_set": ["MUFFIN-007", "FOOD"],     "quantity": 1, "unit_price": 3.25, "sub_total": 3.25 }
  ]
}

include: ["BEVERAGE"]を持つチャレンジタスクは、コーヒー品目が選定され(そのitem_setには"BEVERAGE"が含まれます)、そのタスクに対する支出の$9.00が累積されます。 マフィン行項目は除外されます。

例3 - AEP Experience Event (XDM スキーママッチング)

シナリオ:​件のイベントはAdobe Journey Optimizerを流れます。 受信イベントは、既知のスキーマ IDを持つXDM エクスペリエンスイベントです。 プラットフォームは、パスと値のチェックではなく、スキーマ IDを使用して照合を行います。

受信XDM エンティティ本文 (AJO イベントから抽出されたxdmEntity):

code language-json
{
  "_brandname": {
    "identities": {
      "loyaltyId": "LM-8827361"
    },
    "transactions": {
      "transactionId": "TXN-9901",
      "storeNumber":   "042",
      "utcOffset":     "-07:00",
      "lineItems": [
        { "skuNumber": "11143053", "priceAmount": 345, "qty": 1, "category": "BEVERAGE" },
        { "skuNumber": "11161387", "priceAmount": 495, "qty": 1, "category": "FOOD" }
      ],
      "totalAmount": 840
    }
  },
  "_id":       "87c0cccf-5809-38e0-a703-3994e80173ab",
  "timestamp": "2025-07-04T16:03:32.000Z"
}

イベント定義:

code language-json
{
  "name":        "AJO Brand Purchase",
  "xdmSchemaId": "https://ns.adobe.com/brandname/schemas/purchase-event-v1",
  "transformer":  "{\"_id\": _id, \"event_name\": \"purchase\", \"timestamp\": timestamp, \"utc_offset\": _brandname.transactions.utcOffset, \"location_id\": _brandname.transactions.storeNumber, \"transaction_id\": _brandname.transactions.transactionId, \"loyalty_identity\": {\"id\": _brandname.identities.loyaltyId}, \"item_list\": _brandname.transactions.lineItems.{\"item_set\": [skuNumber, category], \"quantity\": qty, \"unit_price\": priceAmount, \"sub_total\": priceAmount * qty}}"
}

書式設定されたトランスフォーマ:

code language-jsonata
{
  "_id":            _id,
  "event_name":     "purchase",
  "timestamp":      timestamp,
  "utc_offset":     _brandname.transactions.utcOffset,
  "location_id":    _brandname.transactions.storeNumber,
  "transaction_id": _brandname.transactions.transactionId,
  "loyalty_identity": {
    "id": _brandname.identities.loyaltyId
  },
  "item_list": _brandname.transactions.lineItems.{
    "item_set":   [skuNumber, category],
    "quantity":   qty,
    "unit_price": priceAmount,
    "sub_total":  priceAmount * qty
  }
}

注: イベントがXDM スキーマ IDで一致する場合、トランスフォーマはイベントのxdmEntity部分のみを受け取り、外側のAJO エンベロープは受け取りません。 トランスフォーマ式のすべてのパスは、XDM エンティティ本文に対する相対パスです。

JSON スキーマ検証の追加(オプション)

変換を試みる前に、プラットフォームで受信イベントの構造を検証する場合は、schema フィールドをJSON文字列としてエンコードされたJSON スキーマ ​文書に設定します。

スキーマの検証に失敗したイベントは、変換の実行前に拒否されます。 エラー応答には特定の検証エラーが含まれるため、不正なアップストリームイベントを簡単に診断できます。

スキーマの例(上記の例2)
code language-json
{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "type": "object",
  "required": ["_id", "timestamp", "transaction", "member"],
  "properties": {
    "_id":       { "type": "string" },
    "timestamp": { "type": "string", "format": "date-time" },
    "member": {
      "type": "object",
      "required": ["loyaltyId"],
      "properties": {
        "loyaltyId": { "type": "string" }
      }
    },
    "transaction": {
      "type": "object",
      "required": ["items"],
      "properties": {
        "transactionId": { "type": "string" },
        "items": {
          "type": "array",
          "items": {
            "type": "object",
            "required": ["sku", "qty", "lineTotal"],
            "properties": {
              "sku":       { "type": "string" },
              "category":  { "type": "string" },
              "qty":       { "type": "number" },
              "unitPrice": { "type": "number" },
              "lineTotal": { "type": "number" }
            }
          }
        }
      }
    }
  }
}

このスキーマをイベント定義のschema フィールドに縮小JSON文字列として渡します。

フォールバック – ネイティブロイヤルティイベント

受信イベントと一致するイベント定義がない場合、プラットフォームはネイティブのAdobe ロイヤルティイベントとして直接取り込もうとします。 ペイロードが既に上記のロイヤルティイベント形式に準拠している場合、トランスは必要なく、イベントはそのまま適用されます。 これにより、イベントが事前にフォーマットされている顧客は、変換を完全にバイパスできます。

API リファレンス

すべてのイベント定義操作では、基本パス /loyalty/metadata/config/eventsが使用されます。

イベント定義の作成
code language-http
POST /loyalty/metadata/config/events
x-gw-ims-org-id: {ORG_ID}
x-sandbox-name: {SANDBOX}
Content-Type: application/json

{
  "name":        "Retail POS Purchase",
  "xdmSchemaId": "https://ns.adobe.com/yourtenant/schemas/retail-pos-purchase-v1",
  "transformer": "{ ... }"
}
イベント定義のリスト
code language-http
GET /loyalty/metadata/config/events
x-gw-ims-org-id: {ORG_ID}
x-sandbox-name: {SANDBOX}
イベント定義の更新
code language-http
PUT /loyalty/metadata/config/events/{eventId}
x-gw-ims-org-id: {ORG_ID}
x-sandbox-name: {SANDBOX}
Content-Type: application/json

{
  "name":        "Retail POS Purchase (v2)",
  "transformer": "{ ... updated expression ... }"
}
イベント定義の削除
code language-http
DELETE /loyalty/metadata/config/events/{eventId}
x-gw-ims-org-id: {ORG_ID}
x-sandbox-name: {SANDBOX}

変圧器の検証

JSONata式は、イベント定義が保存されたときに構文について検証されます。 式が無効な場合、APIは解析エラーの説明を含む422 エラーを返します。

デプロイする前にトランスフォーマをテストするには、JSONata Exerciserを使用します。ソースイベントを入力として貼り付け、トランスフォーマ式を使用して、出力が期待されるロイヤルティイベント形式と一致することを確認します。

陥りやすい失敗

これらのミスはすべて、単純な1項目のテストペイロードでエラーなく実行されます。これはまさに、検出されずに失敗する理由です。 デプロイする前に、必ず2つ以上の製品でペイロードに対してトランスフォーマをテストしてください。

配列をマッピングする代わりに1つのオブジェクトを作成する

最も頻繁な間違い。 productListItems.SKUを持つ単一のオブジェクトリテラルを使用すると、すべてのSKUとすべての数量が、製品ごとに1行の項目を生成するのではなく、グループ化されたシーケンスに取り込まれます。

✗は、すべての項目を1つに折りたたみます:

code language-jsonata
"item_list": [
  {
    "item_set": [ productListItems.SKU ],
    "quantity": productListItems.quantity
  }
]

2つの製品を使用すると、item_setは両方のSKUを保持し、quantity[1, 4]のような配列になります。

✓製品ごとに1つの行項目:

code language-jsonata
"item_list": [
  productListItems.{
    "item_set": [SKU],
    "quantity": quantity
  }
]

.{ } マップは製品ごとに1回実行され、各製品が独自のエントリになります。

IDの配列インデックスを忘れています

identityMap.Emailは配列です。 [0]を使用しない場合、プロファイルがその名前空間に複数のIDを持つ場合、idは単一の文字列ではなく値のリストになります。

identityMap.Email.id

identityMap.Email[0].id

カスタムテナントフィールドからのIDのソーシング
カスタムフィールドグループでは、_yourtenant.identification.core.emailなどの電子メール形式のフィールドが公開されることがあります。 サンプルデータでは値が返され、正しく見えますが、実稼動イベントでは頻繁に空になり、loyalty_identity.idがnullになります。 常にidentityMapをIDのソースとして使用してください。
item_setに漏洩しているネストされた配列

item_setにカテゴリーフィールドを追加するのは簡単ですが、productCategories自体が配列である場合、結果は予測不可能に展開されます。

✗は予想よりも多くのエントリを生成する可能性があります:

code language-jsonata
"item_set": [SKU, productCategories.categoryID]

3つのカテゴリを持つ製品は、4つの値を持つitem_setを生成します。

✓ネストされた配列をインデックス化して、1つの値を正確に取得します:

code language-jsonata
"item_set": [SKU, productCategories[0].categoryID]
item_listが空または見つかりません

item_listが空または存在しないイベントは、無効として拒否されます。 非トランザクションイベント(チェックイン、カスタムトリガー)の場合、自然な行の項目はないので、合成の行を生成します。

code language-jsonata
"item_list": [{ "item_set": [eventName], "quantity": 1 }]
timestampをISO 8601ではなくUnix エポック整数として使用

プラットフォームにはISO 8601文字列が必要です。 ソースイベントがエポックからミリ秒単位で進行する場合は、次のように変換します。

code language-jsonata
"timestamp": $fromMillis(timestamp)
utc_offsetが省略されました
utc_offsetを使用しない場合、日別ウィンドウのマッチングと連続日ストリーク数の両方がスキップされます。 ストアまたはデバイスのUTC オフセットを、ソースイベントが利用可能な場所からマッピングします。
DCCS イベントのAJO エンベロープに対するトランスフォーマーパス
DCCS イベントの場合、トランスフォーマはxdmEntity ボディのみを受け取り、外側のAJO エンベロープは受け取りません。 すべてのパスは、XDM エンティティのルートに対する相対パスである必要があります。 エクスプレッションが外側のエンベロープ(例:/body/xdmMeta/...)にあるフィールドを参照している場合、それらのフィールドは見つからず、nullが暗黙的に生成されます。
recommendation-more-help
journey-optimizer-help