Update a Loyalty Promotion

Recent Requests
Log in to see full request history
TimeStatusUser Agent
Retrieving recent requests…
LoadingLoading…

Updates an existing loyalty promotion using its unique ID. This is a full-replacement PUT — you must include the complete promotion object in the request body, including any fields you want to preserve. Fields omitted from the request will be cleared or reset to defaults.

Use this endpoint to make changes such as adjusting the promotion name, extending the end date, updating enrolment rules, modifying activity milestones, changing reward limits, or updating liability cost splits.

Important constraints:

  • status cannot be changed via this endpoint — use the dedicated review/status-action endpoints.
  • startDate cannot be modified once the promotion is live (ACTIVE or LIVE).
  • The event field on a SINGLE activity cannot be changed after creation.
  • programId is immutable and cannot be changed after creation.

Example request

{
    "metadata": {
        "name": "Single Milestone Promotion",
        "description": "Single Milestone Promotion",
        "programId": 973,
        "startDate": "2026-02-03T12:28:00+05:30",
        "endDate": "2026-02-28T12:28:59+05:30",
        "promotionType": "GENERIC",
        "status": "DRAFT",
        "promoIdentifier": "single-milestone-promotion",
        "timezoneName": "Asia/Kolkata",
        "promotionMetadata": [],
        "lastModifiedBy": 75139931
    },
    "customerEnrolment": {
        "enrolmentMethod": "TRANSACTION"
    },
    "liabilityOwnerSplitInfo": [],
    "activities": [
        {
            "id": "activity_1770015539223",
            "name": "Activity 1",
            "type": "SINGLE",
            "event": "TransactionAdd",
            "milestones": [
                {
                    "name": "Single Milestone Promotion~Activity 1~Milestone 1770015543",
                    "trackingType": "DEFAULT",
                    "targetEvaluationType": "FIXED_CALENDAR_WINDOW",
                    "frequencyType": "WEEKLY",
                    "sameActionsForEveryCycle": true,
                    "differentTargetsForEveryCycle": false,
                    "leaderboardEnabled": false,
                    "preferredTillId": 75216507,
                    "targetEntity": "TRANSACTION",
                    "targetType": "COUNT",
                    "cycleActionMapping": [
                        {
                            "cycle": "Cycle_1",
                            "startDate": "2026-02-03T12:28:00+05:30",
                            "endDate": "2026-02-09T23:59:59+05:30",
                            "defaultValue": "3",
                            "actions": []
                        },
                        {
                            "cycle": "Cycle_2",
                            "startDate": "2026-02-10T00:00:00+05:30",
                            "endDate": "2026-02-16T23:59:59+05:30",
                            "defaultValue": "3",
                            "actions": []
                        },
                        {
                            "cycle": "Cycle_3",
                            "startDate": "2026-02-17T00:00:00+05:30",
                            "endDate": "2026-02-23T23:59:59+05:30",
                            "defaultValue": "3",
                            "actions": []
                        },
                        {
                            "cycle": "Cycle_4",
                            "startDate": "2026-02-24T00:00:00+05:30",
                            "endDate": "2026-02-28T12:28:59+05:30",
                            "defaultValue": "3",
                            "actions": []
                        }
                    ]
                }
            ],
            "commonCycleActionMapping": [],
            "expJSON": "{\n  \"arity\": \"binary_operation\",\n  \"type\": \"boolean:primitive\",\n  \"value\": \">\",\n  \"operands\": [\n    {\n      \"arity\": \"object_dereference\",\n      \"type\": \"real:object:primitive\",\n      \"operands\": [\n        {\n          \"arity\": \"name\",\n          \"type\": \"tx:object:primitive\",\n          \"value\": \"currentTxn\"\n        },\n        {\n          \"arity\": \"name\",\n          \"type\": \"real:object:primitive\",\n          \"value\": \"value\"\n        }\n      ]\n    },\n    {\n      \"arity\": \"literal\",\n      \"type\": \"number:primitive\",\n      \"value\": \"1000\"\n    }\n  ]\n}"
        }
    ],
    "id": "69804b71d90f137246ab7202",
    "workflowMetadata": {
        "optin": {},
        "enrolment": {}
    }
}

API Quick Reference

{{updateUnifiedPromotion}}
   ├─ {{metadata}}
   │   ├─ {{name}} (string)
   │   ├─ {{description}} (string)
   │   ├─ {{orgId}} (integer)
   │   ├─ {{programId}} (integer)
   │   ├─ {{startDate}} (string)
   │   ├─ {{endDate}} (string)
   │   ├─ {{promotionType}} (string)
   │   ├─ {{status}} (string)
   │   ├─ {{timezoneName}} (string)
   │   └─ {{promoIdentifier}} (string)
   ├─ {{customerEnrolment}}
   │   ├─ {{enrolmentMethod}} (string)
   │   └─ {{audienceGroups}} []
   ├─ {{activities}} []
   │   ├─ {{activityId}} (string)
   │   ├─ {{activityType}} (string)
   │   ├─ {{activityName}} (string)
   │   ├─ {{event}} (string)
   │   ├─ {{expJSON}} (string)
   │   └─ {{commonCycleActionMapping}} []
   │       ├─ {{cycle}} (string)
   │       ├─ {{cycleStartDate}} (string)
   │       ├─ {{cycleEndDate}} (string)
   │       └─ {{actions}} []
   │           ├─ {{actionId}} (string)
   │           ├─ {{actionName}} (string)
   │           ├─ {{actionClass}} (string)
   │           ├─ {{actionDescription}} (string)
   │           └─ {{mandatoryPropertiesValues}}
   │               ├─ {{AwardStrategy}} (string)
   │               ├─ {{ExpiryStrategy}} (string)
   │               └─ {{PointType}} (string)
   ├─ {{limits}} []
   │   ├─ {{granularity}} (string)
   │   ├─ {{limitActionType}} (string)
   │   ├─ {{actionSubTypeId}} (string)
   │   ├─ {{limitType}} (string)
   │   ├─ {{limitValue}} (integer)
   │   └─ {{period}}
   │       ├─ {{periodType}} (string)
   │       ├─ {{periodUnit}} (string)
   │       └─ {{periodValue}} (integer)
   ├─ {{liabilityOwnerSplitInfo}} []
   │   ├─ {{liabilityOwnerId}} (integer)
   │   ├─ {{componentType}} (string)
   │   ├─ {{ratio}} (integer)
   │   ├─ {{isActive}} (boolean)
   │   ├─ {{liabilityOwnerName}} (string)
   │   ├─ {{liabilityOwnerType}} (string)
   │   └─ {{liabilityOrgId}} (integer)
   └─ {{workflowMetadata}}
       ├─ {{optin}} (object)
       └─ {{enrolment}} (object)

Prerequisites

  • Authentication: Basic or OAuth authentication.
  • Default access group

Body parameters

FieldTypeRequiredDescription
metadataObjectYesContains all core promotion settings. See the metadata Object table below.
customerEnrolmentObjectYesDefines who is initially eligible. See the Member Enrolment & Workflows table below.
activitiesArray of ObjectYesDefines the triggers and rewards. See the activities Object table below.
workflowMetadataObjectOptionalDefines dynamic opt-in or enrolment rules. See the workflowMetadata table below.
limitsArray of ObjectOptionalSets usage limits. See the limits Object table below.
liabilityOwnerSplitInfoArray of ObjectOptionalDefines cost splitting within programs and partner programs. See the LiabilityOwnerSplitInfo Object table below.
commentsstringOptionalInternal comments for the promotion. Example: "Initial draft for review"

Metadata object table

Specifies the essential configuration details for the promotion. This object holds the core identifiers and timing of a promotion, such as the name of the promotion, the duration of the promotion, and the promotion type, among others.

FieldTypeRequiredDescription
namestringYesSpecifies the unique name of the promotion, used for identification. Max length: 200. Example: "Q3 Bonus Points for Gold Tier"
orgIdintegerYesIndicates the unique organization identifier the promotion belongs to. This ensures the promotion runs under the correct business account.
startDatestringYesSpecifies the promotion's start date. The startDate cannot be changed once the promotion is active. Time format: ISO 8601 timestamp with timezone offset. Example: 2025-12-16T14:30:45+05:30
endDatestringYesSpecifies the promotion's end date. Time format: ISO 8601 timestamp with timezone offset. Example: 2025-12-31T23:59:59+05:30
promotionTypestringYesIndicates the nature of the reward or promotion type, used for categorisation.
Supported values:
LOYALTY_EARNING: Use this promotion type when the primary reward is awarding loyalty points or currency to the member.
GENERIC: Use this promotion type for any non-point reward, such as issuing a voucher, awarding a badge, or upgrading a member's tier.
statusenumYesInclude the current promotion status in the request body. Status transitions cannot be made via this endpoint — use the dedicated review/status-action endpoints for state changes.
Supported values: DRAFT, ACTIVE, PENDING_APPROVAL, LIVE, UPCOMING.
descriptionstringOptionalIndicates a brief description of the promotion's purpose for internal reference. Example: "Standard weekend points multiplier promotion".
programIdstringOptionalSpecifies the loyalty program ID to which the promotion belongs. To retrieve the programId, use the Get Loyalty Programs API.
timezoneNamestringOptionalSpecifies the timezone name for the promotion's schedule. This is a reference label and is not validated. Supported values: Valid IANA Time Zone Database name. Example: "Asia/Kolkata" or "America/New_York"
promoIdentifierstringOptionalSpecifies the unique string identifier for the promotion. This value is a permanent unique key per org and cannot be changed to match another promotion's identifier. Max length: 255 characters.
promotionMetadataArray (Object)OptionalDefines a list of custom key-value pairs for additional metadata, often for reporting or integration purposes.
Supported Values: Array of PromotionMetadata objects. Example: [ { "key": "CampaignCode", "value": "FALL25" } ]
See the promotionMetadata Object table below for the full structure.

promotionMetadata object

This object captures optional metadata for internal promotion tracking. It lets you add a key–value pair to label or identify the promotion.

FieldTypeRequiredDescription
isBrandDefinedstringOptionalIndicates if the metadata key is custom (true) or system-defined (false). True indicates that the metadata key is user-defined and not internally generated.
keystringOptionalSpecifies the custom metadata key name. This key is used for tracking internal promotions. Example: "CampaignCode"
valuestringOptionalSpecifies the metadata key value. This value is used for the specific code for the promotion. Example: "FALL25"

Example use case 1: applying the metadata object to update a promotion

StyleSavvy Boutique previously created a draft promotion named "StyleSavvy VIP Early Access-lock". Now they need to update the status from DRAFT to ACTIVE to launch the promotion. Additionally, they want to modify the internal description to explicitly state that this promotion acts as a "Liability Lock" to prevent confusion during reporting.

curl --location --request PUT 'https://eu.api.capillarytech.com/v3/promotions/693bba55147aa26388780ffd' \
--header 'Content-Type: application/json' \
--header 'X-CAP-API-AUTH-ORG-ID: 100232' \
--header 'Authorization: Basic Z2VvcmdlLmRvY2RlbW86NjVhMDgzYjk1MGY1NTY5NDk1YmNkNzUxYmJiY2U=' \
--header 'Cookie: _cfuvid=bw96IuxgkLuZy6CYqoMPzwtPCXTg6pMsX5zEo-1765521791434-0.0.1.1-604800000' \
--data '{
  "metadata": {
    "name": "StyleSavvy VIP Early Access-lock",
    "description": "Standard liability - 100% to main program (973).",
    "orgId": 100737,
    "programId": 973,
    "startDate": "2025-11-20T00:00:00+05:30",
    "endDate": "2025-12-31T23:59:59+05:30",
    "promotionType": "LOYALTY_EARNING",
    "status": "DRAFT",
    "timezoneName": "Asia/Kolkata",
    "promoIdentifier": "stylesavvy-vip-early-access-lock",
    "promotionMetadata": []
  },
  "customerEnrolment": {
    "enrolmentMethod": "TRANSACTION",
    "audienceGroups": []
  },
  "activities": [
    {
      "id": "activity_1761xxxxxxb1",
      "type": "SINGLE",
      "name": "Award Points",
      "event": "TransactionAdd",
      "expJSON": "{\n  \"arity\": \"literal\",\n  \"type\": \"boolean:primitive\",\n  \"value\": \"true\"\n}",
      "commonCycleActionMapping": [
        {
          "cycle": "Cycle_1",
          "startDate": "2025-11-20T00:00:00+05:30",
          "endDate": "2025-12-31T23:59:59+05:30",
          "actions": [
            {
              "id": "temp_action_pts_split",
              "actionName": "AWARD_POINTS_ACTION",
              "actionClass": "com.capillary.shopbook.pointsengine.endpoint.impl.action.AwardPointsActionImpl",
              "description": "Award Points with 100% Liability",
              "mandatoryPropertiesValues": {
                "AwardStrategy": "109010",
                "ExpiryStrategy": "109436",
                "PointType": "Main"
              },
              "embeddedStrategies": []
            }
          ]
        }
      ],
      "milestones": []
    }
  ],
  "limits": [],
  "liabilityOwnerSplitInfo": [],
  "workflowMetadata": {
    "optin": {},
    "enrolment": {}
  }
}'

Example use case 2: applying the metadata object to update a promotion

Adventure Quest Outfitters previously drafted a promotion called the "Gear Up Challenge". Now they need to update the status from DRAFT to ACTIVE to officially launch the promotion. Additionally, they have decided to extend the promotion duration by one week, changing the end date from December 31st to January 7th, 2026, to allow for post-holiday participation.

curl --location --request PUT 'https://eu.api.capillarytech.com/v3/promotions/{promotionid}' \
--header 'Content-Type: application/json' \
--header 'X-CAP-API-AUTH-ORG-ID: 100232' \
--header 'Authorization: Basic Z2VvcmdlLmRvY2RlbW86NjVhMDgzYjk1MWY5MGY1NTk1YmNkNzUxYmJiY2U=' \
--header 'Cookie: _cfuvid=bw96IuxgkLuZy69HNs9OCYtPCXTg6pMsX5zEo-1765521791434-0.0.1.1-604800000' \
--data '{
  "metadata": {
    "name": "Adventure Quest Gear Up2",
    "description": "Generic promo for Gear Up Challenge. Issue and Earn. EXTENDED to Jan 7.",
    "orgId": 100737,
    "programId": 973,
    "startDate": "2025-12-01T00:00:00+05:30",
    "endDate": "2026-01-07T23:59:59+05:30",
    "promotionType": "GENERIC",
    "status": "DRAFT",
    "timezoneName": "Asia/Kolkata",
    "promoIdentifier": "aq-gearup-dec25-ui",
    "promotionMetadata": []
  },
  "customerEnrolment": {
    "enrolmentMethod": "TRANSACTION",
    "audienceGroups": []
  },
  "activities": [
    {
      "id": "activity_1761xxxxxx02",
      "type": "SINGLE",
      "name": "Challenge Activity",
      "event": "CustomEvent",
      "expJSON": "{\n  \"arity\": \"literal\",\n  \"type\": \"boolean:primitive\",\n  \"value\": \"true\"\n}",
      "commonCycleActionMapping": [
        {
          "cycle": "Cycle_1",
          "startDate": "2025-12-01T00:00:00+05:30",
          "endDate": "2026-01-07T23:59:59+05:30",
          "actions": [
            {
              "id": "temp_action_badge",
              "actionName": "AWARD_BADGE",
              "actionClass": "com.capillary.shopbook.pointsengine.endpoint.impl.action.AwardBadgeActionImpl",
              "description": "Award Gear Up Badge",
              "mandatoryPropertiesValues": {
                "badgeId": "GEAR_UP_DEC25"
              },
              "mandatoryComplexPropertiesValues": {},
              "embeddedStrategies": []
            }
          ]
        }
      ],
      "milestones": []
    }
  ],
  "limits": [],
  "liabilityOwnerSplitInfo": [],
  "workflowMetadata": {
    "optin": {},
    "enrolment": {}
  }
}'

