[Private Beta]{class="badge informative"}

Guide du transformateur d’événements event-transformer-guide

Table des matières

Prise en main des défis de fidélité

AVAILABILITY
Cette fonctionnalité est actuellement en version bêta privée. Pour plus d’informations sur le cycle de publication et les phases de disponibilité dans Journey Optimizer, voir cycle de publication.

Avant qu’une transaction client puisse être appliquée à un défi de fidélité, elle doit être au format Événement de fidélité compris par le service de défi. Les événements client (à partir d’un système de point de vente, d’une application mobile, d’une plateforme d’e-commerce ou de toute autre source) utilisent généralement le schéma de données du client. Transformateurs d’événement comblez cet écart sans nécessiter de modifications du système en amont.

Vue d’ensemble

Une définition d’événement indique deux choses à la plateforme :

  • Quels événements demander — comment reconnaître qu’un événement entrant appartient à cette définition (correspondance)
  • Comment les remodeler — Expression JSONata qui mappe les champs du client au format de l’événement de fidélité (transformation)

Plusieurs définitions d’événement peuvent être configurées par organisation. La plateforme les évalue dans l’ordre et applique la première qui correspond. Les événements qui ne correspondent à aucune définition font l’objet d’une ingestion native (voir Secours - Événements de fidélité natifs).

Format d’événement de fidélité Adobe

Chaque définition d’événement doit générer un objet JSON au format suivant. Il s’agit des entrées que le service de défi traite.

{
  "_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)
    }
  ]
}

Notes de champ

Champ
Obligatoire
Remarques
loyalty_identity
Oui
Doit contenir id — l’identifiant de fidélité du membre.
item_list
Oui
Doit contenir ≥1 élément ; item_list vide est rejeté.
item_set
Oui (par article)
Les listes d’inclusion/exclusion de la tâche d’identification correspondent.
timestamp
Oui
Utilisé pour l’évaluation des fenêtres de date. Doit être ISO 8601.
utc_offset
Recommandé
Nécessaire pour la correspondance des tranches horaires et le comptage en continu des jours.
_id
Non
Utilisé pour le dédoublonnage si la détection des doublons est activée pour l’organisation.
sub_total
Non
Les tâches avec seuil de dépenses l’utilisent ; omettre signifie zéro dépense.

Champs de définition d’événement

Champ
Type
Obligatoire
Description
guid
Chaîne
Non (affecté par le système)
Identifiant unique attribué par le système ; lecture seule.
name
Chaîne
Oui
Étiquette lisible par l’utilisateur, par exemple "Starbucks POS Purchase".
xdmSchemaId
Chaîne
Oui
Fait correspondre les événements par identifiant de schéma XDM (voir Fonctionnement de la correspondance).
schema
Chaîne
Non
Schéma JSON (sous forme de chaîne) pour valider les événements entrants.
transformer
Chaîne
Oui
Expression JSONata mappant l’événement au format de fidélité.

Fonctionnement de la correspondance

Les événements arrivant par le biais du service principal de collecte de données (DCCS) portent une référence de schéma XDM dans leur enveloppe. La plateforme lit l’identifiant de schéma à partir de /body/xdmMeta/schemaRef/id et le compare aux xdmSchemaId de chaque définition.

La plateforme présente les définitions d’événement de l’organisation dans l’ordre et applique la première correspondance. Une fois qu’une correspondance est trouvée, le corps de la xdmEntity est transmis au transformateur.

Ecriture du transformateur

Le champ transformer est une expression JSONata. Il reçoit l’événement JSON entrant en entrée et doit renvoyer un objet Événement de fidélité Adobe valide.

Modèle de mappage de base

Mappez chaque champ de niveau supérieur du format cible au chemin d’accès correspondant dans votre événement source :

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
  }
}
Codage en dur du nom de l’événement

Si tous les événements correspondant à cette définition représentent la même activité logique, codez en dur le event_name :

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

event_name est utilisé pour les mesures et les rapports internes. Il n’est pas utilisé comme filtre de tâche — la qualification de la tâche est déterminée par item_set contenu, et non par le nom de l’événement.

Mappage d’identité pour les événements DCCS/XDM

Pour les événements arrivant via l’itinéraire DCCS, l’identité du membre est généralement conservée dans le champ de identityMap XDM standard plutôt que dans une propriété de client personnalisée. identityMap est un mappage indexé par espace de noms : la clé elle-même est le nom de l’espace de noms et la valeur est un tableau d’objets d’identité.

