獎勵定義指南 reward-definition-guide

在此頁面上:​瞭解如何設定獎勵提供者和獎勵定義、撰寫獎勵JSONata運算式,以及瞭解用來產生履行裝載的內容。

當挑戰任務、里程碑或挑戰完成​ 並設定了獎勵值 ​時,平台會呼叫您的獎勵提供者的HTTP端點並使用JSON裝載來發出獎勵。 獎勵定義​說明問題的獎勵,並提供JSONata運算式 — rewardJsonata — 以圖形化您的提供者所期望的確切裝載。

本指南涵蓋如何設定獎勵提供者、建立獎勵定義、撰寫rewardJsonata運算式,以及瞭解評估時可供使用的內容。

➡️ 觀看如何設定忠誠獎勵提供者

雙層模型

獎勵分為兩個層級:

Reward Provider  (endpoint, auth, headers)
└── Reward Definition  (denomination, rewardJsonata)
└── Reward Definition
└── ...

獎勵提供者​代表單一外部獎勵系統 — 它包含傳遞端點URL、驗證及任何自訂HTTP標頭。 一個提供者可以保留多個​獎勵定義,每個定義都描述該提供者提供的不同獎勵型別或面額(例如「50顆星」、「雙顆星」、「免費專案」)。

質詢會依據GUID參考提供者和定義。 當發出獎勵時,平台會評估定義的rewardJsonata運算式,並將結果POST至提供者的端點。

獎勵提供者與定義欄位

獎勵提供者欄位
table 0-row-4 1-row-4 2-row-4 3-row-4 4-row-4 5-row-4 6-row-4 7-row-4 8-row-4 html-authored
欄位 類型 必要 說明
guid String 否(系統指派) 唯一識別碼。 唯讀。
name String 是 顯示名稱,組織內唯一。
desc String 無 提供者可讀取的說明。
enabled Boolean 無 當false時,已針對此提供者下的所有定義暫停
獎勵傳遞。
url String 是 接收獎勵裝載的HTTP端點。
平台POST將評估過的
rewardJsonata輸出傳至此URL。
additionalHeaders Object 無 自訂HTTP標頭,可包含在每
個傳遞要求中(例如API金鑰、
內容型別覆寫)。
maxRatePerSecond Integer 無 選擇性的每個提供者速率限制(1-5000)。
Null表示無限制。
enableMTLS Boolean 無 端點是否需要雙向TLS。
獎勵定義欄位
table 0-row-4 1-row-4 2-row-4 3-row-4 4-row-4 5-row-4 6-row-4 7-row-4 html-authored
欄位 類型 必要 說明
guid String 否(系統指派) 唯一識別碼。 唯讀。
name String 是 顯示名稱,在提供者中唯一。
denomination String 無 獎勵的單位,用於顯示
,可在運算式中作為
reward.denomination
使用(例如"Stars"、"Points"、"Miles")。
desc String 無 獎勵的說明,可在運算式中以reward.desc形式取得
。
enabled Boolean 無 當false時,此定義為非使用中
且不會發出獎勵。
isDefault Boolean 無 將此專案標籤為沙箱範圍的預設
獎勵定義。 所有提供者一次只能有一個定義
是預設值;
設定新的預設值會清除上一個定義。
用於在發佈時自動填入
個人化挑戰的獎勵詳細資料。
rewardJsonata String 是 JSONata運算式在
獎勵問題時間評估。 接收完整
獎勵內容,且必須將JSON
裝載傳回至POST給提供者。

獎勵內容

評估rewardJsonata時,它會收到包含獎勵事件所有已知內容的單一根物件。 運算式中的所有路徑都與此根相關。