Member enrolment and workflows object table

Specifies the configuration for how members enrol into the promotion and whom to target.

customerEnrolment object table

It specifies how members first enrol, whether they join automatically via transactions, through a manual list import, or based on existing audience segments.

FieldTypeRequiredDescription
enrolmentMethodenumOptionalDefines the primary enrolment type. TRANSACTION: Auto-enrols the member when any purchase is made.
Example: A cafe's "December Holiday Bonus" promo. Any member who makes purchases during December is automatically enrolled and starts earning.
IMPORT: Manually enrols a specific list.
Example: A brand wants to reward 50 specific contest winners. The marketer uploads a CSV file containing those 50 member IDs, and only those 50 people will be enrolled.
audienceGroupsArray (Object)ConditionalList of audience segments. For audience-based enrolment, configure workflowMetadata.enrolment.basedOn = AUDIENCE with enrolmentMethod = IMPORT.
..audienceGroupIdintegerYesThe ID of the target audience group. Example: 12345
..audienceGroupNamestringOptionalInformational name of the audience group. Example: "Gold Tier"
..descriptionstringOptionalInformational description of the audience group. Example: "High spending members"

workflowMetadata object

Defines the rules that determine how members can join or opt in to the loyalty promotion, whether through automatic, system-driven enrolment or through member-initiated participation.

FieldTypeRequiredDescription
enrolmentObjectOptionalSpecifies rules for automatic dynamic enrolment.
See the enrolment and optin Object table below.
optinObjectOptionalSpecifies rules for member-managed opt-in.
See the enrolment and optin Object table below.

enrolment and optin object

Defines the specific conditions that trigger a member’s enrolment or opt-in, such as performing a qualifying activity or entering a target audience segment, along with any associated mappings or restrictions that apply to the workflow

FieldTypeRequiredDescription
basedOnenumOptionalSpecifies the condition type that triggers the member’s enrolment or opt-in.
ACTIVITY: Enrolment is triggered when the member performs a defined action.
AUDIENCE: Enrolment is triggered when the member enters a specified audience segment.
activitiesArray (Object)ConditionalA list of [Activity Objects ] that trigger the enrolment or opt-in. Required if basedOn is ACTIVITY.
audienceMappingArray (Object)ConditionalList of audience groups that trigger the enrolment or opt-in. Required if basedOn is AUDIENCE.
..groupIdintegerOptionalThe ID of the audience group. Example: 12345
..groupNamestringOptionalInformational name of the audience group. Example: "Gold Tier"
optInStartDatestringConditionalStart of the opt-in window, in ISO 8601 format with the region offset. For example: 2025-12-16T14:30:45+05:30. Applies to the optin block on enrol and opt-in promotions (promotionType: LOYALTY_EARNING) when the CONF_ENROLLMENT_OPTIN_DURATION_ENABLED org flag is enabled. Whether this date can be changed depends on the promotion status. See Editing the opt-in window.
optInEndDatestringConditionalEnd of the opt-in window, in ISO 8601 format with the region offset. For example: 2025-12-16T14:30:45+05:30. Must be after optInStartDate and on or before the promotion endDate. Whether this date can be changed depends on the promotion status. See Editing the opt-in window.
restrictionsObjectOptionalApplies limits specifically to this workflow. See the Promotion Restrictions Object table below.

Editing the opt-in window

Whether you can change optInStartDate and optInEndDate depends on the promotion's status when you send the update. The opt-in window opens at optInStartDate.

Promotion statusoptInStartDateoptInEndDate
DraftEditable, including past dates.Editable, including past dates.
Upcoming, before the window opensEditable. Cannot be set to a past date.Editable. Cannot be set to a past date.
Upcoming, after the window opensLocked.Extend only. Cannot be shortened.
Live or pausedLocked.Extend only. Cannot be shortened or set to a past date.
EndedLocked.Extend only. Cannot be shortened.
Stopped or pending approvalNo edits allowed.No edits allowed.

Note: A promotion that enrols members through an activity or an external API call does not support an opt-in window. Sending optInStartDate or optInEndDate for these enrolment types returns error 310228.

The following errors apply when you change the opt-in window. Every entry returns HTTP 400.

CodeDescription
310224Opt-in start date cannot be changed after the opt-in window has opened.
310225Opt-in end date cannot be shortened, it can only be extended.
310226Opt-in start date cannot be set to a past date during edit.
310227Opt-in end date cannot be set to a past date during edit.
310228Opt-in window is not supported when a member is enrolled via an activity or an external API call.

restrictions object

Specifies the limits on how long an opt-in status lasts, how many times a member can join, and the maximum number of times they can earn rewards through that specific workflow.

FieldTypeRequiredDescription
optinExpiryBasedOnObjectOptionalDefines expiry for the user's opt-in status.
..typeenumOptionalSpecifies the expiry method for the loyalty promotion enrolment. Supported Values: PROMOTION: The loyalty promotion enrolment expires with the promotion end date.
Example: A “Weekend Rewards Boost” promotion runs from Friday 12:00 AM to Sunday 11:59 PM. If a member opts in on Friday afternoon, their enrolment stays active only until the promotion closes on Sunday night, regardless of when they joined.
CUSTOM: Expires after a personal timer.
Example: A retailer offers a “7-Day Bonus Points Trial.” If a member opts in on January 10th, their trial remains active until January 17th. Another member who opts in on January 14th gets their own 7-day window, expiring on January 21st. Each participant’s expiry is calculated individually.
FIXED_DATE: Expires on a specific calendar date for everyone.
Example: A brand runs a “Holiday Mega Contest” that allows opt-ins throughout the year, but all entries expire on December 31st. Whether a member joins in March or December 30th, their enrolment ends for everyone at 11:59 PM on December 31st.
..valueintegerOptionalDuration for which the opt-in lasts. Example: 30, which defines the opt-in expiry as 30 days. Required if type is CUSTOM
..expiryDatestringOptionalTime format: ISO 8601 timestamp with timezone offset. Example: 2025-12-16T14:30:45+05:30 Required if type is FIXED_DATE.
enrolmentExpiryBasedOnObjectOptionalDefines expiry for the user's enrolment status.
..typeenumOptionalSpecifies the expiry method for the loyalty promotion enrolment. Supported Values: PROMOTION: The loyalty promotion enrolment expires with the promotion end date.
Example: A “Weekend Sale” loyalty promotion runs from Friday to Sunday. If a member opts in on Friday, their enrolment remains active only until the promotion ends on Sunday night, regardless of when they joined.
CUSTOM: Expires after a personal timer.
Example: A brand offers a “7-Day Free Trial.” loyalty promotion where a member opts in on Tuesday, their trial lasts exactly 7 days and expires the following Tuesday. If their friend opts in on Friday, that friend’s trial runs for 7 days and expires the next Friday. Each member’s expiry is based on their individual opt-in date.
FIXED_DATE: Expires on a specific calendar date for everyone.
Example: A “Summer Contest” loyalty promotion allows members to opt in at any time, but all enrolments expire on December 31st. Whether someone opts in on July 1st or December 30th, their enrolment still ends at 11:59 PM on December 31st.
..valueintegerOptionalSpecifies the duration during which the enrolment will be active. Example: 7 Required if type is CUSTOM
..expiryDatestringOptionalTime format: ISO 8601 timestamp with timezone offset. Example: 2025-12-16T14:30:45+05:30 Required if type is FIXED_DATE.
optinLimitPerCustomerObjectOptionalLimits how many times a member can opt-in.
..typeenumOptionalDefines how the limit is set
Supported values:
NON_PERIOD_BASED: The limit applies once for the entire promotion. It never resets. Example: Limit 1 free gift per member.” Once a member redeems the gift, they cannot receive another, ever, for this promotion.
PERIOD_BASED: The limit resets after a set timeframe daily or weekly, allowing the member to qualify again. Example: “Limit 1 free coffee per day.” A member can redeem one coffee on Monday, another on Tuesday, another on Wednesday, and so on, the limit refreshes at the start of each day.
..valueintegerOptionalThe maximum number of times a member can opt-in. Example: 1
..periodTypeenumConditionalDefines the window type for evaluating period-based limits. Supported value:MOVING_WINDOW: The limit is evaluated over a rolling timeframe that counts backward from the current moment, rather than following calendar boundaries. Example: If the rule is “1 redemption per 7 days” and a member redeems an offer on Wednesday at 3 PM, they cannot redeem again until the next Wednesday at 3 PM. The window moves with each redemption, it is not tied to a fixed calendar week. Required if type is PERIOD_BASED.
..periodUnitenumConditionalSpecifies the interval at which the limit resets. Supported Values:
DAILY: The limit resets every day. Example: “Get a free coffee with any purchase, limit 1 per day.” A member can redeem one on Monday, another on Tuesday, another on Wednesday, and so on, because the limit refreshes at the start of each new day.
WEEKLY: The limit resets every week. Example: “Earn 100 bonus points on your next purchase, limit 1 per week.” If a member earns the bonus on Tuesday, they must wait until the start of the next calendar week before they can earn it again.
MONTHLY: The limit resets every month. Example: “Get 20% off one bill, limit 1 per month.” A member can redeem it once in October, once in November, once in December, and so on—because the limit resets at the beginning of each month.
maxRedemptionsPerEarnPerCustomerObjectOptionalSpecifies the maximum number of times a member can redeem rewards that they earn after joining the promotion through this specific workflow.
..typeenumOptionalDefines if the redemption limit resets based on a set period.
NON_PERIOD_BASED: The redemption limit applies once for the entire promotion and never resets.
Example: “Limit 1 free welcome gift per member.” Once a member redeems the gift, they cannot receive another, even if they meet the eligibility criteria again later.
PERIOD_BASED: The redemption limit resets on a regular schedule (like daily or weekly), allowing the member to qualify repeatedly.
Example: “Limit 1 free coffee per day.” A member can redeem one on Monday, the limit resets at the start of the next day, and they can redeem again on Tuesday.
..valueintegerOptionalThe maximum number of redemptions that the member can make within a specific period of time.
..periodTypeenumConditionalDefines the type of period at which the redemption limit resets. Supported value: MOVING_WINDOW. Required if type is PERIOD_BASED.
..periodUnitenumConditionalSpecifies the interval at which the member can redeem rewards. Supported Values:
DAILY: The redemption limit resets every day. Example: “Get a free coffee with any purchase, limit 1 per day.” A member can redeem one on Monday, another on Tuesday, another on Wednesday, and so on, the limit refreshes at the start of each new day.
WEEKLY: The redemption limit resets every week. Example: “Earn 100 bonus points on your next purchase, limit 1 per week.” If a member earns the bonus on Tuesday, they cannot earn it again until the next week begins.
“Get 20% off one bill, limit 1 per month.” A member can redeem it once in October, once in November, once in December, and so on, because the limit resets with each new month.
Required if type is PERIOD_BASED.
enrolmentLimitPerPromotionObjectOptionalDefines the overall limit on total enrolments or opt-ins possible through this workflow.
..typeenumOptionalDefines if the enrolment limit per promotion resets based on set intervals.
NON_PERIOD_BASED: The limit applies once for the entire promotion and never resets.
Example: “Limit 1 free welcome gift per member.” Once a member redeems the gift, they cannot receive another, even if they meet the eligibility conditions again.
PERIOD_BASED: The limit resets on a regular schedule (like daily or weekly), allowing the member to qualify repeatedly.
Example: “Limit 1 free coffee per day.” A member can redeem one on Monday, the limit resets overnight, and they can redeem another on Tuesday.
..valueintegerOptionalSpecifies the maximum enrolment limit per promotion.
..periodTypeenumConditionalDefines how the overall limit per promotion is calculated.
MOVING_WINDOW: The limit is based on a rolling timeframe that counts backwards from the current moment, not a fixed calendar block.
Example: Suppose the global enrolment limit is 1000, the limit type is PERIOD_BASED, and the period is a 7-day MOVING_WINDOW. If 1000 members enrol between Monday (Day 1) and Wednesday (Day 3), the promotion reaches its limit and no additional enrolments are allowed. On the following Monday (Day 8), the enrolments from Day 1 fall outside the 7-day window, freeing up capacity and allowing new members to enrol again.
..periodUnitenumConditionalDefines the specific time an overall enrolment limit resets per promotion:
DAILY: The total enrolment cap resets every day.
Example: If the limit is "1,000 enrolments per Day," the system will allow a total of 1,000 members (from all members) to enrol on Monday. On Tuesday, the counter resets, and a new 1,000 total enrolments are allowed.
WEEKLY: The total enrolment cap resets every week.
Example: If the limit is "1,000 enrolments per Week," the system allows 1,000 total members to enrol between Sunday and Saturday. The counter then resets for the next week.
MONTHLY: The total enrolment cap resets every month.
Example: If the limit is "1,000 enrolments per Month," a total of 1,000 members can enrol during all of November. The counter then resets on December 1st. Required if type is PERIOD_BASED.
enrolmentLimitPerCustomerObjectOptionalLimits the number of times a member can enrol on the loyalty promotion via this workflow.
..typeenumOptionalDefines how the limit is applied to a single member:
NON_PERIOD_BASED: The limit applies once for the entire promotion. It never resets.
Example: A “Welcome Offer.” A member can enrol only once. Even if they leave the programme and return later, they cannot enrol in this promotion again.
PERIOD_BASED: The limit resets after a set timeframe (like daily or monthly).
Example: A “Monthly Pass” promotion with a limit of “1 enrolment per month.” A member can enrol in January, and once the monthly period resets, they can enrol again in February.
..valueintegerOptionalThe maximum number of times a member can enrol on this loyalty promotion. Example: 1
..periodTypeenumConditionalThis defines how the time-based limit is calculated for that specific member.
MOVING_WINDOW: The limit is based on a rolling timeframe that starts from the moment the member enrols. It is not tied to a fixed calendar (like "every Monday" or "the 1st of the month").
Example: If the limit is "1 enrolment per WEEKLY period" and a member enrols on a Tuesday, their 7-day "moving window" starts. They cannot enrol again until the following Tuesday.
..periodUnitenumConditionalDefines the specific time unit for the PERIOD_BASED limit.
Supported values:
DAILY: The limit for the member resets every day.
Example: A limit of "1 enrolment per Day." A member can enrol on Monday, and then the limit resets, allowing them to enrol again on Tuesday.
WEEKLY: The limit for the member resets every week.
Example: A limit of "1 enrolment per Week." A member enrols on Tuesday. They cannot enrol again until next week (how the week is defined, e.g., Sun-Sat or a rolling 7 days, depends on the periodType).
MONTHLY: The limit for the member resets every month.
Example: A limit of "1 enrolment per Month." A member enrols in November. They cannot enrol again until the limit resets in December.
maxPointsPerEarnPerCustomerObjectOptionalDefines the maximum points a member can earn from a single reward activity, after they have enrolled or opted in through this workflow.
..typeenumOptionalDefines how often the points limit is applied.
NON_PERIOD_BASED: The cap applies to every single reward activity, individually.
Example: Example: The limit per action is set to 500 points.
Monday: A member makes a purchase that earns 300 points — allowed (300 < 500).
Tuesday: The member makes another purchase worth 700 points — this single action is capped, so they receive 500 points.
Wednesday: The member makes a third purchase worth 400 points — allowed (400 < 500).
Each action is evaluated on its own; there is no daily or weekly total. The limit applies per action, not over time.
PERIOD_BASED: The cap applies to the total points earned over a set time period.
The limit is set to 500 points per day. Monday morning: A member earns 300 points — allowed.Monday afternoon: The member earns 400 points. The system checks the daily total (300 + 400 = 700). Since this exceeds the 500-point limit, the system awards only the remaining 200 points for this second action.Tuesday: The period resets, and the member can earn up to 500 points again.
-On Monday morning, you earn 300 points (OK).
-On Monday afternoon, you earn 400 points. The system checks your total for the day (300 + 400 = 700). Since this exceeds 500, it will only award you the remaining 200 points for this second action.
-On Tuesday, the limit resets, and you can earn up to 500 points again.
..valueintegerOptionalThe maximum points that can be earned. Example: 100
..periodTypeenumConditionalDefines how the time-based points limit is calculated.
MOVING_WINDOW: The limit is based on a rolling timeframe (e.g., "the last 7 days" or "the last 30 days") that counts backward from the exact moment of the transaction. It is not tied to a fixed calendar (like "this week" or "this month").
Example: The limit is set to 1000 points per WEEKLY (periodUnit) MOVING_WINDOW.
Monday, 5 Jan: A member earns 600 points. Their total for the past 7 days is now 600.
Wednesday, 7 Jan: The member attempts to earn 500 points. The system reviews the previous 7 days (1–7 Jan) and sees that the member has already earned 600. Since adding 500 would exceed the 1000-point limit, the system awards only 400 points (bringing them exactly to the cap).
The member must wait until Monday, 12 Jan—when the 600 points earned on 5 Jan drop out of the 7-day window—before they can earn additional points without restriction.
..periodUnitenumConditionalDefines the specific interval for a PERIOD_BASED limit.
DAILY: The limit resets every day.
Example: A limit of “1 reward per day.” A member can receive the reward on Monday, and once the day resets, they can receive it again on Tuesday.
WEEKLY: The limit resets every week.
Example: A limit of “1 reward per week.” If a member receives the reward on Tuesday, they cannot receive it again until the next week begins (e.g., the following Sunday or Monday, depending on the system’s weekly reset settings).
MONTHLY: The limit resets every month.
Example: A limit of “1 reward per month.” If a member receives the reward on 5 November, they cannot receive it again until the next monthly reset on 1 December.