code language-jsonata
"loyalty_identity": {
  "id": identityMap.Email[0].id
}
  • Substitution d’espace de noms : remplacez Email par l’espace de noms que votre organisation utilise pour les membres du programme de fidélité (Loyalty, ECID, CRMID, etc.). Toujours lu à partir de l’espace de noms qui contient l’identité du profil de fidélité principal.

  • Utilisez toujours [0]: identityMap.Email est un tableau. Sans l’index, JSONata renvoie une séquence plutôt qu’une valeur unique si plusieurs identités sont présentes et loyalty_identity.id devient une liste. Épinglez-le au premier élément avec [0].

  • Évitez les champs client personnalisés pour l’identité : les groupes de champs personnalisés exposent parfois un champ qui ressemble à un e-mail (par exemple, _yourtenant.identification.core.email). Dans les données d’exemple, cela renvoie une valeur qui semble correcte, mais dans les événements de production, elle est souvent vide. La source fiable d’identité est toujours identityMap.

Création de item_set

item_set est un tableau d’identifiants de chaîne. Incluez chaque champ sur lequel vos tâches de défis peuvent filtrer :

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

Pour les événements non transactionnels (enregistrement, fin d’enquête, déclencheur personnalisé), un identifiant unique suffit :

code language-jsonata
"item_set": [eventName]
unit_price de mappage

unit_price doit être un prix à l’unité. Certains schémas source stockent plutôt un total de ligne (prix × quantité). Si votre champ d’origine est un total de ligne, divisez par quantité pour obtenir le prix unitaire :

code language-jsonata
"unit_price": priceTotal / quantity

Divisez uniquement si votre champ source est un total de ligne. S’il stocke déjà un prix unitaire, mappez-le directement - diviser un prix unitaire par la quantité produira silencieusement une valeur incorrecte.

Dérivation de transaction_id

Si votre événement source n’inclut pas d’identifiant de transaction, vous pouvez en dériver un stable à partir de la date et de l’heure :

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

Cette opération convertit l’horodatage ISO en millisecondes Epoch et produit une valeur déterministe pour un événement donné. Utilisez la fonction de génération d’identifiants de votre plateforme si elle est disponible.

Utilisation des fonctions JSONata

La bibliothèque complète de fonctions JSONata est disponible. Exemples utiles :

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 }

Exemples

Exemple 1 — Événement personnalisé simple (non transactionnel)

Scénario : une application mobile envoie un événement d’enregistrement. Il n’y a aucun élément de ligne — l’événement lui-même est l’activité admissible.

Événement entrant :

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

Définition de l’événement :

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}]}"
}

Transformateur formaté (pour la lisibilité) :

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
    }
  ]
}

Événement de fidélité 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 }]
}

Une tâche de défi sans restriction d’inclusion/exclusion comptabilise cet événement comme une visite de qualification, c’est-à-dire que la ["store-checkin"] d’entrée de item_set unique correspond à toute tâche qui autorise tous les éléments.

Exemple 2 — Achat PDV avec lignes

Scénario : un système de point de vente envoie une payload de transaction. Chaque élément de ligne possède un SKU et appartient à une catégorie. Les tâches de défi utilisent le SKU et la catégorie pour déterminer ce qui est admissible.

Événement entrant :

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"
  }
}

Définition de l’événement :

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}}"
}

Transformateur formaté:

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
  }
}

Événement de fidélité 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 }
  ]
}

Une tâche de type Défi avec include: ["BEVERAGE"] verrait l’élément de ligne de café qualifié (son item_set contient "BEVERAGE") et accumulerait 9,00 $ de dépenses pour cette tâche. La ligne de muffin serait exclue.

Exemple 3 : événement d’expérience AEP (correspondance de schémas XDM)

Scénario : les événements se propagent dans Adobe Journey Optimizer. L’événement entrant est un événement d’expérience XDM avec un identifiant de schéma connu. La plateforme utilise l’ID de schéma pour la correspondance plutôt qu’une vérification de chemin/valeur.

Corps d’entité XDM entrant (le xdmEntity extrait de l’événement AJO) :

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"
}

Définition de l’événement :

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}}"
}

Transformateur formaté:

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
  }
}

Remarque : lorsqu’un événement correspond par identifiant de schéma XDM, le transformateur ne reçoit que la partie xdmEntity de l’événement, et non l’enveloppe externe d’AJO. Tous les chemins de votre expression de transformateur sont relatifs au corps de l’entité XDM.

Ajouter la validation du schéma JSON (facultatif)

Si vous souhaitez que la plateforme valide la structure des événements entrants avant de tenter une transformation, définissez le champ schema sur un document Schéma JSON codé sous la forme d’une chaîne JSON.

Les événements dont la validation du schéma échoue sont rejetés avant l’exécution de la transformation. La réponse d’erreur inclut l’échec de validation spécifique, ce qui facilite le diagnostic des événements en amont malformés.

Exemple de schéma (exemple 2 ci-dessus)
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" }
            }
          }
        }
      }
    }
  }
}

Transmettez ce schéma en tant que chaîne JSON miniaturisée dans le champ schema de la définition d’événement.

Secours - Événements de fidélité natifs