{
  "rewardContext": {
    "rewardValue": "50",
    "source":      "challenge"
  },
  "reward": {
    "name":         "500 Stars",
    "desc":         "Issue 500 Stars to the member",
    "denomination": "Stars",
    "enabled":      true
  },
  "task": { ... },
  "milestone": { ... },
  "challenge": { ... },
  "timestamp": "2026-02-10T00:29:22.538+00:00"
}
內容欄位
table 0-row-2 1-row-2 2-row-2 3-row-2 4-row-2 5-row-2 6-row-2 7-row-2 8-row-2 9-row-2 10-row-2 11-row-2 12-row-2 13-row-2 14-row-2 15-row-2 16-row-2
欄位 說明
rewardContext.rewardValue 在觸發此發行的挑戰、任務或里程碑上設定的獎勵值字串。
rewardContext.source 觸發獎勵的專案: "task"、"challenge"或"milestone"。
reward RewardDefinition本身 — name、desc、denomination。
task 完成的工作,包括其accumulators、schedule和reward。
task.accumulators.spend 任務累積的合格總支出。
task.accumulators.qty 任務累積的合格專案總數。
task.accumulators.item_list 套用至任務的所有合格專案。 每個專案都有item、transactionId、timestamp、utcOffset、locationId。
task.accumulators.item_list[-1] 最近套用的專案(JSONata負索引)。 對於取得最後一個交易ID或時間戳記非常有用。
task.schedule.currentStreak 目前的連續造訪連續計數(針對連續挑戰)。
task.schedule.currentVisits 造訪總數(針對造訪挑戰)。
milestone 觸發此獎勵的里程碑;若不是里程碑獎勵,則為null。 包含count和reward.rewardValue。
challenge.profileId 成員的熟客ID。
challenge.kvpCustom 在挑戰設定的自訂索引鍵值配對。 傳遞行銷活動ID、產品名稱或提供者專用中繼資料的常見模式。
challenge.name 挑戰名稱。
challenge._id 挑戰ID。
timestamp 獎勵簽發的ISO 8601時間戳記。

撰寫rewardJsonata運算式

運算式會接收獎勵內容作為其輸入,且必須傳回JSON物件 — 裝載已對提供者的端點進行POST。 該物件的形狀完全取決於提供者的API;您可以將內容欄位對應到提供者期望的任何結構。

簡單固定裝載

最簡單的情況:提供者需要點數和成員ID,兩者都從內容中得知。

code language-jsonata
{
  "memberId":   challenge.profileId,
  "points":     $number(rewardContext.rewardValue),
  "currency":   reward.denomination
}

輸出:

code language-json
{
  "memberId": "ADB-0000030",
  "points":   50,
  "currency": "Stars"
}

rewardContext.rewardValue永遠是字串。 如果您的提供者需要數值,請使用$number()進行轉換。

針對提供者特定的中繼資料使用kvpCustom

提供者通常需要行銷活動ID或原始系統程式碼等欄位,這些欄位是每個挑戰執行所專屬。 編寫挑戰時將這些專案儲存在challenge.kvpCustom中,然後在運算式中參照它們 — 讓運算式可跨行銷活動重複使用。

code language-jsonata
{
  "memberId":         challenge.profileId,
  "points":           $number(rewardContext.rewardValue),
  "campaignId":       challenge.kvpCustom.campaignId,
  "transactionSource": "AJO"
}

您也可以將reward.kvpCustom用於固定特定獎勵型別(而非每個挑戰)的常數。

使用工作累計器資料

任務累計器會儲存每個合格事件的記錄。 使用item_list[-1]存取最近套用的專案 — 其transactionId和timestamp對於提供者端的稽核追蹤和重複資料刪除很有用。

code language-jsonata
{
  "memberId":       challenge.profileId,
  "points":         $number(rewardContext.rewardValue),
  "transactionId":  task.accumulators.item_list[-1].transactionId,
  "transactionDate": task.accumulators.item_list[-1].timestamp
}
建構文字訊息

對於通知型提供者(Slack、行動訊息、電子郵件),您可以使用JSONata的&串連運運算元直接建置訊息字串:

code language-jsonata
{
  "text": "You just earned " & rewardContext.rewardValue & " " & reward.denomination & "!"
}

輸出:

code language-json
{
  "text": "You just earned 50 Stars!"
}

範例

範例1 — 簡單點提供者

案例:​基本熟客點數API需要會員ID與點數。