Example use case 1: applying the customerEnrolment object to update a promotion

Healthy Habits Cafe previously drafted the "Wellness Week Opt-In" promotion. Now they need to update the configuration to activate the promotion. Additionally, anticipating higher than expected demand from their "Pre-selected Wellness Group", they decided to double the participation cap. They need to increase the total enrolment limit from 500 to 1,000 members.

curl --location --request PUT 'https://eu.api.capillarytech.com/v3/promotions/{promotionid}' \
--header 'content-type: application/json' \
--header 'Authorization: Basic Z2VvcmdlLmRvY2RlbW862MTE2NWI5ZjAxMzU5NGIwNDllZTk=' \
--header 'Cookie: _cfuvid=LpG9HQOgnKC0mPstUJNTMOdr1nbz8FvnCc-1763223314046-0.0.1.1-604800000' \
--data '{
  "metadata": {
    "name": "Healthy Habits Wellness Week Opt-In - new1",
    "description": "Audience enrol, activity opt-in w/ ALL restrictions. CAPACITY INCREASED to 1000.",
    "orgId": 100737,
    "programId": 973,
    "startDate": "2025-11-17T00:00:00+05:30",
    "endDate": "2025-11-23T23:59:59+05:30",
    "promotionType": "LOYALTY_EARNING",
    "status": "DRAFT",
    "timezoneName": "Asia/Kolkata",
    "promoIdentifier": "hh-wellness-optin-nov25-ui",
    "promotionMetadata": []
  },
  "customerEnrolment": {
    "enrolmentMethod": "AUDIENCE_FILTER",
    "audienceGroups": [
      {
        "audienceGroupId": 612174375,
        "audienceGroupName": "Pre-selected Wellness Group",
        "description": "Pre-selected member list"
      }
    ]
  },
  "workflowMetadata": {
    "optin": {
      "basedOn": "ACTIVITY",
      "activities": [
        {
          "id": "activity_1761xxxxxx03",
          "type": "SINGLE",
          "name": "Opt-in via Purchase",
          "event": "TransactionAdd",
          "expJSON": "{\n  \"arity\": \"literal\",\n  \"type\": \"boolean:primitive\",\n  \"value\": \"true\"\n}",
          "commonCycleActionMapping": [
            {
              "cycle": "Cycle_1",
              "startDate": "2025-11-17T00:00:00+05:30",
              "endDate": "2025-11-23T23:59:59+05:30",
              "actions": [
                {
                  "id": "temp_action_optin_aaa",
                  "actionName": "EARN_PROMOTION_ACTION",
                  "actionClass": "com.capillary.shopbook.pointsengine.endpoint.impl.action.EarnPromotionActionImpl",
                  "description": "Perform Opt-In",
                  "mandatoryPropertiesValues": {
                    "promotionIdentifier": "hh-wellness-optin-nov25-ui",
                    "activityName": "Opt-in via Purchase",
                    "delegateActionClass": "com.capillary.shopbook.pointsengine.endpoint.impl.action.EarnPromotionActionImpl"
                  },
                  "mandatoryComplexPropertiesValues": {},
                  "embeddedStrategies": []
                }
              ]
            }
          ],
          "milestones": []
        }
      ],
      "audienceMapping": [],
      "restrictions": {
        "optinExpiryBasedOn": {
          "type": "PROMOTION",
          "value": ""
        },
        "enrolmentExpiryBasedOn": {
          "type": "CUSTOM",
          "value": 30
        },
        "optinLimitPerCustomer": {
          "value": "1",
          "type": "PERIOD_BASED",
          "periodType": "MOVING_WINDOW",
          "periodUnit": "DAILY"
        },
        "maxRedemptionsPerEarnPerCustomer": {
          "value": "5",
          "type": "NON_PERIOD_BASED"
        },
        "enrolmentLimitPerPromotion": {
          "value": "1000"
        },
        "enrolmentLimitPerCustomer": {
          "value": "1"
        },
        "maxPointsPerEarnPerCustomer": {
          "value": 200,
          "type": "NON_PERIOD_BASED"
        }
      }
    },
    "enrolment": {}
  },
  "activities": [
    {
      "id": "activity_1761xxxxxx04",
      "type": "SINGLE",
      "name": "Wellness Reward",
      "event": "TransactionAdd",
      "expJSON": "{\n  \"arity\": \"literal\",\n  \"type\": \"boolean:primitive\",\n  \"value\": \"true\"\n}",
      "commonCycleActionMapping": [
        {
          "cycle": "Cycle_1",
          "startDate": "2025-11-17T00:00:00+05:30",
          "endDate": "2025-11-23T23:59:59+05:30",
          "actions": [
            {
              "id": "temp_action_wellness",
              "actionName": "AWARD_POINTS_ACTION",
              "actionClass": "com.capillary.shopbook.pointsengine.endpoint.impl.action.AwardPointsActionImpl",
              "mandatoryPropertiesValues": {
                "AwardStrategy": "109010"
              },
              "embeddedStrategies": []
            }
          ]
        }
      ],
      "milestones": []
    }
  ],
  "limits": [],
  "liabilityOwnerSplitInfo": []
}'

Example use case 2: applying the customerEnrolment object to update a promotion

Gadget Galaxy already has a "Beta Tester Program" running where they manually import specific members. However, they now want to expand access. They need to update the promotion so that any member who joins the "PreOrder List" audience group is automatically enrolled as well, without stopping the manual imports.

curl --location --request PUT 'https://eu.api.capillarytech.com/v3/promotions/{promotionid}' \
--header 'Content-Type: application/json' \
--header 'X-CAP-API-AUTH-ORG-ID: 100232' \
--header 'Authorization: Basic Z2VvcmdlLmRvY2RlbW86NjVhMDgzYjk1MWY5MGY1NTY5NDk1YmNkNzUxYmJiY2U=' \
--header 'Cookie: _cfuvid=bw96IuxgkLuZy69HNs9OCYqoMPzwtPCXTg6pMsX5zEo-1765521791434-0.0.1.1-604800000' \
--data '{
  "metadata": {
    "name": "Gadget Galaxy Beta Tester Program Q1-UI1",
    "description": "Import enrol. UPDATED: Added dynamic enrolment for PreOrder List.",
    "orgId": 100737,
    "programId": 973,
    "startDate": "2026-01-01T00:00:00+05:30",
    "endDate": "2026-03-31T23:59:59+05:30",
    "promotionType": "LOYALTY_EARNING",
    "status": "DRAFT",
    "timezoneName": "Asia/Kolkata",
    "promoIdentifier": "gg-beta-q126-ui",
    "promotionMetadata": []
  },
  "customerEnrolment": {
    "enrolmentMethod": "IMPORT",
    "audienceGroups": []
  },
  "workflowMetadata": {
    "optin": {},
    "enrolment": {
      "basedOn": "AUDIENCE",
      "activities": [],
      "audienceMapping": [
        {
          "groupId": 612174375,
          "groupName": "PreOrder List"
        }
      ],
      "restrictions": {
        "enrolmentLimitPerPromotion": {
          "value": "1000"
        }
      }
    }
  },
  "activities": [
    {
      "id": "activity_1761xxxxxx05",
      "type": "SINGLE",
      "name": "Beta Reward (100 Points)",
      "event": "FeedbackSubmitted",
      "expJSON": "{\n  \"arity\": \"literal\",\n  \"type\": \"boolean:primitive\",\n  \"value\": \"true\"\n}",
      "commonCycleActionMapping": [
        {
          "cycle": "Cycle_1",
          "startDate": "2026-01-01T00:00:00+05:30",
          "endDate": "2026-03-31T23:59:59+05:30",
          "actions": [
            {
              "id": "temp_action_beta_pts",
              "actionName": "AWARD_POINTS_ACTION",
              "actionClass": "com.capillary.shopbook.pointsengine.endpoint.impl.action.AwardPointsActionImpl",
              "mandatoryPropertiesValues": {
                "AwardStrategy": "109010",
                "ExpiryStrategy": "109006",
                "PointType": "Main"
              },
              "embeddedStrategies": []
            }
          ]
        }
      ],
      "milestones": []
    }
  ],
  "limits": [
    {
      "granularity": "USER",
      "actionType": "AWARD_CURRENCY",
      "actionSubTypeId": "Points",
      "limitType": "SUM",
      "limitValue": 500,
      "period": {
        "periodType": "NON_PERIOD_BASED"
      }
    }
  ],
  "liabilityOwnerSplitInfo": []
}'

The activities object

Specifies the definitions for what actions a member must perform to qualify, including the specific events to listen for and the rules to evaluate.

activities object (common fields)

Specifies the specific event name to listen for, any rule expressions used to filter that event, and the time-based settings (like frequency and evaluation types) that determine how the activity tracks member progress. These fields are present on all activity objects, whether SINGLE or GROUP.

FieldTypeRequiredDescription
idstringOptionalUnique identifier for the activity. Example: "activity_1761023957829"
typeenumYesSINGLE: A single trigger. GROUP: A combination of triggers. Supported Values: SINGLE, GROUP.
namestringOptionalName for identification. Example: "Main Purchase Activity"
parentIdstringOptionalID of the parent activity if this activity is a child inside a GROUP. Example: "activity_group_parent"
rulesetIdstringOptionalSystem-assigned identifier for the Points Engine ruleset associated with this activity. Populated by the backend after the promotion is published. Pass this back when editing an existing activity.
cyclesArray (Object)OptionalDefines general time windows (cycles) for this activity.
..idstringOptionalCycle ID. Example: "c1"
..namestringOptionalCycle name. Example: "Nov Cycle"
..startDatestringOptionalTime format: ISO 8601 timestamp with timezone offset. Example: 2025-12-16T14:30:45+05:30
..endDatestringOptionalTime format: ISO 8601 timestamp with timezone offset. Example: 2025-12-31T23:59:59+05:30
commonCycleActionMappingArray (Object)OptionalDefines the reward(s) for this activity. Links actions to cycles.
..cyclestringOptionalID of the cycle this action applies to. Example: "main_cycle"
..defaultValuenumberOptionalA default value (e.g., a target amount) used in action calculations. Example: 10.0
..startDatestringOptionalTime format: ISO 8601 timestamp with timezone offset. Example: 2025-12-16T14:30:45+05:30
..endDatestringOptionalTime format: ISO 8601 timestamp with timezone offset. Example: 2025-12-31T23:59:59+05:30
..rulesetIdstringOptionalSystem-assigned ruleset ID for this specific action mapping. Pass this back when editing an existing action mapping.
..actionsArray (Object)OptionalThe list of rewards to issue. See the Action Object table below.

actions object (fields inside activities array)

Specifies the details of the reward fulfilment — the type of reward to issue and the parameters required to process it.

FieldTypeRequiredDescription
idstringOptionalClient-supplied temporary identifier for this action. Used to reference the action within the request payload and is not persisted as the system's internal ID. Example: "temp_06a1a899-99e8-4f5e-8910-a3b2b51aa142"
actionNamestringYesSpecifies the action to trigger for the loyalty promotion. Supported values: See delegateActionClass Values for the complete list.
actionClassstringYesAlways com.capillary.shopbook.pointsengine.endpoint.impl.action.CommunicationDecoratorActionImpl. The actual implementation is determined by delegateActionClass in mandatoryPropertiesValues.
descriptionstringOptionalA brief label for this action. Example: "Award 100 bonus points"
mandatoryPropertiesValuesObjectConditionalKey-value pairs that configure the action. Required keys vary by actionName.
mandatoryComplexPropertiesValuesObjectOptionalObject containing details on line-item filter conditions used to restrict award eligibility to specific products or categories.
embeddedStrategiesArray (Object)OptionalObject containing details on inline strategy configurations (point allocation and expiry) attached to this action.

Line-item based awarding

Award badges, points, or other rewards based on individual line items in a transaction rather than the entire transaction. Use this to target specific products, categories, brands, or payment methods.

Configuring line-item based awarding

Configure the following properties in the mandatoryPropertiesValues object:

PropertyTypeDescription
ProRateOnSourceValuestringControls how awards are distributed. Supported values: LINEITEM_AMOUNT (distribute based on line item amount), LINEITEM_QUANTITY (distribute based on line item quantity), LINEITEM_EXTENDED_FIELD (distribute based on a custom extended field value), EVENT_DEFAULT_VALUE or AMOUNT (bill-level awarding).
ProrateFieldNamestringSpecifies the extended field name to use for distribution. Required when ProRateOnSourceValue is LINEITEM_EXTENDED_FIELD. Example: "item_bonus"
UseProportionsstringSpecifies whether to use proportional distribution across qualifying line items. Set to "true" to enable.