Si aucune définition d’événement ne correspond à un événement entrant, la plateforme tente de l’ingérer directement en tant qu’événement de fidélité Adobe natif. Si la payload est déjà conforme au format d’événement de fidélité décrit ci-dessus, aucun transformateur n’est nécessaire et l’événement est appliqué en l’état. Cela permet aux clients qui ont préformaté leurs événements de contourner entièrement la transformation.

Référence d’API

Toutes les opérations de définition d’événement utilisent le chemin de base /loyalty/metadata/config/events.

Création d’une définition d’événement
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": "{ ... }"
}
Liste des définitions d’événement
code language-http
GET /loyalty/metadata/config/events
x-gw-ims-org-id: {ORG_ID}
x-sandbox-name: {SANDBOX}
Mettre à jour une définition d’événement
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 ... }"
}
Suppression d’une définition d’événement
code language-http
DELETE /loyalty/metadata/config/events/{eventId}
x-gw-ims-org-id: {ORG_ID}
x-sandbox-name: {SANDBOX}

Validation du transformateur

La syntaxe des expressions JSONata est validée lorsque la définition de l’événement est enregistrée. Si l’expression n’est pas valide, l’API renvoie une erreur 422 avec une description de l’échec de l’analyse.

Pour tester un transformateur avant son déploiement, utilisez l’Exerciseur JSONata — collez votre événement source en tant qu’entrée et votre expression de transformateur pour vérifier que la sortie correspond au format d’événement de fidélité attendu.

Pièges courants

Ces erreurs s’exécutent toutes sans erreur sur une simple payload de test d’un seul élément, ce qui est exactement la raison pour laquelle elles passent inaperçues. Testez toujours votre transformateur par rapport à une payload avec deux produits ou plus avant le déploiement.

Création d’un objet au lieu de mapper sur le tableau

L’erreur la plus fréquente. L’utilisation d’un seul littéral d’objet avec productListItems.SKU extrait chaque SKU et chaque quantité en séquences groupées au lieu de produire un élément de ligne par produit.

✗Réduit tous les éléments en un seul :

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

Avec deux produits, item_set contient les deux SKU et quantity devient un tableau comme [1, 4].

✓Un élément de ligne par produit :

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

La carte .{ } s’exécute une fois par produit, de sorte que chacune d’elles devient sa propre entrée.

Oublier l'index de tableau sur l'identité

identityMap.Email est un tableau. Sans [0], si un profil possède plusieurs identités dans cet espace de noms, id devient une liste de valeurs au lieu d’une seule chaîne.

identityMap.Email.id

identityMap.Email[0].id

Identité d’approvisionnement à partir d’un champ client personnalisé
Les groupes de champs personnalisés exposent parfois un champ qui ressemble à un e-mail, tel que _yourtenant.identification.core.email. Dans les exemples de données, elle renvoie une valeur et semble correcte, mais dans les événements de production, elle est souvent vide, ce qui entraîne la loyalty_identity.id de la valeur null. Utilisez toujours identityMap comme source d’identité.
Un tableau imbriqué qui fuit dans item_set

L’ajout d’un champ de catégorie à item_set semble simple, mais si productCategories est lui-même un tableau, le résultat se développe de manière imprévisible.

✗peut produire plus d’entrées que prévu :

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

Un produit avec trois catégories génère un item_set avec quatre valeurs.

✓indexez le tableau imbriqué pour obtenir exactement une valeur :

code language-jsonata
"item_set": [SKU, productCategories[0].categoryID]
item_list est vide ou manquant

Un événement avec un item_list vide ou absent est rejeté comme non valide. Pour les événements non transactionnels (archivages, déclencheurs personnalisés), il n’existe aucun élément de ligne naturel. Produisez donc un élément synthétique :

code language-jsonata
"item_list": [{ "item_set": [eventName], "quantity": 1 }]
timestamp en tant qu’entier Unix epoch au lieu d’ISO 8601

La plateforme attend une chaîne ISO 8601. Si votre événement source dure plusieurs millisecondes depuis l’époque Unix, convertissez-le :

code language-jsonata
"timestamp": $fromMillis(timestamp)
utc_offset omis
Sans utc_offset, la correspondance de la fenêtre de la partie de jour et le comptage de séries consécutives sont tous deux ignorés. Mappez le décalage UTC du magasin ou de l’appareil à partir de votre événement source là où il est disponible.
Chemins de transformation relatifs à l’enveloppe AJO sur un événement DCCS
Pour les événements DCCS, le transformateur ne reçoit que le corps xdmEntity, et non l’enveloppe externe d’AJO. Tous les chemins doivent être relatifs à la racine d’entité XDM. Si votre expression fait référence à des champs qui se trouvent dans l’enveloppe externe (par exemple, /body/xdmMeta/...), ils ne seront pas trouvés et produiront silencieusement la valeur null.
recommendation-more-help
journey-optimizer-help