獎勵定義:

code language-json
{
  "name":         "Standard Points",
  "denomination": "Points",
  "desc":         "Award loyalty points",
  "enabled":      true,
  "rewardJsonata": "{\"memberId\": challenge.profileId, \"pointQuantity\": $number(rewardContext.rewardValue), \"denomination\": reward.denomination}"
}

格式化運算式:

code language-jsonata
{
  "memberId":      challenge.profileId,
  "pointQuantity": $number(rewardContext.rewardValue),
  "denomination":  reward.denomination
}

裝載張貼至提供者:

code language-json
{
  "memberId":      "ADB-0000030",
  "pointQuantity": 50,
  "denomination":  "Points"
}
範例2 — 包含行銷活動中繼資料的提供者裝載

案例:​提供者需要包含稽核欄位、行銷活動參照和成員說明的結構化獎勵記錄。 行銷活動特定值儲存在challenge.kvpCustom中,因此相同的獎勵定義可在行銷活動間運作,而不需編輯運算式。

挑戰kvpCustom (在編寫挑戰時設定):

code language-json
{
  "parentCampaignId": "CAMP-2026-Q1",
  "productName":      "Loyalty Program"
}

獎勵定義:

code language-json
{
  "name":         "Stars — Campaign Award",
  "denomination": "Stars",
  "desc":         "Issue Stars for completing a qualifying purchase",
  "enabled":      true,
  "rewardJsonata": "{\"awardPoints\":[{\"idType\":\"externalId\",\"id\":challenge.profileId,\"transactionId\":task.accumulators.item_list[-1].transactionId,\"transactionDate\":task.accumulators.item_list[-1].timestamp,\"originalTransactionId\":task.accumulators.item_list[-1].transactionId,\"transactionSource\":\"AJO\",\"channelSource\":\"Web\",\"parentCampaignId\":challenge.kvpCustom.parentCampaignId,\"productName\":challenge.kvpCustom.productName,\"memberAwardDescription\":reward.desc,\"pointQuantity\":$number(rewardContext.rewardValue)}]}"
}

格式化運算式:

code language-jsonata
{
  "awardPoints": [
    {
      "idType":                "externalId",
      "id":                    challenge.profileId,
      "transactionId":         task.accumulators.item_list[-1].transactionId,
      "transactionDate":       task.accumulators.item_list[-1].timestamp,
      "originalTransactionId": task.accumulators.item_list[-1].transactionId,
      "transactionSource":     "AJO",
      "channelSource":         "Web",
      "parentCampaignId":      challenge.kvpCustom.parentCampaignId,
      "productName":           challenge.kvpCustom.productName,
      "memberAwardDescription": reward.desc,
      "pointQuantity":         $number(rewardContext.rewardValue)
    }
  ]
}

裝載張貼至提供者:

code language-json
{
  "awardPoints": [
    {
      "idType":                "externalId",
      "id":                    "ADB-0000030",
      "transactionId":         "b4fa0e89-f4bb-41ce-b370-fb97f9c52f1a",
      "transactionDate":       "2026-02-08T00:12:00.000+00:00",
      "originalTransactionId": "b4fa0e89-f4bb-41ce-b370-fb97f9c52f1a",
      "transactionSource":     "AJO",
      "channelSource":         "Web",
      "parentCampaignId":      "CAMP-2026-Q1",
      "productName":           "Loyalty Program",
      "memberAwardDescription": "Issue Stars for completing a qualifying purchase",
      "pointQuantity":         50
    }
  ]
}
範例3 — 里程碑獎勵

案例: Streak挑戰會每隔N次造訪發出里程碑獎勵。 運算式包含里程碑計數以及提供者端內容的目前連結。

格式化運算式:

code language-jsonata
{
  "memberId":       challenge.profileId,
  "points":         $number(rewardContext.rewardValue),
  "milestoneCount": milestone.count,
  "currentStreak":  task.schedule.currentStreak,
  "denomination":   reward.denomination,
  "source":         rewardContext.source
}