Filtering line items

Use the mandatoryComplexPropertiesValues object to filter which line items qualify for the award. Each filter is an object with operator and values properties.

PropertyDescriptionExample
skusFilters by product SKU codes.{"operator": "IN", "values": "SKU001,SKU002,SKU003"}
brandsFilters by brand names.{"operator": "IN", "values": "Nike,Adidas"}
categoriesFilters by product categories.{"operator": "IN", "values": "Electronics,Apparel"}
attributesFilters by product attributes.{"operator": "IN", "values": "color:red,size:large"}
tendermodeFilters by payment/tender type.{"operator": "IN", "values": "cash,card"}

When multiple filters are specified, a line item must match all filters to qualify (AND logic).

activities object (single activities)

Specifies the specific event name to listen for, any rule expressions used to filter that event, and the time-based settings (like frequency and evaluation types) that determine how the activity tracks member progress.

FieldTypeRequiredDescription
eventstringYesThe exact event name that triggers the activity. The event cannot be changed on an existing activity once the draft is created. Supported events: TransactionAdd, GroupTransactionAdd, TransactionFinished, NewBill, SlabUpgrade and custom behavioural events.
ruleExpressionstringOptionalSpecifies the rule expression string to filter the triggering event.
expJSONstringOptionalSpecifies the JSON representation of the loyalty rule expression. (Valid JSON rule structure string). To generate an expJSON from a loyalty workflow expression use the Generate Expression JSON API.
frequencyTypestringOptionalSpecifies the time unit for milestone evaluation.
Supported values:
DAILY: Evaluates daily.
WEEKLY: Evaluates weekly.
MONTHLY: Evaluates monthly.
QUARTERLY: Evaluates quarterly.
HALF_YEARLY: Evaluates every half year.
YEARLY: Evaluates yearly.
CUSTOM: Uses a custom-defined frequency period.
targetEvaluationTypeenumOptionalSpecifies the evaluation logic window:


FIXED_CALENDAR_WINDOW: Tracks progress within the specific start and end dates set for the milestone, regardless of the broader promotion duration.
Example: "Spend $500 specifically between December 1st and December 15th to get a bonus."
CYCLIC_WINDOW: Tracks and resets progress based on the custom time intervals defined in your activityCycles list.
Example: "Complete 3 tasks during 'Phase 1' (Jan 1-10) and then 3 tasks during 'Phase 2' (Jan 20-30)."
PERIOD_AGNOSTIC_WINDOW: Tracks cumulative progress over the entire duration of the promotion without ever resetting.
Example: "Reach a total of 50 visits at any point during the year-long promotion to become a VIP."
CALENDAR_CYCLIC_WINDOW: Tracks progress based on standard calendar units (like days or weeks) that repeat automatically.
Example: "Make a purchase every Sunday (Weekly cycle) to earn double points."
targetCycleStartDatestring (date-time)OptionalTime format: ISO 8601 timestamp with timezone offset. Example: 2025-12-16T14:30:45+05:30
targetCycleEndDatestring (date-time)OptionalTime format: ISO 8601 timestamp with timezone offset. Example: 2025-12-31T23:59:59+05:30
activityCyclesArray (Object)OptionalDefines specific, named time periods used by a milestone.
..idstringOptionalSpecifies the activity cycle ID.
..namestringOptionalSpecifies the activity cycle name.
..startDatestringOptionalDefines the activity cycle's start date in ISO 8601 format with the region offset. For example: The start date is at 14:30:45 on December 16, 2025, in India Format for the request parameter: 2025-12-16T14:30:45+05:30
..endDatestringOptionalDefines the activity cycle's end date in ISO 8601 format with the region offset. For example: The end date is at 14:30:45 on December 16, 2025, in India Format for the request parameter: 2025-12-16T14:30:45+05:30
..refCodestringOptionalSpecifies the activity cycle reference code.
..isActivebooleanOptionalIndicates if the activity cycle is active. Supported values : true , false
milestonesArray (Object)OptionalDefines a list of target milestones within this activity.
..namestringOptionalSpecifies the milestone name.
..sameActionsForEveryCyclebooleanOptionalIndicates if the same actions apply across all cycles.
..differentTargetsForEveryCyclebooleanOptionalIndicates if target values differ across cycles.
..descriptionstringOptionalIndicates the milestone description (String description).
..trackingTypestringOptionalSpecifies how the milestone is tracked.
Supported values: DEFAULT, STREAKS, NON_CONTINUOUS_STREAKS, CAPPING, UNIFIED.
..targetTypeenumYesDefines the KPI to track for the milestone.
Supported values: QUANTITY, SALES, COUNT, VISIT, GROSS_SALES, REGULAR_POINTS, PROMOTIONAL_POINTS, ALL_POINTS, EXTENDED_FIELD, EVENT_ATTRIBUTE. See the milestones object table below for full descriptions.
..targetEntityenumYesSpecifies the entity to track the targetType against.
Supported values: TRANSACTION, LINEITEM, POINTS, EVENT, ALTERNATE_CURRENCIES.
..defaultValuestringOptionalSpecifies the target value threshold for achieving the milestone. Although string type, holds a numeric value. Example: "15"
..targetValuestringOptionalSpecifies the target value to achieve the milestone (often same as defaultValue).
..preferredTillIdinteger (int64)OptionalSpecifies the till ID if tracking transactions on specific POS terminals is required.
..frequencyTypestringOptionalSpecifies the time unit for milestone evaluation.
Supported values: DAILY, WEEKLY, MONTHLY, QUARTERLY, HALF_YEARLY, YEARLY, CUSTOM.
..targetEvaluationTypeenumOptionalSpecifies the evaluation logic window.
Supported values: FIXED_CALENDAR_WINDOW, CYCLIC_WINDOW, PERIOD_AGNOSTIC_WINDOW, CALENDAR_CYCLIC_WINDOW.
..recurringCyclesinteger (int32)OptionalSpecifies the number of recurring cycles for milestone evaluation.
..targetGroupIdinteger (int64)OptionalIndicates the target group ID associated with this milestone's tracking logic.
..leaderboardEnabledbooleanOptionalIndicates if a leaderboard competition is enabled for this milestone (true or false).
..cycleActionMappingArray (Object)OptionalDefines the reward(s) for achieving the milestone.
..streaksArray (Object)OptionalDefines streak configuration if trackingType is STREAKS.
....namestringOptionalSpecifies the streak name.
....targetCountOfSequenceinteger (int32)OptionalSpecifies the required count within each period.
....consecutivebooleanOptionalTrue means the streak must be unbroken; false allows non-consecutive days.
....aggregateFunctionstringOptionalSpecifies the function to aggregate values: "SUM" or "COUNT".
..cyclesArray (Object)OptionalDefines the evaluation cycles specific to this milestone.
....idstringOptionalSpecifies the cycle ID .
....namestringOptionalSpecifies the cycle name.
....startDatedateOptionalTime format: ISO 8601 timestamp with timezone offset. Example: 2025-12-16T14:30:45+05:30
....endDatedateOptionalTime format: ISO 8601 timestamp with timezone offset. Example: 2025-12-31T23:59:59+05:30
..periodsArrayOptionalDefines the evaluation periods for the milestone.
....idstringOptionalSpecifies the period ID.
....namestringOptionalSpecifies the period name.
....startDatestringOptionalTime format: ISO 8601 timestamp with timezone offset. Example: 2025-12-16T14:30:45+05:30
....endDatestringOptionalTime format: ISO 8601 timestamp with timezone offset. Example: 2025-12-31T23:59:59+05:30
....refCodestringOptionalSpecifies the period reference code.
....isActivebooleanOptionalIndicates if the period is active.
..targetRuleIdsArray (integer int64)OptionalIndicates associated target rule IDs.
..individualMilestoneFileDetailsArray (Object)OptionalDetails related to uploaded files providing individual targets.
....fileNamestringOptionalThe original name of the uploaded file.
....fileHandlestringOptionalThe system URL/handle where the file is stored.
....statusstringOptionalThe processing status of the file.
....totalRecordsintegerOptionalThe total number of records found in the file.
....successRecordsintegerOptionalThe number of records successfully processed.
....failureCountintegerOptionalThe number of records that failed to process .

activities (group activities)

FieldTypeRequiredDescription
combinationTypeenumYesLogic to combine children in GROUP activities.
Supported values: ANY, ALL.
childrenArray (Object)YesAn array of child Activity objects (SINGLE or nested GROUP). Note: When a GROUP activity has commonCycleActionMapping populated, it takes precedence as the reward definition for the entire group. Child activities must not have their own commonCycleActionMapping.
eventstringOptionalThe event that applies to the group as a whole. Built-in events: TransactionAdd, GroupTransactionAdd, TransactionFinished. Custom org-configured events (e.g., GymCheckIn) are also supported.
ruleExpressionstringOptionalRule expression that applies to the group result.
expJSONstringOptionalJSON representation of the group's rule expression.

milestones object (fields inside milestones array)

FieldTypeRequiredDescription
namestringOptionalSpecifies the milestone name. Supported Values: String. Example: "Tier 1: Spend $100"
sameActionsForEveryCyclebooleanOptionalSpecifies if the same actions apply across all cycles defined for this milestone. Supported Values:
true: The same reward will be given every time a cycle is completed. false: You can define different reward for each specific cycle.
differentTargetsForEveryCyclebooleanOptionalSpecifies if target values differ across cycles. Supported Values: true: The target value (e.g., spend $100) can be different for each cycle. false: The target value is the same for all cycles.
descriptionstringOptionalIndicates the milestone description. Supported Values: String. Example: "Reward for reaching the first spending level"
trackingTypestringOptionalSpecifies how the milestone is tracked.
Supported values:
DEFAULT: Tracks simple cumulative progress towards a single target.
STREAKS: Tracks consecutive event occurrences (e.g., "Visit 3 days in a row").
NON_CONTINUOUS_STREAKS: Tracks streak occurrences that do not need to be consecutive.
CAPPING: Applies a cap on how much progress can accumulate within a period.
UNIFIED: Tracks progress across a unified set of targets.
INDIVIDUAL: Tracks progress against personalised targets uploaded from a CSV file.
targetTypeenumYesDefines the KPI to track for the milestone. When using EXTENDED_FIELD, you must also populate the targetRuleMetaData object.
Supported values:
QUANTITY: Total items purchased.
SALES: Net transaction value after discounts.
COUNT: Number of event occurrences.
VISIT: Number of unique days a member performs an action.
GROSS_SALES: Total sales value before discounts.
REGULAR_POINTS: Total regular (base) points earned.
PROMOTIONAL_POINTS: Total promotional (bonus) points earned.
ALL_POINTS: Sum of regular and promotional points.
EXTENDED_FIELD: Tracks a custom data field sent with the transaction.
EVENT_ATTRIBUTE: Tracks a specific attribute from the incoming event payload.
targetEntityenumYesSpecifies the entity to track the targetType against.
Supported values:
TRANSACTION: Tracks at the overall transaction level (e.g., total bill amount).
LINEITEM: Tracks at the individual product level (e.g., quantity of a specific SKU).
POINTS: Tracks points earned.
EVENT: Tracks a non-transactional event (e.g., a gym check-in).
ALTERNATE_CURRENCIES: Tracks a custom-defined currency (e.g., "Coins" or "Gems").
targetRuleMetaDataObjectConditionalRequired when targetType is EXTENDED_FIELD. Defines which custom extended field to track and how to aggregate its values across line items or events.
..namestringYesThe name of the extended field to track. Must match an extended field configured for the org. Example: "CentralGST"
..aggregateFunctionenumYesSpecifies how to aggregate the extended field values.
Supported values: SUM, COUNT, MAX, MIN.
defaultValuestringOptionalSpecifies the default target value threshold for achieving the milestone. Although string type, holds a numeric value. Example: "15"
targetValuestringOptionalSpecifies the target value to achieve the milestone (often same as defaultValue).
preferredTillIdinteger (int64)OptionalIndicates a preferred till ID if tracking is specific to certain POS terminals.
frequencyTypestringOptionalSpecifies the time unit for milestone evaluation.
Supported values:
DAILY, WEEKLY, MONTHLY, QUARTERLY, HALF_YEARLY, YEARLY, CUSTOM.
targetEvaluationTypeenumOptionalSpecifies the evaluation logic window.
Supported values:
FIXED_CALENDAR_WINDOW: Uses defined start/end dates for the entire milestone. Example: Counter resets every Monday or on the 1st of every month.
CYCLIC_WINDOW: Resets progress based on cycles. Example: Progress resets at the start of a pre-configured cycle.
PERIOD_AGNOSTIC_WINDOW: Evaluates over the entire promotion duration without resets. Example: Counter never resets; must achieve the goal at any point during the promotion.
CALENDAR_CYCLIC_WINDOW: Combines calendar and cyclic logic. Example: Resets counting after every 30 days of activity.
recurringCyclesinteger (int32)OptionalSpecifies the number of recurring cycles for milestone evaluation.
targetGroupIdinteger (int64)OptionalIndicates the target group ID associated with this milestone's tracking logic.
leaderboardEnabledbooleanOptionalIndicates if a leaderboard competition is enabled for this milestone.
Supported values: true, false.
cycleActionMappingArray (Object)OptionalDefines the reward(s) for achieving the milestone. Same structure as commonCycleActionMapping.
streaksArray (Object)OptionalDefines streak configuration. Required when trackingType is STREAKS or NON_CONTINUOUS_STREAKS.
..namestringOptionalSpecifies the streak name. Example: "3 Consecutive Visits"
..targetCountOfSequenceinteger (int32)OptionalThe required number of consecutive qualifying events per cycle. Example: 3 (visit 3 days in a row).
..consecutivebooleanOptionalWhether the events must be consecutive.
true: The streak must be unbroken.
false: Non-consecutive occurrences are allowed.
aggregateFunctionstringOptionalSpecifies the function to aggregate values when tracking metrics that require summation or counting.
Supported values: SUM, COUNT, MAX, MIN.
cyclesArray (Object)OptionalDefines the evaluation cycles specific to this milestone.
..idstringOptionalSpecifies the cycle ID. Supported Values: String ID.
..namestringOptionalSpecifies the cycle name. Supported Values: String. Example: "Milestone Nov Cycle"
..startDatestring (date-time)OptionalTime format: ISO 8601 timestamp with timezone offset. Example: 2025-12-16T14:30:45+05:30
..endDatestring (date-time)OptionalTime format: ISO 8601 timestamp with timezone offset. Example: 2025-12-31T23:59:59+05:30
periodsArray (Object)OptionalDefines the evaluation periods for the milestone.
..idstringOptionalSpecifies the period ID. Supported Values: String ID. Example: "m_period_1"
..namestringOptionalSpecifies the period name. Supported Values: String. Example: "Milestone Nov Period"
..startDatestringOptionalTime format: ISO 8601 timestamp with timezone offset. Example: 2025-12-16T14:30:45+05:30
..endDatestringOptionalTime format: ISO 8601 timestamp with timezone offset. Example: 2025-12-31T23:59:59+05:30
..refCodestringOptionalSpecifies the period reference code.
..isActivebooleanOptionalIndicates if the period is active. Supported Values: true: The period is currently active. false: The period is inactive. Example: true
individualMilestoneFileDetailsArray (Object)OptionalArray containing the configuration for the CSV file to be processed.
..fileHandlestringYesThe unique UUID reference to the file after it has been uploaded to the file storage service.
..fileNamestringYesThe original name of the CSV file being uploaded (e.g., "targets.csv").
..columnsMappingObjectYesAn object that maps your CSV column names to the system's fields.
....userIdentifierstringYesThe user identification method. Possible Values: EMAIL: User is identified by their email address. USER_ID: User is identified by their system User ID. MOBILE: User is identified by their mobile number. EXTERNAL_ID: User is identified by an external system ID.
....userIdentifierColumnstringYesThe exact name of the column in your CSV that contains the user identifier (e.g., "phone_number").
....targetColumnsObjectYesA key-value map where the key is the system cycle (Cycle_1, Cycle_2) and the value is the corresponding column name in your CSV (e.g., "October_Target").

