---
title: "Create a Loyalty Promotion"
method: POST
path: "/v3/promotions"
---

# Create a Loyalty Promotion

`POST /v3/promotions`

Endpoint for POST /v3/promotions

## Headers

- `cookie` string
- `x-cap-remote-user` string

## Request body

- object — Request body as an object
  - `metadata` object
    - `name` string — The display name of the promotion. Required. Maximum 255 characters.
    - `description` string — A detailed description of the promotion. Maximum 1000 characters.
    - `programId` integer — The numeric ID of the loyalty program this promotion belongs to.
    - `startDate` string, date-time — The date and time when the promotion becomes active. Required. ISO 8601 format with timezone offset (e.g., 2026-01-01T00:00:00Z or 2026-01-01T05:30:00+05:30).
    - `endDate` string, date-time — The date and time when the promotion ends. Required. ISO 8601 format with timezone offset.
    - `promotionType` 'GENERIC' | 'LOYALTY' | 'LOYALTY_EARNING' — The type of promotion. Determines the enrollment and earning model in combination with enrolmentMethod and loyaltyEarningType. GENERIC: Neither opt-in nor enrollment is active. The promotion is triggered automatically by customer transactions. Use with enrolmentMethod: TRANSACTION and loyaltyEarningType: null. LOYALTY: Enrollment is active, opt-in is disabled. Focuses on loyalty membership status rather than a specific earning mechanism. Use with enrolmentMethod: IMPORT and loyaltyEarningType: null. LOYALTY_EARNING with loyaltyEarningType ISSUE_AND_EARN: Both enrollment and opt-in are active. Customers sign up (enroll) and then earn rewards upon qualifying events. Use with enrolmentMethod: IMPORT. LOYALTY_EARNING with loyaltyEarningType DIRECT_EARN: Opt-in is active, enrollment is disabled. Customers earn rewards directly through participation without a formal enrollment step. Use with enrolmentMethod: IMPORT.
    - `status` 'DRAFT' | 'ACTIVE' | 'PAUSED' | 'PENDING_APPROVAL' | 'STOPPED' | 'SNAPSHOT' | 'LIVE' | 'UPCOMING' | 'COMPLETED' | 'PUBLISH_FAILED' — The state of the promotion. Set to DRAFT on creation. The full status lifecycle is: DRAFT → PENDING_APPROVAL → ACTIVE. Other values (PAUSED, STOPPED, COMPLETED, PUBLISH_FAILED) are set by the system or via the review endpoint. Note: once a promotion becomes active, it is persisted with status `LIVE`, not `ACTIVE` — `ACTIVE` is only the request-time value used to activate a promotion. Both `status=ACTIVE` and `status=LIVE` work when searching promotions, but retrieving a LIVE promotion directly by ID (GET /v3/promotions/{id}) may return error 310191 ("Promotion not found") — use search-by-status instead if a direct ID lookup fails.
    - `timezoneName` string — The IANA timezone identifier used to evaluate all promotion dates and cycle boundaries (e.g., Asia/Kolkata, America/New_York, UTC).
    - `promoIdentifier` string — A unique human-readable identifier for this promotion. Maximum 255 characters.
    - `loyaltyConfigMetaData` object
      - `isStackable` boolean — When true, this promotion can be earned alongside other promotions in the same transaction. When false, it cannot be combined with others.
      - `isExclusive` boolean — When true, earning this promotion prevents any other promotion from being earned in the same transaction.
      - `isAlwaysApply` boolean — When true, the promotion is automatically applied to all qualifying events without requiring explicit enrollment.
      - `isConsideredForRanking` boolean — When true, this promotion participates in the promotion ranking evaluation that determines priority when multiple promotions compete for the same transaction.
    - `loyaltyEarningType` 'DIRECT_EARN' | 'ISSUE_AND_EARN', nullable — Defines the earning model. DIRECT_EARN: points are awarded immediately on the qualifying event. ISSUE_AND_EARN: points are held pending and earned after a subsequent redemption event. Set to null for default earning behavior.
    - `promotionMetadata` string[] — A list of custom key-value metadata entries for the promotion. Each entry is an object with key (string), value (string), and isBrandDefined (string) fields. Leave as an empty array if no custom metadata is needed.
  - `customerEnrolment` object
    - `enrolmentMethod` 'TRANSACTION' | 'IMPORT' | 'AUDIENCE_FILTER' — Defines how customers are enrolled in the promotion. TRANSACTION: auto-enrolled upon completing a qualifying transaction. IMPORT: enrolled via bulk data import. AUDIENCE_FILTER: enrolled by matching a defined audience segment.
  - `activities` object[]
    - `id` string — A unique identifier for this activity. Caller-supplied string (e.g., activity_1234567890 or group_main_goal).
    - `type` 'SINGLE' | 'GROUP' — The activity type. SINGLE: a standalone activity that tracks one event with optional milestones. GROUP: a container that groups multiple child activities using AND/OR logic defined by combinationType.
    - `name` string — The display name for this activity. Maximum 100 characters.
    - `event` string — The loyalty event that triggers this activity. Known values: TransactionAdd, VoucherRedemption, FriendReferred, TargetCompleted.
    - `expJSON` string — A JSON expression tree defining the eligibility condition for this activity. Each node contains arity (literal, binary_operation, object_dereference, name), type, value, and operands fields. Use {"arity":"literal","type":"boolean:primitive","value":"true"} to match all events with no additional filter.
    - `milestones` object[] — A list of milestones (targets) for this activity. A SINGLE activity supports at most one milestone. Omit or leave empty when rewards fire directly via commonCycleActionMapping without a target threshold.
      - `name` string — A unique name for the milestone. Recommended convention: {promotionName}~{activityName}~Milestone {id}.
      - `trackingType` 'DEFAULT' | 'CAPPING' | 'UNIFIED' | 'STREAKS' | 'NON_CONTINUOUS_STREAKS' — How progress is tracked for this milestone. DEFAULT: standard linear tracking. CAPPING: tracks up to a defined cap. UNIFIED: tracks progress across multiple activities together. STREAKS: tracks consecutive cycle completions. NON_CONTINUOUS_STREAKS: tracks non-consecutive streak completions.
      - `targetType` 'COUNT' | 'SALES' | 'GROSS_SALES' | 'QUANTITY' | 'VISIT' | 'REGULAR_POINTS' | 'PROMOTIONAL_POINTS' | 'ALL_POINTS' | 'EXTENDED_FIELD' | 'EVENT_ATTRIBUTE' — The metric being measured. COUNT: number of qualifying events. SALES: net transaction value. GROSS_SALES: gross transaction value. QUANTITY: item quantity. VISIT: number of store visits. REGULAR_POINTS: regular points earned. PROMOTIONAL_POINTS: promotional points earned. ALL_POINTS: all points combined. EXTENDED_FIELD: value from a transaction extended field. EVENT_ATTRIBUTE: value from a custom event attribute.
      - `targetEntity` 'TRANSACTION' | 'LINEITEM' | 'POINTS' | 'EVENT' | 'ALTERNATE_CURRENCIES' — The entity level at which the target is tracked. TRANSACTION: counts at the full transaction level. LINEITEM: counts at the individual line item level. POINTS: tracks point values. EVENT: counts custom events. ALTERNATE_CURRENCIES: tracks alternate loyalty currency values.
      - `defaultValue` string — The target value that must be reached to complete this milestone (e.g., '3' for 3 transactions, '1000' for 1000 in sales). Passed as a string even though it represents a number.
      - `targetEvaluationType` 'FIXED_CALENDAR_WINDOW' | 'CYCLIC_WINDOW' | 'PERIOD_AGNOSTIC_WINDOW' | 'CALENDAR_CYCLIC_WINDOW' — The window type used to evaluate this milestone. FIXED_CALENDAR_WINDOW: evaluates within fixed calendar periods (e.g., weekly, monthly) set by frequencyType — the system auto-generates cycles within the promotion date range. CYCLIC_WINDOW: uses recurring cycles of a fixed length. PERIOD_AGNOSTIC_WINDOW: evaluates over the entire promotion duration with no cycle boundaries. CALENDAR_CYCLIC_WINDOW: combines calendar alignment with cyclic repetition.
      - `frequencyType` 'DAILY' | 'WEEKLY' | 'MONTHLY' | 'QUARTERLY' | 'HALF_YEARLY' | 'YEARLY' | 'CUSTOM' — The cycle frequency when targetEvaluationType is FIXED_CALENDAR_WINDOW. The system automatically divides the promotion period into cycles of this length. DAILY: one cycle per day. WEEKLY: one cycle per week (Mon–Sun). MONTHLY: one cycle per calendar month. QUARTERLY: one cycle per quarter. HALF_YEARLY: one cycle per half-year. YEARLY: one cycle per year. CUSTOM: boundaries defined manually in cycleActionMapping.
      - `sameActionsForEveryCycle` boolean — When true, the reward actions defined in commonCycleActionMapping for Cycle_1 are reused automatically for every cycle — each cycleActionMapping entry's actions array can be left empty. When false, each cycle entry in cycleActionMapping must explicitly define its own actions.
      - `differentTargetsForEveryCycle` boolean — When true, each cycle entry in cycleActionMapping can specify a different defaultValue (target). When false, the top-level defaultValue applies uniformly to all cycles.
      - `leaderboardEnabled` boolean — When true, a leaderboard is enabled for this milestone, ranking customers by their progress towards the target.
      - `preferredTillId` integer — The ID of the preferred till (point-of-sale terminal) used for transaction tracking within this milestone. Set to the org's default till ID if not targeting a specific terminal.
      - `cycleActionMapping` object[] — Per-cycle target and action definitions. Each entry maps a cycle to its date range, target value, and optional actions. When sameActionsForEveryCycle is true, the actions array can be left empty in every entry.
        - `cycle` string — The cycle identifier (e.g., Cycle_1, Cycle_2). Auto-generated by the system based on frequencyType and the promotion date range.
        - `startDate` string — Start date and time of this cycle in ISO 8601 format.
        - `endDate` string — End date and time of this cycle in ISO 8601 format.
        - `defaultValue` string — The target value for this specific cycle. Only used when differentTargetsForEveryCycle is true; otherwise the milestone-level defaultValue applies.
        - `actions` object[] — Actions to execute when this cycle's target is met. Leave as an empty array when sameActionsForEveryCycle is true.
    - `commonCycleActionMapping` object[] — Maps reward actions directly to cycles for this activity. Use commonCycleActionMapping when the promotion rewards customers on every qualifying event with no target threshold to hit. Leave this empty and populate milestones instead when the reward is conditional on reaching a defined target (e.g., spend $500, complete 10 check-ins).
      - `cycle` string — The cycle identifier this mapping applies to (e.g., Cycle_1). For promotions without milestones, use a single cycle ID spanning the full promotion period.
      - `actions` object[]
        - `id` string — A unique identifier for this action. Caller-supplied string.
        - `actionName` string — The reward action to execute. The corresponding implementation class must be set in delegateActionClass inside mandatoryPropertiesValues. Points and currency: ADVANCE_CURRENCY_ALLOCATION_ACTION, AWARD_POINTS_ACTION, AWARD_TARGET_POINTS_ACTION, BILL_POINTS_ACTION, DELAYED_ACCRUAL_ACTION, TRIGGER_BASED_DELAYED_ACCRUAL_ACTION, REDEEM_POINTS_ACTION, POINTS_REDEMPTION_REVERSAL_ACTION, POINTS_TRANSFER_ACTION, POINTS_GROUP_TRANSFER_ACTION, POINTS_GROUP_REDEMPTION_ACTION, POINTS_CONTRIBUTION_TO_GROUP_ACTION, AWARD_REFERRER_POINTS_ACTION, AWARD_REFEREE_POINTS_ACTION. Tier and slab: UPGRADE_SLAB_ACTION, UPGRADE_SLAB_ACTION_SLAB, UPGRADE_SLAB_ACTION_STRATEGY, RENEW_SLAB_ACTION, TIER_ACTION_V2, UPGRADE_TIER_BASED_ON_PARTNER_PROGRAM, RENEW_TIER_BASED_ON_PARTNER_PROGRAM. Voucher, promotion, and reward: PE_ISSUE_VOUCHER_ACTION, ISSUE_PROMOTION_ACTION, EARN_PROMOTION_ACTION, CONVERT_POINTS_TO_REWARD_ACTION, BADGE_EARN_ACTION. Communication: SEND_COMMUNICATION_ACTION, PE_EMAIL_ACTION, PE_SMS_ACTION, PE_MOBILE_PUSH_ACTION, PE_FACEBOOK_ACTION, PE_WECHAT_MESSAGE_ACTION, PE_MESSAGE_ACTION, EBILL_ACTION. Customer and status: TAG_CUSTOMER_ACTION, STATUS_LABEL_UPDATE_ACTION, INITIATE_SUPPLEMENTARY_MEMBERSHIP. Other: TRACKER_EVALUATION_ACTION, RETURN_BILL_ACTION, REEVALUATE_REGULAR_BILL, FORWARD, NO_OP_ACTION (placeholder, performs no reward).
        - `actionClass` string — The fully qualified Java wrapper class for this action. For point allocation actions (ADVANCE_CURRENCY_ALLOCATION_ACTION, AWARD_POINTS_ACTION, BILL_POINTS_ACTION, etc.), use com.capillary.shopbook.pointsengine.endpoint.impl.action.CommunicationDecoratorActionImpl. The specific action implementation is set separately via delegateActionClass inside mandatoryPropertiesValues.
        - `description` string — A human-readable description of what this action does.
        - `mandatoryPropertiesValues` object — Key-value map of configuration properties for this action. All values are strings. Supported keys: delegateActionClass — The specific action implementation class. Set this to the class that corresponds to the actionName. For example, ADVANCE_CURRENCY_ALLOCATION_ACTION uses com.capillary.shopbook.pointsengine.endpoint.impl.action.AdvanceCurrencyAllocationActionImpl. PointType — The point category to award. Valid values are the point type names configured for the org (e.g., Main, Promotional). Defaults to the main point type if omitted. Currency — The currency type to award. POINTS for standard loyalty points. Any org-configured alternate currency identifier (e.g., MILES, CASHBACK) is also valid. Event Name — The event name that this action responds to. Must match the event field of the parent activity (e.g., TransactionAdd, TargetCompleted). PointsRoundingStrategy — How the final points amount is rounded. ACTUAL (default): rounds half-up. FLOOR: always rounds down. ROUND: rounds to nearest. ROUND_TO_NEAREST: rounds to nearest (half-down variant). SourceValueRoundingStrategy — How the source value (e.g., bill amount) is rounded before points are calculated. ACTUAL (default): no rounding. FLOOR: always rounds down. ROUND: rounds to nearest. ROUND_TO_NEAREST: rounds to nearest (half-down variant). DelayStrategy — Controls when points are accrued. AS_DEFINED_IN_ALLOCATION_STRATEGY (default): follows the allocation strategy. FIXED_DELAY_FROM_ITEM_RETURN_PERIOD: accrues after the item return period. ON_EXTERNAL_TRIGGER: accrues only when an external trigger fires. FIXED_DELAY_FROM_TXN_ITEM_EXT_FIELD: delay read from a transaction/line item extended field. ACCRUAL_DATE_AS_PER_BE_ATTRIBUTE: uses a backend attribute for the accrual date. ACCRUAL_DATE_FROM_TXN_ITEM_EXT_FIELD: reads accrual date from a transaction/line item extended field. FIXED_DELAY_FROM_BE_ATTRIBUTE: delays by a fixed duration from a backend attribute. ProRateOnSourceValue — The base value used when prorating points. EVENT_DEFAULT_VALUE (default): the event's default value (e.g., bill amount). AMOUNT: transaction or line item amount. QUANTITY: item quantity. LINEITEM_AMOUNT: individual line item amount. LINEITEM_QUANTITY: individual line item quantity. DEFINED_TARGET: the defined milestone target value. ACHIEVED_VALUE: value achieved towards a target. ACHIEVED_VALUE_MINUS_DEFINED_TARGET: achieved minus target. TRACKED_VALUE: a tracker's current tracked value. TRACKER_EVALUATION: the tracker evaluation result. CURRENT_AGGREGATE / PREVIOUS_AGGREGATE: tracker aggregate values. GENERIC_EVENT_FIELD_VALUE: a named field from the event. TRANSACTION_EXTENDED_FIELD / LINEITEM_EXTENDED_FIELD / CUSTOMER_EXTENDED_FIELD / CUSTOMER_CUSTOM_FIELD: extended or custom attribute values. GAP_TO_UPGRADE: gap between next slab threshold and current tracker value before the event. GAP_TO_UPGRADE_AFTER_EVENT: same gap computed after the event. CURRENT_EVENT_TRACKED_VALUE: value tracked in the current event. EXCESS_AFTER_UPGRADE: amount by which the current event exceeds the upgrade gap. EvaluatedEntity — The entity the action is evaluated against. USER (default): the individual customer. USERGROUP2: a user group. USER_ENTITY_REFERENCE: same as USERGROUP2. ProrateFieldName — The exact field name to use when ProRateOnSourceValue is a field-based value (e.g., TRANSACTION_EXTENDED_FIELD). Leave as an empty string if not applicable.
        - `mandatoryComplexPropertiesValues` object — Key-value map of complex (object or array) configuration properties for this action. Returns {} for most actions. Only populated for actions that support line-item filtering — restricting awards to specific SKUs, brands, categories, tender types, or product attributes via include/exclude blocks. Supported on AdvanceCurrencyAllocationActionImpl and badge earn actions only. For the Issue Currency action with FIXED allocation, an optional threshold block (aggregateFunction: SUM, kpi: LINEITEM_AMOUNT, value: a non-negative number) gates the award on the qualifying spend of the filtered line items. A threshold requires an include or exclude block to define its qualifying set: an item filter (skus/brands/categories/attributes) scopes it to matching items; a tendermode-only filter scopes it to the full basket paid by that tender. Attribute filters are capped at 200 keys per include/exclude block, each with up to 200 values.
        - `embeddedStrategies` object[] — Inline strategy configurations attached to this action. Defines how points are allocated (strategyTypeId 1) and when they expire (strategyTypeId 3) without requiring a pre-configured strategy. Each action supports at most one allocation strategy and one expiry strategy.
          - `strategyTypeId` 1 | 3 — Identifies the strategy type. 1: point allocation strategy — defines how many points to award per slab. 3: point expiry strategy — defines when awarded points expire. These are the only valid values.
          - `propertyValues` string — JSON string containing the strategy configuration. Fields differ by strategyTypeId. For strategyTypeId 1 (allocation): allocation_type (only FIXED confirmed) and allocation_values (comma-separated point amounts, one per slab level — e.g., "100,100,100,100,100,100,100,100" for 8 slabs). For strategyTypeId 3 (expiry): expiry_type (SLAB_INDEPENDENT: same expiry for all slabs; SLAB_BASED: expiry differs per slab), expiry_from (when the expiry clock starts — CURRENT_DATE, CUSTOMER_ENROLLMENT_DATE, ACTIVITY_BASED_EXTENSION, MEMBERSHIP_DATE, FARTHEST_POINTS_TRANSFERRED_EXPIRY_DATE, EXPIRY_DATE_BEFORE_REDEMPTION_REVERSAL, EXPIRY_DATE_BEFORE_POINTS_CONTRIBUTION_TO_GROUP, BILL_EXTENDED_FIELD, LINEITEM_EXTENDED_FIELD, CUSTOMER_EXTENDED_FIELD), expiry_time_units (comma-separated unit per slab — NUM_DAYS, MONTH_END, NUM_MONTHS, NUM_MONTHS_END, YEAR_END, NUM_YEARS, NEVER, FIXED_DATE, ENROLLMENT_DATE, FIXED_DATE_WITHOUT_YEAR), expiry_time_values (comma-separated values per slab — date strings when unit is FIXED_DATE; numeric duration when unit is NUM_DAYS/NUM_MONTHS/etc.; use "100" as a placeholder when unit is NEVER).
          - `owner` string — The system that owns this strategy. Typically LOYALTY.
          - `strategySubType` string — The strategy sub-type. DEFAULT is the standard value.
          - `useCommonExpiryStrategy` boolean, nullable — Controls whether this embedded strategy references a shared expiry from commonStrategies.expiry instead of defining expiry inline. Null when using inline expiry (propertyValues is set directly on this strategy). True when the action's embedded strategy should point to a named shared expiry entry in commonStrategies.expiry. Only valid for expiry strategies (strategyTypeId: 3). Using on a non-expiry strategy causes a validation error. Validation rules: (1) If true, strategyRef must be non-empty — error: "Expiry strategy must provide a non-empty strategyRef". (2) If true, propertyValues must not be set — error: "Expiry strategy cannot set propertyValues when useCommonExpiryStrategy=true". Inline example: {"strategyTypeId": 3, "propertyValues": "{...}", "useCommonExpiryStrategy": null} Shared reference example: {"strategyTypeId": 3, "useCommonExpiryStrategy": true, "strategyRef": "shared_expiry_90d"}
          - `strategyRef` string, nullable — The name/key of the shared expiry entry in commonStrategies.expiry to use. Null when useCommonExpiryStrategy is null (inline expiry mode). Only valid for expiry strategies (strategyTypeId: 3). Validation rules: (1) Must be non-empty when useCommonExpiryStrategy is true — error: "Expiry strategy must provide a non-empty strategyRef". (2) Must match an existing entry in commonStrategies.expiry — error: "Invalid common expiry strategyRef '<ref>'". (3) All strategyRef values in commonStrategies.expiry must be unique. (4) Not applicable for allocation strategies (strategyTypeId: 1). Example: "strategyRef": "shared_expiry_90d"
  - `limits` object[]
    - `entityScope` 'PROGRAM' | 'PROMOTION' — The scope at which this limit is enforced. PROGRAM: applies across the entire loyalty program. PROMOTION: scoped to this promotion only.
    - `entityId` integer — The numeric ID of the entity the limit applies to. Required when entityScope is PROGRAM — set this to the programId. Not required when entityScope is PROMOTION.
    - `granularity` 'OVERALL' | 'USER' | 'PER_ACTIVITY' — The level at which the limit is tracked. USER: limit is applied per individual customer. OVERALL: limit is applied across all customers combined. PER_ACTIVITY: limit is tracked per activity within the promotion.
    - `actionType` 'AWARD_CURRENCY' | 'AWARD_BADGE' | 'ISSUE_COUPON' | 'ISSUE_REWARD' | 'UPGRADE_TIER' | 'RENEW_TIER' | 'DOWNGRADE_TIER' — The type of reward action this limit constrains. Supported values: AWARD_CURRENCY (points/currency), AWARD_BADGE, ISSUE_COUPON, ISSUE_REWARD, UPGRADE_TIER, RENEW_TIER, DOWNGRADE_TIER.
    - `actionSubTypeId` string — The specific currency or sub-type this limit targets. For points-based limits, use the point type name (e.g., Points).
    - `limitType` 'SUM' | 'COUNT' — How the limit is measured. SUM: caps the total value awarded (e.g., max 10,000 points per month). COUNT: caps the number of times the reward action fires (e.g., max 5 reward events).
    - `limitValue` integer — The maximum allowed threshold for this limit. For SUM limits this is the total points/currency cap; for COUNT limits this is the maximum number of reward occurrences.
    - `period` object
      - `periodType` 'MOVING_WINDOW' | 'FIXED_CALENDAR_WINDOW' | 'NON_PERIOD_BASED' | 'FIXED_WINDOW' — Defines the time window for this limit. NON_PERIOD_BASED: limit applies for the lifetime of the promotion with no reset. FIXED_CALENDAR_WINDOW: resets on a fixed calendar cycle (e.g., monthly on the 1st). MOVING_WINDOW: a rolling window relative to the current date (e.g., last 30 days). FIXED_WINDOW: a specific fixed date range.
      - `periodUnit` 'DAYS' | 'WEEKS' | 'MONTHS' — The time unit for the period window. Required when periodType is FIXED_CALENDAR_WINDOW or MOVING_WINDOW.
      - `periodValue` integer — The number of time units in the window (e.g., periodValue: 1 with periodUnit: MONTHS means a 1-month window).
    - `active` boolean — Whether this limit is currently active and being enforced.
  - `liabilityOwnerSplitInfo` string[]
  - `workflowMetadata` object — Configures opt-in and enrollment workflows. Required for LOYALTY and LOYALTY_EARNING promotion types. For GENERIC promotions, pass an empty object ({}) for both enrolment and optin.
    - `enrolment` object — Enrollment workflow configuration. Controls how and when customers become enrolled in the promotion.
      - `basedOn` 'ACTIVITY' | 'AUDIENCE' | 'EXTERNAL_TRIGGER' — The enrollment trigger. ACTIVITY: customers are enrolled upon completing a defined activity. AUDIENCE: all customers in the specified audience groups are enrolled. EXTERNAL_TRIGGER: enrollment is triggered by an external system call.
      - `audienceMapping` object[] — The audience groups whose members will be enrolled. Required when basedOn is AUDIENCE.
        - `groupId` integer — The numeric ID of the audience group.
        - `groupName` string — The name of the audience group. Maximum 255 characters.
      - `restrictions` object — Enrollment-level constraints and expiry rules.
        - `enrolmentLimitPerCustomer` object — Maximum number of times a single customer can be enrolled in this promotion.
          - `value` integer — The maximum enrollment count per customer.
          - `type` 'NON_PERIOD_BASED' | 'PERIOD_BASED' — NON_PERIOD_BASED: lifetime limit with no reset. PERIOD_BASED: limit resets on a rolling period defined by periodType and periodUnit.
        - `enrolmentLimitPerPromotion` object — Maximum total enrollments allowed across all customers for this promotion.
          - `value` integer
          - `type` 'NON_PERIOD_BASED' | 'PERIOD_BASED'
        - `maxRedemptionsPerEarnPerCustomer` object — Maximum number of times a customer can redeem each earned instance of this promotion.
          - `value` integer
          - `type` 'NON_PERIOD_BASED' | 'PERIOD_BASED'
        - `maxPointsPerEarnPerCustomer` object — Maximum points a customer can earn per enrolled instance of this promotion.
          - `value` integer
          - `type` 'NON_PERIOD_BASED' | 'PERIOD_BASED'
        - `enrolmentExpiryBasedOn` object — Configures when an enrollment expires.
          - `type` 'PROMOTION' | 'CUSTOM' | 'FIXED_DATE' — PROMOTION: enrollment expires when the promotion ends. CUSTOM: expires a fixed number of days after enrollment (set via value). FIXED_DATE: expires on a specific date (set via expiryDate).
          - `value` integer — Number of days until enrollment expires. Used when type is CUSTOM.
          - `expiryDate` string, date-time — The fixed expiry date. Used when type is FIXED_DATE.
    - `optin` object — Opt-in workflow configuration. Controls how and when customers opt in to the promotion.
      - `basedOn` 'ACTIVITY' | 'AUDIENCE' | 'EXTERNAL_TRIGGER' — The opt-in trigger. ACTIVITY: opt-in is based on completing a defined activity. AUDIENCE: customers in specified audience groups are auto-opted in. EXTERNAL_TRIGGER: opt-in is triggered by an external system call.
      - `audienceMapping` object[] — Audience groups to auto-opt in. Required when basedOn is AUDIENCE.
        - `groupId` integer
        - `groupName` string
      - `optInStartDate` string, date-time — The date from which customers can opt in. Optional; defaults to the promotion start date if omitted.
      - `optInEndDate` string, date-time — The date after which no new opt-ins are accepted. Optional; defaults to the promotion end date if omitted.
      - `restrictions` object — Opt-in-level constraints and expiry rules.
        - `optinLimitPerCustomer` object — Maximum number of times a single customer can opt in to this promotion.
          - `value` integer
          - `type` 'NON_PERIOD_BASED' | 'PERIOD_BASED'
        - `optinExpiryBasedOn` object — Configures when an opt-in expires.
          - `type` 'PROMOTION' | 'CUSTOM' | 'FIXED_DATE' — PROMOTION: opt-in expires when the promotion ends. CUSTOM: expires a fixed number of days after opt-in (set via value). FIXED_DATE: expires on a specific date (set via expiryDate).
          - `value` integer — Number of days until opt-in expires. Used when type is CUSTOM.
          - `expiryDate` string, date-time — The fixed expiry date. Used when type is FIXED_DATE.

## Response `200`

OK

- object

## Other responses

- `201` — Created

---

[API](https://skmtc.net/capillarytech/apis/sample-api.md) · [All operations](https://skmtc.net/capillarytech/apis/sample-api/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/capillarytech/sample-api/revisions/de241f2ed9b3/schema)