裝載已發佈至提供者 (在第2次造訪里程碑時):

code language-json
{
  "memberId":       "ADB-0000030",
  "points":         20,
  "milestoneCount": 2,
  "currentStreak":  2,
  "denomination":   "Stars",
  "source":         "milestone"
}

當rewardContext.source為"milestone"時,milestone物件會填入count和reward.rewardValue。 當來源是"task"或"challenge"時,milestone是null。

API 參考

獎勵提供者
code language-http
POST   /loyalty/metadata/config/rewards/providers
GET    /loyalty/metadata/config/rewards/providers
GET    /loyalty/metadata/config/rewards/providers/{providerId}
PUT    /loyalty/metadata/config/rewards/providers/{providerId}
DELETE /loyalty/metadata/config/rewards/providers/{providerId}

所有要求都需要x-gw-ims-org-id和x-sandbox-name標頭。

建立提供者:

code language-http
POST /loyalty/metadata/config/rewards/providers
x-gw-ims-org-id: {ORG_ID}
x-sandbox-name: {SANDBOX}
Content-Type: application/json

{
  "name":    "My Points Provider",
  "desc":    "Issues loyalty points via REST",
  "enabled": true,
  "url":     "https://rewards.example.com/award",
  "additionalHeaders": {
    "x-api-key": "YOUR_API_KEY"
  }
}
獎勵定義
code language-http
POST   /loyalty/metadata/config/rewards/definitions/{providerId}
GET    /loyalty/metadata/config/rewards/definitions/{providerId}
GET    /loyalty/metadata/config/rewards/definitions/{providerId}/{rewardId}
PUT    /loyalty/metadata/config/rewards/definitions/{providerId}/{rewardId}
DELETE /loyalty/metadata/config/rewards/definitions/{providerId}/{rewardId}

建立獎勵定義:

code language-http
POST /loyalty/metadata/config/rewards/definitions/{providerId}
x-gw-ims-org-id: {ORG_ID}
x-sandbox-name: {SANDBOX}
Content-Type: application/json

{
  "name":         "50 Stars",
  "denomination": "Stars",
  "desc":         "Award 50 Stars on task completion",
  "enabled":      true,
  "rewardJsonata": "{ \"memberId\": challenge.profileId, \"points\": $number(rewardContext.rewardValue) }"
}

運算式驗證

rewardJsonata運算式已在發佈時針對語法進行驗證。 如果運算式無效,API會傳回422錯誤,其中包含剖析失敗的描述。

若要在發佈之前開發和測試運算式,請使用JSONata Exerciser。 將獎勵內容JSON貼上為輸入檔案和您的運算式,以驗證輸出符合您的提供者期望。 每個觸發程式型別(task、milestone、challenge)的代表性獎勵內容會顯示在上面的範例中。

常見錯誤

錯誤
效果
修正
rewardContext.rewardValue已用為數字但未轉換
如果提供者驗證欄位為數值,則型別不符
以$number(rewardContext.rewardValue)換行
challenge.kvpCustom.someKey傳回null
編寫時挑戰未設定金鑰
確保在使用這個定義的每個挑戰上kvpCustom中都存在金鑰
task.accumulators.item_list[-1]為null
未套用任何專案後才簽發獎勵(非購買事件)
以條件式保全,或改用內容中的timestamp
milestone在來源為"task"或"challenge"時存取
milestone為空;運算式擲回或產生null欄位
存取milestone前請先檢查rewardContext.source,或僅在附加至里程碑獎勵的定義中使用milestone
運算式傳回陣列而非物件
提供者接收未預期的裝載結構
在外部物件中換行傳回陣列的運算式: { "items": [...] }

作法影片 video

➡️觀看如何設定忠誠獎勵提供者

AI Knowledge Reference

This section contains structured knowledge intended to support interpretation, retrieval, and question answering related to this topic.

For complete understanding, this information should be combined with the documentation on this page. Neither source is intended to stand alone; the page describes the feature, while this section provides additional context that helps disambiguate terminology, intent, applicability, and constraints.

  • TL;DR: This page explains how to configure reward providers and reward definitions for Loyalty Challenges, including the fields involved, the reward context passed to the rewardJsonata expression, and how to write that expression to build the payload posted to a reward provider’s endpoint.