Example use case 1: applying the activities object to update a promotion

FitNation Gym previously drafted the "Workout Warrior" challenge with a target of 15 visits. Now they need to update the promotion to go live. However, after reviewing member attendance data, management decided that 15 visits in one month is too difficult for most members. To encourage higher participation, they are lowering the milestone target to 12 visits before activating the promotion.

curl --location --request PUT 'https://eu.api.capillarytech.com/v3/promotions/{promotionid}' \
--header 'Content-Type: application/json' \
--header 'X-CAP-API-AUTH-ORG-ID: 100232' \
--header 'Authorization: Basic Z2VvcmdlLmRvY2RlbW86NjVhMDgzYjk1MWY5MGY1NTY5NDk1YmNkNzUxYmJiY2U=' \
--header 'Cookie: _cfuvid=bw96IuxgkLuZy69HNs9OCYqoMPzwtPCXTg6pMsX5zEo-1765521791434-0.0.1.1-604800000' \
--data '{
  "metadata": {
    "name": "FitNation Workout Warrior - Nov-UI2",
    "description": "Milestone: 12 Gym Check-ins in Nov at Loc 101. UPDATED TARGET.",
    "orgId": 100737,
    "programId": 973,
    "startDate": "2025-11-01T00:00:00+05:30",
    "endDate": "2025-11-30T23:59:59+05:30",
    "promotionType": "GENERIC",
    "status": "DRAFT",
    "timezoneName": "Asia/Kolkata",
    "promoIdentifier": "fn-warrior-nov25-ui",
    "promotionMetadata": []
  },
  "customerEnrolment": {
    "enrolmentMethod": "TRANSACTION",
    "audienceGroups": []
  },
  "activities": [
    {
      "id": "activity_1761000000006",
      "type": "SINGLE",
      "name": "Track Gym CheckIns",
      "event": "GymCheckIn",
      "expJSON": "{\n  \"arity\": \"binary_operation\",\n  \"type\": \"boolean:primitive\",\n  \"value\": \"==\",\n  \"operands\": [\n    {\n      \"arity\": \"object_dereference\",\n      \"type\": \"number:object:primitive\",\n      \"operands\": [\n        {\n          \"arity\": \"name\",\n          \"type\": \"event:object:primitive\",\n          \"value\": \"currentEvent\"\n        },\n        {\n          \"arity\": \"name\",\n          \"type\": \"number:object:primitive\",\n          \"value\": \"locationId\"\n        }\n      ]\n    },\n    {\n      \"arity\": \"literal\",\n      \"type\": \"number:primitive\",\n      \"value\": \"101\"\n    }\n  ]\n}",
      "commonCycleActionMapping": [],
      "frequencyType": "WEEKLY",
      "targetEvaluationType": "CYCLIC_WINDOW",
      "activityCycles": [
        {
          "id": "ac_w1",
          "name": "Nov Week 1",
          "startDate": "2025-11-01T00:00:00+05:30",
          "endDate": "2025-11-09T23:59:59+05:30",
          "refCode": "W1",
          "isActive": true
        },
        {
          "id": "ac_w2",
          "name": "Nov Week 2",
          "startDate": "2025-11-10T00:00:00+05:30",
          "endDate": "2025-11-16T23:59:59+05:30",
          "refCode": "W2",
          "isActive": true
        },
        {
          "id": "ac_w3",
          "name": "Nov Week 3",
          "startDate": "2025-11-17T00:00:00+05:30",
          "endDate": "2025-11-23T23:59:59+05:30",
          "refCode": "W3",
          "isActive": true
        },
        {
          "id": "ac_w4",
          "name": "Nov Week 4",
          "startDate": "2025-11-24T00:00:00+05:30",
          "endDate": "2025-11-30T23:59:59+05:30",
          "refCode": "W4",
          "isActive": true
        }
      ],
      "milestones": [
        {
          "name": "Achieve 12 Check-Ins",
          "trackingType": "DEFAULT",
          "leaderboardEnabled": true,
          "targetType": "VISIT",
          "targetEntity": "EVENT",
          "aggregateFunction": "COUNT",
          "preferredTillId": null,
          "cycleActionMapping": [
            {
              "cycle": "Cycle_1",
              "startDate": "2025-11-01T00:00:00+05:30",
              "endDate": "2025-11-30T23:59:59+05:30",
              "defaultValue": "12",
              "actions": [
                {
                  "id": "temp_action_tierup",
                  "actionName": "UPGRADE_SLAB_ACTION_SLAB",
                  "actionClass": "com.capillary.shopbook.pointsengine.endpoint.impl.action.UpgradeSlabActionImpl",
                  "description": "Upgrade to Tier 2",
                  "mandatoryComplexPropertiesValues": {},
                  "mandatoryPropertiesValues": {
                    "EvaluatedEntity": "USER",
                    "ToSlabSerialNumber": "2",
                    "TierBasedExpiry": "0"
                  },
                  "embeddedStrategies": []
                }
              ]
            }
          ]
        }
      ]
    }
  ],
  "limits": [],
  "liabilityOwnerSplitInfo": [],
  "workflowMetadata": {
    "optin": {},
    "enrolment": {}
  }
}'

Example use case 2: applying the activities object to update a promotion

Gourmet Galaxy Foods drafted their "Dinner Special" promotion, configured to award points during specific dinner hours for purchasing either a 'Premium Steak' or a 'Salad + Bread' bundle over $30. Now they need to update the promotion to launch it. Additionally, they have decided to slightly extend the "Dinner Hours" window to capture late-night diners, changing the end time from 10:00 PM to 11:00 PM.

curl --location --request PUT 'https://eu.api.capillarytech.com/v3/promotions/{promotionid}' \
--header 'content-type: application/json' \
--header 'Authorization: Basic Z2VvcmdlLmRvY2RlbW86MmQ1OTNhMZjAxMzU5NGIwNDllZTk=' \
--header 'Cookie: _cfuvid=LpG9HQ0MrgpPTMOdr1nbz8FvnCc-1763223314046-0.0.1.1-604800000' \
--data '{
  "metadata": {
    "name": "Gourmet Galaxy Dinner Special-22-UI",
    "description": "Points for Steak OR (Salad AND Bread AND >$30). UPDATED: Dinner hours extended to 11 PM.",
    "orgId": 100737,
    "programId": 973,
    "startDate": "2025-11-01T00:00:00+05:30",
    "endDate": "2025-11-30T23:59:59+05:30",
    "promotionType": "LOYALTY_EARNING",
    "status": "DRAFT",
    "timezoneName": "Asia/Kolkata",
    "promoIdentifier": "gg-dinner-nov25-ui",
    "promotionMetadata": []
  },
  "customerEnrolment": {
    "enrolmentMethod": "TRANSACTION",
    "audienceGroups": []
  },
  "activities": [
    {
      "id": "group_176100000007",
      "type": "GROUP",
      "name": "Dinner Special Check (ANY)",
      "combinationType": "ANY",
      "event": "TransactionAdd",
      "cycles": [
        {
          "id": "c_dinner",
          "name": "Dinner Hours",
          "startDate": "2025-11-01T18:00:00+05:30",
          "endDate": "2025-11-30T23:00:00+05:30"
        }
      ],
      "expJSON": "{\n  \"arity\": \"literal\",\n  \"type\": \"boolean:primitive\",\n  \"value\": \"true\"\n}",
      "children": [
        {
          "id": "sub_activity_176100000008",
          "parentId": "group_176100000007",
          "name": "Bought Premium Steak",
          "type": "SINGLE",
          "event": "TransactionAdd",
          "expJSON": "{\n  \"arity\": \"unary_operation\",\n  \"type\": \"boolean:primitive\",\n  \"value\": \"exists\",\n  \"operands\": [\n    {\n      \"arity\": \"object_dereference\",\n      \"type\": \"collection:object:primitive\",\n      \"operands\": [\n        {\n          \"arity\": \"name\",\n          \"type\": \"transaction:object:primitive\",\n          \"value\": \"currentTransaction\"\n        },\n        {\n          \"arity\": \"name\",\n          \"type\": \"collection:object:primitive\",\n          \"value\": \"lineItems\"\n        }\n      ]\n    },\n    {\n      \"arity\": \"binary_operation\",\n      \"type\": \"boolean:primitive\",\n      \"value\": \"==\",\n      \"operands\": [\n        {\n          \"arity\": \"object_dereference\",\n          \"type\": \"string:object:primitive\",\n          \"operands\": [\n            {\n              \"arity\": \"name\",\n              \"type\": \"item:object:primitive\",\n              \"value\": \"item\"\n            },\n            {\n              \"arity\": \"name\",\n              \"type\": \"string:object:primitive\",\n              \"value\": \"sku\"\n            }\n          ]\n        },\n        {\n          \"arity\": \"literal\",\n          \"type\": \"string:primitive\",\n          \"value\": \"GG-STK-01\"\n        }\n      ]\n    }\n  ]\n}",
          "milestones": [
            {
              "name": "Default Milestone Steak",
              "trackingType": "DEFAULT",
              "leaderboardEnabled": false
            }
          ],
          "commonCycleActionMapping": []
        },
        {
          "id": "sub_group_176100000009",
          "parentId": "group_176100000007",
          "name": "Side Bundle Check (ALL)",
          "type": "GROUP",
          "combinationType": "ALL",
          "rulesetId": "SIDE_BUNDLE",
          "event": "TransactionAdd",
          "expJSON": "{\n  \"arity\": \"literal\",\n  \"type\": \"boolean:primitive\",\n  \"value\": \"true\"\n}",
          "children": [
            {
              "id": "sub_sub_activity_10",
              "parentId": "sub_group_176100000009",
              "name": "Bought Organic Salad",
              "type": "SINGLE",
              "event": "TransactionAdd",
              "expJSON": "{\n  \"arity\": \"unary_operation\",\n  \"type\": \"boolean:primitive\",\n  \"value\": \"exists\",\n  \"operands\": [\n    {\n      \"arity\": \"object_dereference\",\n      \"type\": \"collection:object:primitive\",\n      \"operands\": [\n        {\n          \"arity\": \"name\",\n          \"type\": \"transaction:object:primitive\",\n          \"value\": \"currentTransaction\"\n        },\n        {\n          \"arity\": \"name\",\n          \"type\": \"collection:object:primitive\",\n          \"value\": \"lineItems\"\n        }\n      ]\n    },\n    {\n      \"arity\": \"binary_operation\",\n      \"type\": \"boolean:primitive\",\n      \"value\": \"==\",\n      \"operands\": [\n        {\n          \"arity\": \"object_dereference\",\n          \"type\": \"string:object:primitive\",\n          \"operands\": [\n            {\n              \"arity\": \"name\",\n              \"type\": \"item:object:primitive\",\n              \"value\": \"item\"\n            },\n            {\n              \"arity\": \"name\",\n              \"type\": \"string:object:primitive\",\n              \"value\": \"sku\"\n            }\n          ]\n        },\n        {\n          \"arity\": \"literal\",\n          \"type\": \"string:primitive\",\n          \"value\": \"GG-SAL-01\"\n        }\n      ]\n    }\n  ]\n}",
              "milestones": [
                {
                  "name": "Default Milestone Salad",
                  "trackingType": "DEFAULT",
                  "leaderboardEnabled": false
                }
              ],
              "commonCycleActionMapping": []
            },
            {
              "id": "sub_sub_activity_11",
              "parentId": "sub_group_176100000009",
              "name": "Bought Artisan Bread",
              "type": "SINGLE",
              "event": "TransactionAdd",
              "expJSON": "{\n  \"arity\": \"unary_operation\",\n  \"type\": \"boolean:primitive\",\n  \"value\": \"exists\",\n  \"operands\": [\n    {\n      \"arity\": \"object_dereference\",\n      \"type\": \"collection:object:primitive\",\n      \"operands\": [\n        {\n          \"arity\": \"name\",\n          \"type\": \"transaction:object:primitive\",\n          \"value\": \"currentTransaction\"\n        },\n        {\n          \"arity\": \"name\",\n          \"type\": \"collection:object:primitive\",\n          \"value\": \"lineItems\"\n        }\n      ]\n    },\n    {\n      \"arity\": \"binary_operation\",\n      \"type\": \"boolean:primitive\",\n      \"value\": \"==\",\n      \"operands\": [\n        {\n          \"arity\": \"object_dereference\",\n          \"type\": \"string:object:primitive\",\n          \"operands\": [\n            {\n              \"arity\": \"name\",\n              \"type\": \"item:object:primitive\",\n              \"value\": \"item\"\n            },\n            {\n              \"arity\": \"name\",\n              \"type\": \"string:object:primitive\",\n              \"value\": \"sku\"\n            }\n          ]\n        },\n        {\n          \"arity\": \"literal\",\n          \"type\": \"string:primitive\",\n          \"value\": \"GG-BRD-01\"\n        }\n      ]\n    }\n  ]\n}",
              "milestones": [
                {
                  "name": "Default Milestone Bread",
                  "trackingType": "DEFAULT",
                  "leaderboardEnabled": false
                }
              ],
              "commonCycleActionMapping": []
            },
            {
              "id": "sub_sub_activity_12",
              "parentId": "sub_group_176100000009",
              "name": "Transaction > $30",
              "type": "SINGLE",
              "event": "TransactionAdd",
              "expJSON": "{\n  \"arity\": \"binary_operation\",\n  \"type\": \"boolean:primitive\",\n  \"value\": \">\",\n  \"operands\": [\n    {\n      \"arity\": \"object_dereference\",\n      \"type\": \"real:object:primitive\",\n      \"operands\": [\n        {\n          \"arity\": \"name\",\n          \"type\": \"transaction:object:primitive\",\n          \"value\": \"currentTransaction\"\n        },\n        {\n          \"arity\": \"name\",\n          \"type\": \"real:object:primitive\",\n          \"value\": \"amount\"\n        }\n      ]\n    },\n    {\n      \"arity\": \"literal\",\n      \"type\": \"number:primitive\",\n      \"value\": \"30\"\n    }\n  ]\n}",
              "milestones": [
                {
                  "name": "Default Milestone Amount",
                  "trackingType": "DEFAULT",
                  "leaderboardEnabled": false
                }
              ],
              "commonCycleActionMapping": []
            }
          ],
          "commonCycleActionMapping": []
        }
      ],
      "commonCycleActionMapping": [
        {
          "cycle": "c_dinner",
          "startDate": "2025-11-01T18:00:00+05:30",
          "endDate": "2025-11-30T23:00:00+05:30",
          "rulesetId": "DINNER_REWARD_RULES",
          "actions": [
            {
              "id": "temp_action_dinnerpts",
              "actionName": "AWARD_POINTS_ACTION",
              "actionClass": "com.capillary.shopbook.pointsengine.endpoint.impl.action.AwardPointsActionImpl",
              "description": "Award 150 Dinner Special Points",
              "mandatoryPropertiesValues": {
                "AwardStrategy": "111728",
                "ExpiryStrategy": "121544",
                "PointType": "Main"
              },
              "mandatoryComplexPropertiesValues": {},
              "embeddedStrategies": []
            }
          ]
        }
      ]
    }
  ],
  "limits": [],
  "liabilityOwnerSplitInfo": [],
  "workflowMetadata": {
    "optin": {},
    "enrolment": {}
  }
}'

Limits object

Specifies limits for issuance and redemption. This is an array, so you can define multiple limit objects.

FieldTypeRequiredDescription
entityScopeenumOptionalSpecifies the scope of the limit.
Supported values:
PROMOTION: The limit applies only to rewards generated by this specific promotion.
PROGRAM: The limit applies across the entire loyalty program, affecting rewards from all promotions sharing this program ID.
Default value: PROMOTION.
entityIdintegerConditionalThe numeric ID of the entity this limit applies to. Required when entityScope is PROGRAM — set this to the programId. Not required when entityScope is PROMOTION.
granularityenumOptionalSpecifies the tracking level for the limit.
Supported values:
OVERALL: Limits the total count/sum across all members combined.
USER: Limits the count/sum per individual member.
PER_ACTIVITY: Limits the count/sum based on each individual triggering activity instance.
actionTypeenumOptionalSpecifies the category of action to cap. Restricts the limit to only apply when a specific type of action occurs.
Supported values:
AWARD_CURRENCY: Limits points or currency awards (e.g., cap total points a member can earn).
AWARD_BADGE: Limits badge awards.
ISSUE_COUPON: Limits coupon or voucher issuance.
ISSUE_REWARD: Limits reward issuance from the rewards catalog.
UPGRADE_TIER: Limits tier upgrade actions.
RENEW_TIER: Limits tier renewal actions.
DOWNGRADE_TIER: Limits tier downgrade actions.
actionSubTypeIdstringOptionalSpecifies the limit to a specific sub-type.
If actionType is AWARD_CURRENCY, this can be the currency name like "Points" or "Miles".
If actionType is ISSUE_COUPON, this can be the coupon series ID like "FALLSALE25".
limitTypeenumOptionalSpecifies the measure used for limiting.
Supported values:
SUM: Limits the total value accumulated. Example: maximum 1000 points total.
COUNT: Limits the total number of times an action can occur. Example: maximum 1 redemption.
limitValuenumberOptionalSpecifies the maximum allowable value for the limit. If limitType is COUNT, this is the max number of occurrences. If limitType is SUM, this is the max total value.
periodObjectConditionalDefines the reset window. Required when you need to set a reset interval for limits. See the period Object table below for full structure.

period object

Defines the time window and reset logic for a limit.

FieldTypeRequiredDescription
periodTypeenumOptionalDefines the reset style for the limit.
Supported values:
NON_PERIOD_BASED: The limit applies once for the entire promotion duration and never resets.
MOVING_WINDOW: Uses a rolling duration from the current moment. Example: "last 7 days".
FIXED_CALENDAR_WINDOW: Resets based on fixed calendar periods aligned to specific dates or boundaries. Supports three sub-modes: every X days from a reference date (periodUnit = DAYS), every week starting on a specified day (periodUnit = WEEKS + periodStartDay), or every calendar month (periodUnit = MONTHS).
FIXED_WINDOW: The limit is only active during the specific startDate and endDate defined below.
periodUnitenumOptionalSpecifies the unit of time if periodType is MOVING_WINDOW or FIXED_CALENDAR_WINDOW.
DAYS: Daily windows.
WEEKS: Weekly windows.
MONTHS: Monthly windows.
periodValueintegerOptionalSpecifies the number of period units in one complete limit period.
For MOVING_WINDOW: the window size. Example: 7 with DAYS means "last 7 days".
For FIXED_CALENDAR_WINDOW with DAYS: the chunk size in days from the reference date.
For FIXED_CALENDAR_WINDOW with WEEKS + periodStartDay: must be 1.
periodStartDayenumOptionalSpecifies which day the calendar week starts on. Only applicable when periodType is FIXED_CALENDAR_WINDOW and periodUnit is WEEKS. When specified, periodValue must be 1.
Supported values: SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY.
referenceDatestring (date-time)OptionalSpecifies the anchor date used to calculate fixed calendar periods when periodType is FIXED_CALENDAR_WINDOW and periodUnit is DAYS or MONTHS. All period boundaries are calculated relative to this date. Required for DAYS and MONTHS unless using periodStartDay for weekly periods.
Time format: ISO 8601 timestamp. Example: "2025-01-01T00:00:00Z"
startDatestring (date-time)OptionalFor periodType = FIXED_WINDOW: the exact date/time this limit becomes active. Not applicable for other period types. Time format: ISO 8601 timestamp with timezone offset. Example: 2025-12-01T00:00:00+05:30
endDatestring (date-time)OptionalFor periodType = FIXED_WINDOW: the exact date/time this limit becomes inactive. Must be null for FIXED_CALENDAR_WINDOW. Time format: ISO 8601 timestamp with timezone offset. Example: 2025-12-31T23:59:59+05:30

Example use case 1: applying the limits object to update a promotion

Fresh Juice Bar previously set up a "Buy 5 Get 1 Free" coupon promotion with a limit of 2 coupons per member per week. Now they need to update the promotion based on member feedback. They have decided to increase the redemption limit, allowing members to earn up to 3 coupons every week instead of just 2.

curl --location --request PUT 'https://eu.api.capillarytech.com/v3/promotions/{promotionid}' \
--header 'Content-Type: application/json' \
--header 'X-CAP-API-AUTH-ORG-ID: 100232' \
--header 'Authorization: Basic Z2VvcmdlLmRvY2RlbW86NjVhMDgzYjk1MWY5MGY1NTY5zUxYmJiY2U=' \
--header 'Cookie: _cfuvid=bw96IuxgkLuZy69HNs9OCYqtPCXTg6pMsX5zEo-1765521791434-0.0.1.1-604800000' \
--data '{
  "metadata": {
    "name": "Fresh Juice Weekly Coupon Limit4-UI2",
    "description": "Limit B5G1_JUICE coupon to 3 per user per 7 days. UPDATED LIMIT.",
    "orgId": 100737,
    "programId": 973,
    "startDate": "2025-12-01T00:00:00+05:30",
    "endDate": "2025-12-31T23:59:59+05:30",
    "promotionType": "GENERIC",
    "status": "DRAFT",
    "timezoneName": "Asia/Kolkata",
    "promoIdentifier": "juice-limit-dec25-ui",
    "promotionMetadata": []
  },
  "customerEnrolment": {
    "enrolmentMethod": "TRANSACTION",
    "audienceGroups": []
  },
  "activities": [
    {
      "id": "activity_1761xxxxxxa1",
      "type": "SINGLE",
      "name": "Issue B5G1 Coupon",
      "event": "TransactionAdd",
      "expJSON": "{\n  \"arity\": \"literal\",\n  \"type\": \"boolean:primitive\",\n  \"value\": \"true\"\n}",
      "commonCycleActionMapping": [
        {
          "cycle": "Cycle_1",
          "startDate": "2025-12-01T00:00:00+05:30",
          "endDate": "2025-12-31T23:59:59+05:30",
          "actions": [
            {
              "id": "temp_action_b5g1",
              "actionName": "PE_ISSUE_VOUCHER_ACTION",
              "actionClass": "com.capillary.shopbook.pointsengine.endpoint.impl.action.PointsEngineIssueVoucherActionImpl",
              "description": "Issue B5G1 Juice Voucher",
              "mandatoryPropertiesValues": {
                "VoucherSeriesId": "B5G1_JUICE"
              },
              "mandatoryComplexPropertiesValues": {},
              "embeddedStrategies": []
            }
          ]
        }
      ],
      "milestones": []
    }
  ],
  "limits": [
    {
      "granularity": "USER",
      "actionType": "AWARD_CURRENCY",
      "actionSubTypeId": "Points",
      "limitType": "COUNT",
      "limitValue": 3,
      "period": {
        "periodType": "MOVING_WINDOW",
        "periodUnit": "DAYS",
        "periodValue": 7
      }
    }
  ],
  "liabilityOwnerSplitInfo": [],
  "workflowMetadata": {
    "optin": {},
    "enrolment": {}
  }
}'

Example use case 2: applying the limits object to update a promotion

Global Gadgets previously launched a Q1 promotion with an overall liability cap of 1,000,000 points. Due to higher-than-expected member engagement, they are approaching this limit faster than anticipated. To ensure the promotion continues without interruption, they need to update the promotion to double the total allowable points to 2,000,000 within the same 30-day rolling window.

curl --location --request PUT 'https://eu.api.capillarytech.com/v3/promotions/{promotionid}' \
--header 'content-type: application/json' \
--header 'Authorization: Basic Z2VvcmdlLmRvY2RlbW86MmQ1OTNTk1OGE2NWI5ZjAxMzU5NGIwNDllZTk=' \
--header 'Cookie: _cfuvid=BC9cwq9u_qIzIGlUxLEwV1ldTNVbpH1faK4-1763285329155-0.0.1.1-604800000' \
--data '{
  "metadata": {
    "name": "Global Gadgets Points Promo w Overall Limit-UI",
    "description": "Overall promo limit: Max 2M points total awarded in any 30-day window. CAP INCREASED.",
    "orgId": 100737,
    "programId": 973,
    "startDate": "2026-02-01T00:00:00+05:30",
    "endDate": "2026-04-30T23:59:59+05:30",
    "promotionType": "LOYALTY_EARNING",
    "status": "DRAFT",
    "timezoneName": "Asia/Kolkata",
    "promoIdentifier": "gg-limit-roll-q126-ui",
    "promotionMetadata": []
  },
  "customerEnrolment": {
    "enrolmentMethod": "TRANSACTION",
    "audienceGroups": []
  },
  "activities": [
    {
      "id": "activity_1761xxxxxxa2",
      "type": "SINGLE",
      "name": "Award Points",
      "event": "TransactionAdd",
      "expJSON": "{\n  \"arity\": \"literal\",\n  \"type\": \"boolean:primitive\",\n  \"value\": \"true\"\n}",
      "commonCycleActionMapping": [
        {
          "cycle": "Cycle_1",
          "startDate": "2026-02-01T00:00:00+05:30",
          "endDate": "2026-04-30T23:59:59+05:30",
          "actions": [
            {
              "id": "temp_action_pts_lim",
              "actionName": "AWARD_POINTS_ACTION",
              "actionClass": "com.capillary.shopbook.pointsengine.endpoint.impl.action.AwardPointsActionImpl",
              "description": "Award Standard Points",
              "mandatoryPropertiesValues": {
                "AwardStrategy": "109010",
                "ExpiryStrategy": "109006",
                "PointType": "Main"
              },
              "mandatoryComplexPropertiesValues": {},
              "embeddedStrategies": []
            }
          ]
        }
      ],
      "milestones": []
    }
  ],
  "limits": [
    {
      "entityScope": "PROMOTION",
      "granularity": "OVERALL",
      "actionType": "AWARD_CURRENCY",
      "actionSubTypeId": "Points",
      "limitType": "SUM",
      "limitValue": 2000000,
      "period": {
        "periodType": "MOVING_WINDOW",
        "periodValue": 30,
        "periodUnit": "DAYS"
      }
    }
  ],
  "liabilityOwnerSplitInfo": [],
  "workflowMetadata": {
    "optin": {},
    "enrolment": {}
  }
}'

liabilityOwnerSplitInfo object

Defines how the reward liability (cost) is financially distributed. This is an array, so you can define multiple split objects.

FieldTypeRequiredDescription
orgIdintegerOptionalThe Organization ID of the entity bearing this portion of the cost. Supported Values: A valid integer orgId.
liabilityOwnerIdintegerOptionalThe specific ID of the entity that owns this liability.
If liabilityOwnerType is PROGRAM, this is the Program ID. If liabilityOwnerType is PARTNER, this is the Partner ID. Supported Values: A valid Program or Partner ID. Example: 229 (representing the "Diamond" program).
componentTypeenumOptionalSpecifies what this liability split is for.
PROMOTION: (Default) The cost split applies only to this specific promotion. PROGRAM: The split applies to the entire program.
rationumberOptionalThe percentage (0-100) of the total liability this owner will cover. The sum of all ratio values in the liabilityOwnerSplitInfo array should equal 100. Supported Values: Number between 0 and 100. Example: 50 (for 50%).
liabilityOwnerTypeenumOptionalSpecifies if the owner is an internal program or an external partner.
PROGRAM: Liability is assigned to one of your own loyalty programs. PARTNER: Liability is assigned to an external partner entity.
activebooleanOptionalIndicates if this specific split rule is active. If set to false, this split entry is ignored. Supported Values: true, false. Example: true

Example use case 1: applying the liabilityOwnerSplitInfo object to update a promotion

The brand previously configured a promotion with a 50/50 liability split between two programs. Now they need to update this financial arrangement based on a new budget agreement. Management has decided that the main program (ID 82) should bear a larger share of the cost. They are updating the split ratio to 70% for the main program and reducing the transfer program's share (ID 87) to 30%.

curl --location --request PUT 'https://eu.api.capillarytech.com/v3/promotions/{promotionid}' \
--header 'content-type: application/json' \
--header 'Authorization: Basic Z2VvcmdlLmRvY2RlbW86MmQ1OTNhMjI2MTk1OGE2NWI5ZjAxMzU5NGIwNDllZTk=' \
--header 'Cookie: _cfuvid=BC9cwq9xQ4RRsN9u_qIzIGlUxLEwV1ldTNVbpH1faK4-1763285329155-0.0.1.1-604800000' \
--data '{
  "metadata": {
    "name": "Liability Split - DocDemo and DocTransfer",
    "description": "UPDATED: Split liability adjusted to 70/30 (Main/Transfer).",
    "orgId": 100737,
    "programId": 973,
    "startDate": "2025-11-20T00:00:00+05:30",
    "endDate": "2025-12-31T23:59:59+05:30",
    "promotionType": "LOYALTY_EARNING",
    "status": "DRAFT",
    "timezoneName": "Asia/Kolkata",
    "promoIdentifier": "liability-split-progprog-ui-v2",
    "promotionMetadata": []
  },
  "customerEnrolment": {
    "enrolmentMethod": "TRANSACTION",
    "audienceGroups": []
  },
  "activities": [
    {
      "id": "activity_1761xxxxxxb1",
      "type": "SINGLE",
      "name": "Award Points",
      "event": "TransactionAdd",
      "expJSON": "{\n  \"arity\": \"literal\",\n  \"type\": \"boolean:primitive\",\n  \"value\": \"true\"\n}",
      "commonCycleActionMapping": [
        {
          "cycle": "Cycle_1",
          "startDate": "2025-11-20T00:00:00+05:30",
          "endDate": "2025-12-31T23:59:59+05:30",
          "actions": [
            {
              "id": "temp_action_pts_split",
              "actionName": "AWARD_POINTS_ACTION",
              "actionClass": "com.capillary.shopbook.pointsengine.endpoint.impl.action.AwardPointsActionImpl",
              "description": "100 Points",
              "mandatoryPropertiesValues": {
                "AwardStrategy": "109010",
                "ExpiryStrategy": "109436",
                "PointType": "Main"
              },
              "mandatoryComplexPropertiesValues": {},
              "embeddedStrategies": []
            }
          ]
        }
      ],
      "milestones": []
    }
  ],
  "limits": [],
  "liabilityOwnerSplitInfo": [
    {
      "liabilityOwnerId": 82,
      "componentType": "PROMOTION",
      "ratio": 70,
      "isActive": true,
      "liabilityOwnerName": "DocDemoDefaultProgram",
      "liabilityOwnerType": "PROGRAM",
      "orgId": 100737
    },
    {
      "liabilityOwnerId": 87,
      "componentType": "PROMOTION",
      "ratio": 30,
      "isActive": true,
      "liabilityOwnerName": "DocTransferPoints",
      "liabilityOwnerType": "PROGRAM",
      "orgId": 100737
    }
  ],
  "workflowMetadata": {
    "optin": {},
    "enrolment": {}
  }
}'