Intents:

  • Configure a reward provider (endpoint, authentication, headers) and one or more reward definitions under it.
  • Understand the fields available on reward providers and reward definitions, including which are required.
  • Write a rewardJsonata expression that maps reward context fields onto the payload a provider expects.
  • Use kvpCustom on a challenge to pass campaign-specific or provider-specific metadata without editing the expression.
  • Use task accumulator data, such as the most recent item in item_list, inside an expression.
  • Distinguish task, milestone, and challenge reward sources and access the milestone object correctly.
  • Validate a rewardJsonata expression before publishing and troubleshoot common mistakes.

Glossary:

  • Reward Provider (product-specific): represents a single external rewards system; holds the delivery endpoint URL, authentication, and custom HTTP headers.
  • Reward Definition (product-specific): describes a distinct reward type or denomination offered by a provider, including its rewardJsonata expression.
  • rewardJsonata (product-specific): a JSONata expression configured on a reward definition that is evaluated at reward-issue time and must return the JSON payload to POST to the provider.
  • Reward context (product-specific): the single root object passed to rewardJsonata at evaluation time, containing rewardContext, reward, task, milestone, challenge, and timestamp.
  • kvpCustom (product-specific): custom key-value pairs configured on a challenge (challenge.kvpCustom) or available as constants on a reward (reward.kvpCustom), used to pass campaign IDs, product names, or provider-specific metadata.
  • isDefault (product-specific): marks a reward definition as the sandbox-wide default, used to auto-populate reward details on personalized challenges at publish time.
  • item_list (product-specific): the list of all qualifying items applied to a task’s accumulators; item_list[-1] accesses the most recently applied item via JSONata negative indexing.

Guardrails:

  • name and url are required fields on a reward provider; guid is system-assigned and read-only.
  • name and rewardJsonata are required fields on a reward definition; guid is system-assigned and read-only.
  • When a reward provider’s enabled field is false, reward delivery is suspended for all definitions under that provider.
  • When a reward definition’s enabled field is false, that definition is inactive and will not issue rewards.
  • Only one reward definition across all providers may be marked as default (isDefault) at a time; setting a new default clears the previous one.
  • maxRatePerSecond on a reward provider accepts a value from 1 to 5000 (per provider); null means unlimited.
  • rewardContext.rewardValue is always a string; it must be converted with $number() if the provider expects a numeric value.
  • milestone is null when the reward source is "task" or "challenge"; it is populated (with count and reward.rewardValue) only when rewardContext.source is "milestone".
  • rewardJsonata expressions are validated for syntax at publish time; an invalid expression causes the API to return a 422 error with a description of the parse failure.
  • challenge.kvpCustom.someKey returns null if the key was not set on the challenge at authoring time.
  • task.accumulators.item_list[-1] is null if no items were applied before the reward was issued (a non-purchase event).
  • If a rewardJsonata expression returns an array instead of an object, the provider receives an unexpected payload structure; it must be wrapped in an outer object.

Terminology:

  • Canonical name: Reward Provider — holds the endpoint, authentication, and headers for an external rewards system.
  • Canonical name: Reward Definition — describes a reward type or denomination and its rewardJsonata expression; belongs to one Reward Provider.
  • Do not confuse: “Reward Provider” (the external system’s endpoint, auth, and headers) ≠ “Reward Definition” (a specific reward type/denomination with its own rewardJsonata, nested under a provider).
  • Do not confuse: “challenge.kvpCustom” (per-challenge custom metadata) ≠ “reward.kvpCustom” (constants fixed for a given reward type).
  • Do not confuse: rewardContext.rewardValue (the reward value string that triggered issuance) ≠ milestone.reward.rewardValue (the reward value specific to a milestone).