Example use case 2: applying the liabilityOwnerSplitInfo object to update a promotion

Digital Hub previously launched a promotion where their main loyalty program covered 100% of the costs. Now they need to update this configuration because they have secured a sponsorship deal. An external partner, "TechGiant Partners" (Partner ID 505), has agreed to subsidize the promotion.

To reflect this new financial agreement, the brand needs to update the liability split: the main program will now cover only 60% of the cost, while the new partner will cover the remaining 40%.

curl --location --request PUT 'https://eu.api.capillarytech.com/v3/promotions/{promotionid}' \
--header 'Content-Type: application/json' \
--header 'X-CAP-API-AUTH-ORG-ID: 100232' \
--header 'Authorization: Basic Z2VvcmdlLmRvY2RlbW86NjVhMDgzYjk1MWY5NDk1YmNkNzUxYmJiY2U=' \
--header 'Cookie: _cfuvid=bw96IuxgkLuZy69HCXTg6pMsX5zEo-1765521791434-0.0.1.1-604800000' \
--data '{
  "metadata": {
    "name": "Digital Hub Standard 100 Liability2",
    "description": "UPDATED: Liability split 60% Program / 40% Partner (TechGiant).",
    "orgId": 100737,
    "programId": 973,
    "startDate": "2025-12-01T00:00:00+05:30",
    "endDate": "2025-12-31T23:59:59+05:30",
    "promotionType": "GENERIC",
    "status": "DRAFT",
    "timezoneName": "Asia/Kolkata",
    "promoIdentifier": "dh-std-liab-dec25-ui",
    "promotionMetadata": []
  },
  "customerEnrolment": {
    "enrolmentMethod": "TRANSACTION",
    "audienceGroups": []
  },
  "activities": [
    {
      "id": "activity_1763288000001",
      "name": "Activity 1",
      "type": "SINGLE",
      "event": "TransactionAdd",
      "expJSON": "{\n  \"arity\": \"literal\",\n  \"type\": \"boolean:primitive\",\n  \"value\": \"true\"\n}",
      "commonCycleActionMapping": [
        {
          "cycle": "Cycle_1",
          "startDate": "2025-12-01T00:00:00+05:30",
          "endDate": "2025-12-31T23:59:59+05:30",
          "actions": []
        }
      ],
      "milestones": []
    }
  ],
  "limits": [],
  "liabilityOwnerSplitInfo": [
    {
      "liabilityOwnerId": 82,
      "componentType": "PROMOTION",
      "ratio": 60,
      "isActive": true,
      "liabilityOwnerName": "DocDemoDefaultProgram",
      "liabilityOwnerType": "PROGRAM",
      "orgId": 100737
    },
    {
      "liabilityOwnerId": 505,
      "componentType": "PROMOTION",
      "ratio": 40,
      "isActive": true,
      "liabilityOwnerName": "TechGiant Partners",
      "liabilityOwnerType": "PARTNER",
      "orgId": 100737
    }
  ],
  "workflowMetadata": {
    "optin": {},
    "enrolment": {}
  }
}'

Example response

{
    "data": {
        "metadata": {
            "name": "Efd Activity Based Prom11",
            "description": "Enrolment for all members, requires activity-based opt-in.",
            "programId": "HiddenGemDefaultProgram_ID",
            "orgId": 51174,
            "startDate": "2025-11-27T05:30:00+05:30",
            "endDate": "2025-12-28T05:29:59+05:30",
            "timezoneName": "Asia/Kolkata",
            "promotionType": "LOYALTY",
            "status": "DRAFT",
            "promoIdentifier": null,
            "promotionId": 0,
            "createdOn": "2025-10-21T20:43:08+05:30",
            "lastModifiedOn": "2025-10-21T20:43:08+05:30",
            "createdBy": 0,
            "lastModifiedBy": 0,
            "version": null,
            "draftDetails": null,
            "promotionMetadata": null,
            "commonStrategies": null
        },
        "customerEnrolment": {
            "enrolmentMethod": "TRANSACTION"
        },
        "activities": [
            {
                "type": "SINGLE",
                "id": "activity_reward_trigger",
                "name": "Main Reward Activity",
                "commonCycleActionMapping": [
                    {
                        "cycle": "cycle_promo_period",
                        "actions": [
                            {
                                "id": "action_award_points",
                                "actionName": "AWARD_CURRENCY",
                                "actionClass": "com.capillary.loyalty.engine.actor.promotion.actions.AwardCurrency",
                                "mandatoryPropertiesValues": {
                                    "POINTS_TO_AWARD": "100",
                                    "CURRENCY_IDENTIFIER": "POINTS"
                                }
                            }
                        ],
                        "startDate": "2025-11-27",
                        "endDate": "2025-12-27"
                    }
                ],
                "event": "TransactionAdd",
                "allCycles": []
            }
        ],
        "comments": null,
        "parentId": null,
        "version": 1,
        "limits": [],
        "liabilityOwnerSplitInfo": [],
        "id": "68f7a304fd5b970d1efaf626",
        "workflowMetadata": {
            "enrolment": {
                "basedOn": null,
                "activities": null,
                "audienceMapping": null,
                "restrictions": null
            },
            "optin": {
                "basedOn": "ACTIVITY",
                "audienceMapping": null,
                "activities": [
                    {
                        "type": "SINGLE",
                        "id": "activity_optin_trigger",
                        "name": "Opt-in Trigger Activity",
                        "event": "TransactionAdd",
                        "allCycles": []
                    }
                ],
                "restrictions": null
            }
        }
    },
    "errors": null,
    "warnings": null
}

Response body parameters