FAQ:

  • Q: What triggers a reward to be issued? — When a challenge task, milestone, or challenge completes and has a reward value configured, the platform issues a reward by calling the reward provider’s HTTP endpoint with a JSON payload.
  • Q: What fields are required to create a reward provider? — name and url are required; guid is system-assigned and read-only.
  • Q: What fields are required to create a reward definition? — name and rewardJsonata are required; guid is system-assigned and read-only.
  • Q: How many reward definitions can be marked as default? — Only one definition across all providers may be default at a time; setting a new default clears the previous one.
  • Q: Why does rewardContext.rewardValue need conversion before use as a number? — Because rewardContext.rewardValue is always a string; use $number() to convert it if the provider expects a numeric value.
  • Q: Why would challenge.kvpCustom.someKey return null? — Because the key was not set on the challenge at authoring time; ensure the key is present in kvpCustom on every challenge that uses the definition.
  • Q: What happens if an invalid rewardJsonata expression is published? — The API returns a 422 error with a description of the parse failure.
  • Q: Where can I test a rewardJsonata expression before publishing? — In the JSONata Exerciser, by pasting the reward context JSON as the input document and the expression to verify the output.

Troubleshooting:

  • task.accumulators.item_list[-1] is null — Cause: no items were applied before the reward was issued (a non-purchase event). Fix: guard with a conditional or use timestamp from context instead.
  • milestone accessed when source is "task" or "challenge" — Cause: milestone is null in that case. Fix: check rewardContext.source before accessing milestone, or only use milestone in definitions attached to milestone rewards.
  • Expression returns an array instead of an object — Cause: the provider receives an unexpected payload structure. Fix: wrap array-returning expressions in an outer object, for example { "items": [...] }.
  • challenge.kvpCustom.someKey returns null — Cause: the key was not set on the challenge at authoring time. Fix: ensure the key is present in kvpCustom on every challenge that uses this definition.
  • 422 error — Cause: the rewardJsonata expression is invalid at publish time. Fix: the API returns a description of the parse failure; use the JSONata Exerciser to develop and test the expression before publishing.

Examples:

  • Simple fixed payload: maps challenge.profileId, $number(rewardContext.rewardValue), and reward.denomination into a memberId/points/currency object, producing {"memberId": "ADB-0000030", "points": 50, "currency": "Stars"}.
  • Using kvpCustom: an expression references challenge.kvpCustom.campaignId alongside challenge.profileId and rewardContext.rewardValue to keep the expression reusable across campaigns.
  • Using task accumulator data: an expression uses task.accumulators.item_list[-1].transactionId and .timestamp for audit trails and deduplication on the provider side.
  • Constructing a text message: an expression concatenates strings with &, for example "You just earned " & rewardContext.rewardValue & " " & reward.denomination & "!", producing {"text": "You just earned 50 Stars!"}.
  • Example 1 — Simple points provider: a Reward Definition with rewardJsonata mapping challenge.profileId, $number(rewardContext.rewardValue), and reward.denomination to memberId, pointQuantity, and denomination, producing a payload with pointQuantity: 50.
  • Example 2 — Provider payload with campaign metadata: a Reward Definition builds an awardPoints array using challenge.kvpCustom.parentCampaignId and challenge.kvpCustom.productName (set on the challenge) plus task accumulator transaction data, so the same definition works across campaigns without editing the expression.
  • Example 3 — Milestone reward: an expression includes milestone.count and task.schedule.currentStreak; when rewardContext.source is "milestone", the milestone object is populated with count and reward.rewardValue.

Personas:

  • admin: Can configure a reward provider with its endpoint URL, authentication, and additional headers, and create reward definitions under it. Can set maxRatePerSecond between 1 and 5000, or leave it null for unlimited. Can mark enabled to false on a provider or definition to suspend or stop reward delivery. The page states that only one reward definition across all providers may be isDefault at a time. Where: “Reward provider & definition fields”.

Onboarding:

  • Level: Intermediate
  • First minimal step: Paste the reward context JSON as the input document in the JSONata Exerciser and add your rewardJsonata expression.
  • Success check: The output matches what your provider expects.
recommendation-more-help
journey-optimizer-help