FieldTypeDescription
dataObjectThe main data object for the promotion.
.idStringSpecifies the unique system-generated identifier for the unified promotion.
.metadataObjectDefines the object containing all metadata for the promotion.
..nameStringSpecifies the name of the promotion.
..descriptionStringSpecifies the description of the promotion.
..programIdStringSpecifies the program ID associated with the promotion.
..orgIdInteger (int64)Specifies the organization ID.
..startDateString (date-time)Indicates the promotion's start date in ISO 8601 format, returned in the server time zone. EU server example 2025-12-16T14:30:45Z → 16 December 2025, 14:30:45 (UTC) India server example 2025-12-16T14:30:45+05:30 → 16 December 2025, 14:30:45 (IST) Note: The response time zone always matches the server time zone, regardless of the time zone offset in the request.
..endDateString (date-time)Indicates the promotion's end date in ISO 8601 format, returned in the server time zone. EU server example 2025-12-16T14:30:45Z → 16 December 2025, 14:30:45 (UTC) India server example 2025-12-16T14:30:45+05:30 → 16 December 2025, 14:30:45 (IST) Note: The response time zone always matches the server time zone, regardless of the time zone offset in the request.
..timezoneNameStringSpecifies the timezone name for the promotion's schedule.
..promotionTypeStringSpecifies the type of promotion.
..statusStringIndicates the current status of the promotion.
..promoIdentifierStringSpecifies a unique string identifier for the promotion.
..promotionIdInteger (int32)Specifies the legacy numerical promotion ID.
..createdOnString (date-time)Indicates the creation date and time of the promotion in ISO 8601 format, returned in the server time zone. EU server example 2025-12-16T14:30:45Z → 16 December 2025, 14:30:45 (UTC) India server example 2025-12-16T14:30:45+05:30 → 16 December 2025, 14:30:45 (IST) Note: The response time zone always matches the server time zone, regardless of the time zone offset in the request.
..lastModifiedOnString (date-time)Indicates the last modification timestamp in ISO 8601 format, returned in the server time zone. EU server example 2025-12-16T14:30:45Z → 16 December 2025, 14:30:45 (UTC) India server example 2025-12-16T14:30:45+05:30 → 16 December 2025, 14:30:45 (IST) Note: The response time zone always matches the server time zone, regardless of the time zone offset in the request.
..createdByInteger (int32)Specifies the user ID of the creator.
..lastModifiedByInteger (int32)Specifies the user ID of the last modifier.
..versionStringSpecifies the version string of the promotion.
..draftDetailsObjectDefines details if the promotion is a draft.
...idStringSpecifies the draft's unique ID.
...statusStringSpecifies the draft's status.
...versionInteger (int32)Specifies the draft version number.
...lastModifiedByInteger (int32)Specifies the user who last modified the draft.
...lastModifiedOnString (date-time)Indicates the last modification timestamp of the draft in ISO 8601 format, returned in the server time zone. EU server example 2025-12-16T14:30:45Z → 16 December 2025, 14:30:45 (UTC) India server example 2025-12-16T14:30:45+05:30 → 16 December 2025, 14:30:45 (IST) Note: The response time zone always matches the server time zone, regardless of the time zone offset in the request.
..promotionMetadataArray (Object)Defines a list of custom key-value metadata pairs (PromotionMetadata).
...isBrandDefinedStringSpecifies if the metadata is defined by the brand.
...keyStringSpecifies the metadata key.
...valueStringSpecifies the metadata value.
..commonStrategiesObjectDefines common strategies, like expiry.
...expiryArray (Object)Defines a list of expiry strategies.
....strategyTypeIdInteger (int32)Specifies the strategy type ID.
....propertyValuesStringSpecifies the property values for the strategy.
....ownerStringSpecifies the owner of the strategy.
....updatedViaNewUIBooleanIndicates if the strategy was updated via the new UI.
....useCommonExpiryStrategyBooleanIndicates if the common expiry strategy is used.
....strategyRefStringSpecifies the strategy reference.
....strategySubTypeStringIndicates the strategy sub-type.
..promotionModeStringIndicates the promotion mode.
.customerEnrolmentObjectDefines the member enrolment rules.
..enrolmentMethodStringIndicates the method of customer enrolment.
..audienceGroupsArray (Object)Defines a list of audience groups for enrolment (AudienceGroupDetails).
...audienceGroupIdInteger (int64)Specifies the unique ID for the audience group.
...audienceGroupNameStringSpecifies the name of the audience group.
...descriptionStringSpecifies the description of the audience group.
.activitiesArray (Object)Defines a list of activities.
..idStringSpecifies the unique ID for the activity.
..typeStringSpecifies the type of the activity.
..nameStringSpecifies the name of the activity.
..parentIdStringSpecifies the parent activity's ID.
..rulesetIdStringSpecifies the ruleset ID for the activity.
..cyclesArray (Object)Defines a list of general cycles.
...idstringSpecifies the cycle ID.
...namestringSpecifies the cycle name.
...startDatestring (date-time)Indicates the cycle's start date in ISO 8601 format, returned in the server time zone. EU server example 2025-12-16T14:30:45Z → 16 December 2025, 14:30:45 (UTC) India server example 2025-12-16T14:30:45+05:30 → 16 December 2025, 14:30:45 (IST) Note: The response time zone always matches the server time zone, regardless of the time zone offset in the request.
...endDatestring (date-time)Indicates the cycle's end date in ISO 8601 format, returned in the server time zone. EU server example 2025-12-16T14:30:45Z → 16 December 2025, 14:30:45 (UTC) India server example 2025-12-16T14:30:45+05:30 → 16 December 2025, 14:30:45 (IST) Note: The response time zone always matches the server time zone, regardless of the time zone offset in the request.
..commonCycleActionMappingArray (Object)Defines action mappings for common cycles.
...cycleStringSpecifies the cycle name/ID.
...defaultValueNumber (double)Specifies the default value for the action.
...startDateString (date-time)Indicates the cycle mapping's start date in ISO 8601 format, returned in the server time zone. EU server example 2025-12-16T14:30:45Z → 16 December 2025, 14:30:45 (UTC) India server example 2025-12-16T14:30:45+05:30 → 16 December 2025, 14:30:45 (IST) Note: The response time zone always matches the server time zone, regardless of the time zone offset in the request.
...endDateString (date-time)Indicates the cycle mapping's end date in ISO 8601 format, returned in the server time zone. EU server example 2025-12-16T14:30:45Z → 16 December 2025, 14:30:45 (UTC) India server example 2025-12-16T14:30:45+05:30 → 16 December 2025, 14:30:45 (IST) Note: The response time zone always matches the server time zone, regardless of the time zone offset in the request.
...rulesetIdStringSpecifies the cycle mapping ruleset ID.
...actionsArray (Object)Defines a list of actions.
....idStringSpecifies the action ID.
....actionNameStringSpecifies the action name.
....actionClassStringSpecifies the action class.
....descriptionStringSpecifies the action description.
....mandatoryPropertiesValuesObjectDefines key-value pairs for mandatory properties.
....mandatoryComplexPropertiesValuesObjectDefines key-value pairs for mandatory complex properties.
....embeddedStrategiesArray (Object)Defines a list of embedded StrategyInfo objects.
..eventStringSpecifies the event associated with the activity.
..ruleExpressionStringSpecifies the rule expression.
..expJSONStringSpecifies the rule expression in JSON format.
..frequencyTypeStringSpecifies the frequency type.
..targetEvaluationTypeStringSpecifies the target evaluation type.
..targetCycleStartDateString (date-time)Indicates the target cycle's start date in ISO 8601 format, returned in the server time zone. EU server example 2025-12-16T14:30:45Z → 16 December 2025, 14:30:45 (UTC) India server example 2025-12-16T14:30:45+05:30 → 16 December 2025, 14:30:45 (IST) Note: The response time zone always matches the server time zone, regardless of the time zone offset in the request.
..targetCycleEndDateString (date-time)Indicates the target cycle's end date in ISO 8601 format, returned in the server time zone. EU server example 2025-12-16T14:30:45Z → 16 December 2025, 14:30:45 (UTC) India server example 2025-12-16T14:30:45+05:30 → 16 December 2025, 14:30:45 (IST) Note: The response time zone always matches the server time zone, regardless of the time zone offset in the request.
..activityCyclesArray (Object)Defines activity-specific cycles.
...idstringSpecifies the activity cycle ID.
...namestringSpecifies the activity cycle name.
...startDatestringIndicates the activity cycle's start date in ISO 8601 format, returned in the server time zone. EU server example 2025-12-16T14:30:45Z → 16 December 2025, 14:30:45 (UTC) India server example 2025-12-16T14:30:45+05:30 → 16 December 2025, 14:30:45 (IST) Note: The response time zone always matches the server time zone, regardless of the time zone offset in the request.
...endDatestringIndicates the activity cycle's end date in ISO 8601 format, returned in the server time zone. EU server example 2025-12-16T14:30:45Z → 16 December 2025, 14:30:45 (UTC) India server example 2025-12-16T14:30:45+05:30 → 16 December 2025, 14:30:45 (IST) Note: The response time zone always matches the server time zone, regardless of the time zone offset in the request.
...refCodestringSpecifies the activity cycle reference code.
...isActivebooleanIndicates if the activity cycle is active.
..milestonesArray (Object)Defines milestones.
...namestringSpecifies the milestone name.
...sameActionsForEveryCyclebooleanIndicates if the same actions apply across all cycles.
...differentTargetsForEveryCyclebooleanIndicates if target values differ across cycles.
...descriptionstringIndicates the milestone description.
...trackingTypestringSpecifies how the milestone is tracked.
...targetTypeenumDefines the KPI to track.
...targetEntityenumSpecifies the entity to track against.
...defaultValuestringSpecifies the target value threshold.
...targetValuestringSpecifies the target value to achieve.
...preferredTillIdinteger (int64)Indicates a preferred till ID.
...frequencyTypestringSpecifies the frequency type for evaluation.
...targetEvaluationTypeenumSpecifies the evaluation logic window.
...recurringCyclesinteger (int32)Specifies the number of recurring cycles.
...targetGroupIdinteger (int64)Indicates the target group ID.
...leaderboardEnabledbooleanIndicates if a leaderboard is enabled.
...cycleActionMappingArray (Object)Specifies actions associated with milestone cycles.
...streaksArray (Object)Defines streak configuration (StreakConfig).
....namestringSpecifies the streak name.
....targetCountOfSequenceinteger (int32)Specifies the required count to achieve the streak.
....consecutivebooleanIndicates if the sequence must be consecutive.
...aggregateFunctionstringSpecifies the function to aggregate values.
...cyclesArray (Object)Defines the evaluation cycles specific to this milestone.
...periodsArray (Object)Defines the periods for the milestone. .
...targetRuleIdsArray (integer int64)Indicates associated target rule IDs.
...individualMilestoneFileDetailsArray (Object)Details related to uploaded files for individual targets.
....uploadFileNamestringThe original name of the uploaded file.
....fileUrlstringThe system URL where the file is stored.
....statusstringThe processing status of the file.
....totalRecordsintegerThe total number of records found in the file.
....successRecordsintegerThe number of records successfully processed.
....failureCountintegerThe number of records that failed to process.
..allCyclesArray (Object)Defines a list of all cycles.
..combinationTypeStringIndicates how child activities are combined.
..childrenArray (Object)Defines a array of objects of child GroupActivity or SingleActivity objects.
.commentsStringSpecifies any comments on the promotion.
.parentIdStringSpecifies the ID of the parent promotion.
.parentDetailsObjectDefines details of the parent promotion.
..idStringSpecifies the parent promotion ID.
..statusStringSpecifies the parent promotion status.
..versionInteger (int32)Specifies the parent promotion version.
..lastModifiedByInteger (int32)Specifies the user who last modified the parent.
..lastModifiedOnString (date-time)Indicates the parent's last modification timestamp in ISO 8601 format, returned in the server time zone. EU server example 2025-12-16T14:30:45Z → 16 December 2025, 14:30:45 (UTC) India server example 2025-12-16T14:30:45+05:30 → 16 December 2025, 14:30:45 (IST) Note: The response time zone always matches the server time zone, regardless of the time zone offset in the request.
.versionInteger (int32)Specifies the version number of the promotion.
.limitsArray (Object)Defines a list of limits for the promotion (LimitResponse).
..idInteger (int64)Specifies the limit ID (system-assigned).
..entityScopeStringIndicates the scope of the limit.
..granularityStringIndicates the granularity of the limit.
..entityIdInteger (int64)Specifies the entity ID the limit applies to.
..actionTypeStringIndicates the action type being limited.
..actionSubTypeIdStringSpecifies the action sub-type ID.
..limitTypeStringIndicates the type of limit
..limitValueNumberSpecifies the value of the limit.
..periodObjectDefines the time period for the limit.
...idInteger (int64)Specifies the period ID.
...periodTypeStringIndicates the type of period.
...periodUnitStringIndicates the unit for the period.
...periodValueInteger (int32)Specifies the value for the period.
...periodStartDayStringIndicates the start day for weekly periods.
...startDateString (date-time)Indicates the period's start date in ISO 8601 format, returned in the server time zone. EU server example 2025-12-16T14:30:45Z → 16 December 2025, 14:30:45 (UTC) India server example 2025-12-16T14:30:45+05:30 → 16 December 2025, 14:30:45 (IST) Note: The response time zone always matches the server time zone, regardless of the time zone offset in the request.
...endDateString (date-time)Indicates the period's end date in ISO 8601 format, returned in the server time zone. EU server example 2025-12-16T14:30:45Z → 16 December 2025, 14:30:45 (UTC) India server example 2025-12-16T14:30:45+05:30 → 16 December 2025, 14:30:45 (IST) Note: The response time zone always matches the server time zone, regardless of the time zone offset in the request.
..createdOnString (date-time)Indicates the creation timestamp of the limit in ISO 8601 format, returned in the server time zone. EU server example 2025-12-16T14:30:45Z → 16 December 2025, 14:30:45 (UTC) India server example 2025-12-16T14:30:45+05:30 → 16 December 2025, 14:30:45 (IST) Note: The response time zone always matches the server time zone, regardless of the time zone offset in the request.
..createdByInteger (int64)Specifies the user ID of the limit creator.
..lastUpdatedOnString (date-time)Indicates the last update timestamp of the promotion in ISO 8601 format, returned in the server time zone. EU server example 2025-12-16T14:30:45Z → 16 December 2025, 14:30:45 (UTC) India server example 2025-12-16T14:30:45+05:30 → 16 December 2025, 14:30:45 (IST) Note: The response time zone always matches the server time zone, regardless of the time zone offset in the request.
..lastUpdatedByInteger (int64)Specifies the user ID of the last user who updated the promotion.
..activeBooleanIndicates if the limit is active.
.liabilityOwnerSplitInfoArray (Object)Defines how liability is split.
..orgIdInteger (int32)Specifies the organization ID.
..liabilityOwnerIdInteger (int32)Specifies the liability owner ID.
..componentIdInteger (int32)Specifies the component ID.
..componentTypeStringIndicates the component type
..createdByInteger (int32)Specifies the user ID of the creator.
..ratioNumber (double)Specifies the liability split ratio.
..liabilityOwnerNameStringSpecifies the liability owner's name.
..liabilityOwnerTypeStringIndicates the liability owner's type.
..activeBooleanIndicates if the split info is active.
.workflowMetadataObjectDefines workflow metadata for enrolment and opt-in.
..enrolmentObjectDefines enrolment workflow details.
...basedOnStringIndicates the basis for enrolment.
...activitiesArray (Object)Defines activities triggering enrolment.
...audienceMappingArray (Object)Defines audience mappings for enrolment.
....groupIdInteger (int64)Specifies the group ID.
....groupNameStringSpecifies the group name.
...restrictionsObjectDefines enrolment restrictions.
....optinExpiryBasedOnObjectDefines opt-in expiry (PromotionExpiryConfigResponse).
.....valueInteger (int32)Specifies the expiry value.
.....expiryDateString (date-time)Indicates the opt-in expiry date in ISO 8601 format, returned in the server time zone. EU server example 2025-12-16T14:30:45Z → 16 December 2025, 14:30:45 (UTC) India server example 2025-12-16T14:30:45+05:30 → 16 December 2025, 14:30:45 (IST) Note: The response time zone always matches the server time zone, regardless of the time zone offset in the request.
.....typeStringSpecifies the expiry type.
....enrolmentExpiryBasedOnObjectDefines enrolment expiry.
.....valueInteger (int32)Specifies the expiry value.
.....expiryDateString (date-time)Indicates the enrolment expiry date in ISO 8601 format, returned in the server time zone. EU server example 2025-12-16T14:30:45Z → 16 December 2025, 14:30:45 (UTC) India server example 2025-12-16T14:30:45+05:30 → 16 December 2025, 14:30:45 (IST) Note: The response time zone always matches the server time zone, regardless of the time zone offset in the request.
.....typeStringSpecifies the expiry type.
....optinLimitPerCustomerObjectDefines opt-in limit.
.....valueInteger (int32)Specifies the limit value.
.....typeStringSpecifies the limit period type.
.....periodTypeStringSpecifies the window type.
.....periodUnitStringSpecifies the period unit.
....maxRedemptionsPerEarnPerCustomerObjectDefines redemption limit.
.....valueInteger (int32)Specifies the limit value.
.....typeStringSpecifies the limit period type.
.....periodTypeStringSpecifies the window type.
.....periodUnitStringSpecifies the period unit.
....enrolmentLimitPerPromotionObjectDefines total enrolment limit.
.....valueInteger (int32)Specifies the limit value.
.....typeStringSpecifies the limit period type.
.....periodTypeStringSpecifies the window type.
.....periodUnitStringSpecifies the period unit.
....enrolmentLimitPerCustomerObjectDefines enrolment limit per member.
.....valueInteger (int32)Specifies the limit value.
.....typeStringSpecifies the limit period type.
.....periodTypeStringSpecifies the window type.
.....periodUnitStringSpecifies the period unit.
....maxPointsPerEarnPerCustomerObjectDefines max points per earn limit.
.....valueInteger (int32)Specifies the limit value.
.....typeStringSpecifies the limit period type.
.....periodTypeStringSpecifies the window type.
.....periodUnitStringSpecifies the period unit.
..optinObjectDefines opt-in workflow details.
...basedOnStringIndicates the basis for opt-in.
...activitiesArray (Object)Defines activities triggering opt-in.
...audienceMappingArray (Object)Defines audience mappings for opt-in.
....groupIdInteger (int64)Specifies the group ID.
....groupNameStringSpecifies the group name.
...restrictionsObjectDefines opt-in restrictions.
....optinExpiryBasedOnObjectDefines opt-in expiry.
.....valueInteger (int32)Specifies the expiry value.
.....expiryDateString (date-time)Indicates the opt-in expiry date in ISO 8601 format, returned in the server time zone. EU server example 2025-12-16T14:30:45Z → 16 December 2025, 14:30:45 (UTC) India server example 2025-12-16T14:30:45+05:30 → 16 December 2025, 14:30:45 (IST) Note: The response time zone always matches the server time zone, regardless of the time zone offset in the request.
.....typeStringSpecifies the expiry type.
....enrolmentExpiryBasedOnObjectDefines enrolment expiry.
.....valueInteger (int32)Specifies the expiry value.
.....expiryDateString (date-time)Indicates the enrolment expiry date in ISO 8601 format, returned in the server time zone. EU server example 2025-12-16T14:30:45Z → 16 December 2025, 14:30:45 (UTC) India server example 2025-12-16T14:30:45+05:30 → 16 December 2025, 14:30:45 (IST) Note: The response time zone always matches the server time zone, regardless of the time zone offset in the request.
.....typeStringSpecifies the expiry type.
....optinLimitPerCustomerObjectDefines opt-in limit.
.....valueInteger (int32)Specifies the limit value.
.....typeStringSpecifies the limit period type.
.....periodTypeStringSpecifies the window type.
.....periodUnitStringSpecifies the period unit.
....maxRedemptionsPerEarnPerCustomerObjectDefines redemption limit.
.....valueInteger (int32)Specifies the limit value.
.....typeStringSpecifies the limit period type.
.....periodTypeStringSpecifies the window type.
.....periodUnitStringSpecifies the period unit.
....enrolmentLimitPerPromotionObjectDefines total enrolment limit.
.....valueInteger (int32)Specifies the limit value.
.....typeStringSpecifies the limit period type.
.....periodTypeStringSpecifies the window type.
.....periodUnitStringSpecifies the period unit.
....enrolmentLimitPerCustomerObjectDefines enrolment limit per member.
.....valueInteger (int32)Specifies the limit value.
.....typeStringSpecifies the limit period type.
.....periodTypeStringSpecifies the window type.
.....periodUnitStringSpecifies the period unit.
....maxPointsPerEarnPerCustomerObjectDefines max points per earn limit.
.....valueInteger (int32)Specifies the limit value.
.....typeStringSpecifies the limit period type.
.....periodTypeStringSpecifies the window type.
.....periodUnitStringSpecifies the period unit.
.communicationApprovalStatusObjectDefines the status of communication approval.
..successBooleanIndicates if the approval status check was successful.
..messageStringSpecifies the status message.
..responseObjectDefines the array of objects of response.
..statusCodeInteger (int32)Specifies the status code.
errorsArray (Object)Defines a list of errors that occurred, if any.
.codeInteger (int64)Specifies the error code.
.messageStringSpecifies the error message.
warningsArray (Object)Defines a list of warnings, if any.
.messageStringSpecifies the warning message.

Warning messages

Warning messageDescription
Unable to enroll in promotions. Customer already enrolled or limit reached.The customer is already enrolled in the promotion or the maximum enrollment limit has been reached.
Partial Success: Milestone enrollment failed.The main promotion enrollment was successful, but one or more milestone enrollments failed.
Unable to enroll in promotions. No Response from service.The systems processed the request, but a downstream service (EMF) did not return a confirmation, leading to an uncertain state.

Error codes

Error CodeDescription
9013Request not found. Use a valid promotion_id and ensure the promotion exists.
310152Promotion name not editable. Promotion name cannot be modified when the promotion is in a STOPPED state.
310153Start date not editable. The startDate cannot be modified once a promotion has gone live.
310155Invalid end date for live promotion. The endDate can only be extended into the future or reduced to the current time for active promotions.
310158Program field immutable. The programId associated with the promotion cannot be changed after creation.
310159Promotion identifier exists. The promoIdentifier must be unique across the organization.
310161Activity event immutable. The triggering event for a SingleActivity cannot be changed once created.
310162Milestone KPI immutable. The targetType for a published milestone cannot be modified.
310174Edits blocked during approval. No modifications are allowed while a promotion is in PENDING_APPROVAL status.
310175Status immutable. Promotion status cannot be changed via the update endpoint; use the review or status-specific actions instead.
310183No edits allowed when stopped. Comprehensive block on all modifications for promotions in a STOPPED state.
310186Version immutable. The promotion version is managed by the system and cannot be manually updated.
310187Promotion name exists. Another promotion with the same name already exists in the organization.
310190Eligibility type not editable. Customer eligibility settings cannot be changed once the promotion is live.
Path Params
string
required
Body Params

Request body

string
integer
integer
boolean
string
string
condition
object
action
object
integer
integer
integer
integer
integer
integer
string
promotionRestrictions
object
boolean
Headers
string
string
string
Response

Language
URL
LoadingLoading…
Response
Click Try It! to start a request and see the response here! Or choose an example:
application/json