> For the complete documentation index, see [llms.txt](https://help.openloyalty.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://help.openloyalty.io/api-reference/campaign/update-campaigns-configuration.md).

# Update campaign’s configuration

Management\
\
This method allows changing the information about a specific Campaign.\
\
**Rules semantics on PUT:**

* `rules` replaces the whole rule list. Edit a campaign as `GET` → change → `PUT`, sending back every rule you keep with its `ruleKey` and its `limits` as `GET` returned them.
* A rule with its `ruleKey` is updated in place and keeps the usage its per-SKU `limits` counted. A rule without one is saved as new, its members' allowances starting from zero; a stored rule left out is deleted. Neither raises an error.
* A rule's `limits` are replaced too: a rule sent without them loses them, and a limit sent without its `interval` becomes a lifetime limit.
* Rules are stored in array order, and that order decides which rule pays a line when `ruleClubbing` is `false`. Omitting `ruleClubbing` keeps the stored value.
* A webhook payload carries no `ruleKey` and is not a basis for this request.

**Custom-fields semantics on PUT:**

* `customFields` omitted from the request body → existing custom-field values on the campaign are **kept untouched** (no-op; no validation runs).
* `customFields: null` → existing values are **kept untouched** (treated identically to omitting the field).
* `customFields: {}` (explicit empty object) → all existing custom-field values on the campaign are **cleared**. Required-field validation still runs against the empty payload, so this fails with 400 if any active campaign custom-field group contains a required field.
* `customFields: { groupKey: { fieldKey: value, ... }, ... }` → **wholesale entity replace**. The campaign's entire custom-field state is deleted first, then only the groups present in the payload are persisted. Consequence: any group NOT listed in the payload is also wiped, even if it had values before. Required-field validation runs against the payload as a whole.

The feature flag `CUSTOM_FIELD_CAMPAIGN` must be enabled; when disabled the `customFields` key is silently ignored regardless of value.\
\
For granular per-group or per-key updates that preserve other groups, use the bulk patch endpoint (`PATCH /api/{storeCode}/custom-fields/campaign/{campaignId}`).

````json
{"openapi":"3.0.0","info":{"title":"Open Loyalty","version":"0.0.1"},"tags":[{"name":"Campaign","description":"These endpoints will allow you to easily manage campaigns."}],"servers":[{"url":"http://openloyalty.localhost"}],"security":[{"Bearer":[]},{"token":[]}],"components":{"securitySchemes":{"Bearer":{"type":"http","description":"Open Loyalty has the JWT authorization.\nTo learn what a JSON Web Token is and how it works, check out Introduction to JSON Web Tokens <https://jwt.io/introduction/>\n\n### Obtain an access token\n\nSend a request with the parameters username and password\n\n#### Definition\n\n`POST /api/admin/login_check` [Go to definition](#operation/adminLoginCheck)\\\n`POST /api/{storeCode}/member/login_check` [Go to definition](#operation/memberLoginCheck)\n\n#### Example\n\n```bash\ncurl {HOST}/api/admin/login_check\n    -H 'Content-Type: application/json;charset=UTF-8'\n    -H 'Accept: application/json, text/plain, */*'\n    --data-binary '{\"username\":\"admin\",\"password\":\"password\"}'\n```\n\n#### Example Response\n\n```json\n{\n    \"token\":\"eyJhbGciOiJSUzI1NiIsInR5cCI6...\",\n    \"refresh_token\":\"0558f8bb29948c4e54c443f...\"\n}\n```\n\n### Using JSON Web Token\n\nAdd authorization header to each request\\\n`Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6...`\\\nYou can now access any API method you want under the /api prefix.\n\n#### Example\n```bash\ncurl {HOST}/api/{storeCode}/analytics/members\n    -H 'Accept: application/json'\n    -H 'Content-type: application/x-www-form-urlencoded'\n    -H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6...'\n```\n\n### Refresh JSON Web Token\nYou can refresh token using refresh_token that was given during login.\\\nThere are two endpoints\\\nTo refresh admin token `POST /api/token/refresh` [Go to definition](#operation/tokenRefresh)\\\nTo refresh member token `POST /api/{storeCode}/token/refresh` [Go to definition](#operation/tokenRefreshMember)\n","bearerFormat":"JWT","scheme":"bearer"},"token":{"type":"apiKey","name":"X-AUTH-TOKEN","in":"header"}},"parameters":{"storeCode":{"name":"storeCode","in":"path","description":"Store code","required":true,"schema":{"type":"string"}},"campaign":{"name":"campaign","in":"path","description":"Campaign ID","required":true,"schema":{"type":"string","format":"uuid"}}},"schemas":{"AchievementCampaignRequestBody":{"title":"AchievementCampaign","required":["campaign"],"properties":{"campaign":{"required":["type","trigger","translations","activity","rules","achievementId"],"properties":{"triggerStrategy":{"$ref":"#/components/schemas/TriggerStrategy"},"visibility":{"$ref":"#/components/schemas/Visibility"},"audience":{"$ref":"#/components/schemas/Audience"},"rules":{"$ref":"#/components/schemas/Rule"},"limits":{"$ref":"#/components/schemas/Limit"},"type":{"type":"string","enum":["direct","referral"]},"trigger":{"type":"string"},"activity":{"required":["startsAt"],"properties":{"startsAt":{"type":"string"},"endsAt":{"type":"string","nullable":true}},"type":"object"},"displayOrder":{"type":"number"},"active":{"type":"boolean"},"labels":{"type":"array","items":{"required":["key","value"],"properties":{"key":{"type":"string"},"value":{"type":"string"}},"type":"object"}},"translations":{"$ref":"#/components/schemas/CampaignTranslations"},"achievementId":{"type":"string","format":"uuid","deprecated":true},"customFields":{"type":"object","nullable":true,"description":"Custom-field values grouped by group key. PUT semantics: omitted or null → existing values kept (no-op); empty object {} → all values cleared (validation still runs); non-empty object → wholesale entity replace (every group not in the payload is also wiped). Requires the CUSTOM_FIELD_CAMPAIGN feature flag; when disabled the field is silently ignored. See the endpoint description for full semantics.","additionalProperties":{"type":"object","additionalProperties":true}}},"type":"object","additionalProperties":false}},"type":"object","additionalProperties":false},"TriggerStrategy":{"properties":{"type":{"type":"string","enum":["birthday","registration_anniversary","daily","weekly","monthly"]},"executionSchedule":{"description":"Required for `weekly` and `monthly` type of campaign.","properties":{"dayOfWeek":{"description":"List of days when campaign should be executed. 0 for Sunday, 6 for Saturday.","type":"array","items":{"type":"integer","enum":[0,1,2,3,4,5,6]},"nullable":true},"dayOfMonth":{"description":"List of month days when campaign should be executed. 'L' for the last day of the month.","type":"array","items":{"oneOf":[{"type":"integer","enum":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31]},{"type":"string","enum":["L"]}]},"nullable":true}},"type":"object","nullable":true}},"type":"object"},"Visibility":{"description":"Visibility settings for the campaign. Optional field.","properties":{"target":{"type":"string","enum":["segment","tier","none"]},"tiers":{"description":"Visibility settings for the campaign. Optional field.","type":"array","items":{"type":"string"}},"segments":{"description":"Visibility settings for the campaign. Optional field.","type":"array","items":{"type":"string"}}},"type":"object","additionalProperties":false},"Audience":{"description":"Audience settings for the campaign. Optional field.","properties":{"target":{"type":"string","enum":["segment","tier"]},"tiers":{"type":"array","items":{"type":"string"}},"segments":{"type":"array","items":{"type":"string"}}},"type":"object","nullable":true},"Rule":{"type":"array","items":{"required":["effects"],"properties":{"ruleKey":{"description":"The handle of a stored rule, as `GET /{storeCode}/campaign/{campaign}` returns it. An opaque string: do not parse it, compute it or rely on it being numeric.\n\nOn `PUT`, a rule sent with its `ruleKey` is updated in place and keeps everything hanging off it — the usage its `limits` counted above all. A rule sent without one is saved as a new rule, and its members' allowances start from zero; no error is raised, so edit a campaign as `GET` → change → `PUT` and send back the `ruleKey` of every rule you keep. A stored rule the `PUT` leaves out is deleted. A `ruleKey` that does not belong to this campaign, or one repeated in a payload, is rejected with `400` on `rules.N.ruleKey`; on `POST` any `ruleKey` is rejected the same way. Campaign webhooks do not carry `ruleKey`, so a webhook payload is not a basis for a `PUT`.\n\nRule order is array order: the rules are stored and returned in the order of the last `POST` or `PUT`, and that order decides which rule pays a line when `ruleClubbing` is `false`. Reorder rules with a `PUT` of the whole list, keys included; `PATCH` does not reorder them.","type":"string"},"name":{"type":"string"},"description":{"type":"string"},"target":{"description":"Selects which member the rule's effects apply to: `self` — the member whose event triggered the campaign (the referred member); `referrer` — the member who referred them.\n\nRequired on every rule of a `referral` campaign sent with `POST` or `PUT`: leaving it out is a `400` on `rules.N.target`. On a campaign of any other type the field is rejected outright. A `PATCH` that omits it keeps the stored value. `rules` is shared by every campaign type, so the schema cannot mark it required; the condition above is what decides.","type":"string","enum":["self","referrer"]},"lineTargets":{"description":"Accepted only when the campaign trigger is `transaction` or `return_transaction`.","type":"array","items":{"$ref":"#/components/schemas/LineTarget"}},"limits":{"description":"Per-SKU caps on what the rule rewards. Accepted only when the campaign trigger is `transaction`; on any other trigger the field is rejected with `400` (`This form should not contain extra fields.`) on `rules.N`.\n\nAt most one limit per rule: a second one is rejected with `400` on `rules.N.limits`. A rule therefore caps either `items` or `units`, and counts either per SKU or per rule.\n\n`PUT` replaces a rule's `limits` whole. A rule sent without `limits` loses them, and a limit sent without its `interval` becomes a lifetime limit, with no error. Changing a kept rule's `interval`, or its `countedPer` from `sku` to `rule`, takes effect from now: the current period is not recounted, and a new pot starts from zero.","type":"array","maxItems":1,"items":{"$ref":"#/components/schemas/CampaignRuleLimit"}},"effects":{"type":"array","items":{"required":["effect"],"properties":{"effect":{"type":"string","enum":["give_points","give_reward","deduct_unit","assign_member_custom_attribute","remove_member_custom_attribute","grant_badge","cancel_transaction_effects","assign_member_to_tier"],"description":"`assign_member_to_tier` is accepted on five combinations of campaign `type` and `trigger`: `direct` × `custom_event`, `direct` × `challenge`, `referral` × `custom_event`, `direct` × `transaction` and `referral` × `transaction`. Every other combination is rejected with `400` on `rules.N.effects.M.effect` — `referral` × `challenge` among them, even though the challenge request body admits `type: referral`, and `return_transaction` on either type, even though it sits in the same trigger enum as `transaction`. This enum is shared by every request body that carries `rules`, so the value is advertised on campaign variants that cannot accept it — the list above, not the enum, is what decides. The effect puts the member on the configured tier and marks that tier as manually held on the member's tier progress, so it is not lost to an automatic downgrade; the member-history entry still reports `manually: false` and names the granting campaign instead.\n\nRepeating the trigger does not grant the tier twice. The grant is skipped when the member already holds the configured tier from an earlier grant or an administrator's assignment, and when the member holds a higher tier of the same set — the effect raises a member to the tier, never lowers them to it. It still runs for a member who reached the tier on their own: it reports `success`, appears in `effectsGiven`, and from then on the tier is protected from an automatic downgrade. A run whose only effect was skipped sends no `CampaignEffectWasApplied` webhook at all — not a webhook with an empty `effectsGiven`.\n\nA returned transaction does not take the tier back. `cancel_transaction_effects` reverses the points the original transaction earned and leaves the tier standing, along with the mark that protects it from an automatic downgrade. The return carries the `return_transaction` trigger, which no campaign holding this effect can match, so it neither revokes the grant nor makes a second one.\n\nOn a `referral` campaign the tier goes to the member named by the rule's `target`: `self` — the member whose event triggered the campaign, `referrer` — the member who referred them. A live run for a member who has no referrer is skipped whole — none of the campaign's effects run, not just this one. Simulation does not apply that skip; see `SimulateResult`.\n\nOn a multi-level referral campaign (`multiLevel` above 1) the campaign re-runs once for each member up the referrer chain, so a single event can grant the tier to as many members as `multiLevel` allows. The skip described above does not hold it to one: every run resolves `target` against a different member. The chain ends at the first member who has no referrer — that member's run is skipped whole, so they are not granted the tier.\n\nOn the `custom_event` trigger, `eventId` stays optional and guards only against the very same event being delivered twice. A different event — a second device registration, say — is a new event: the campaign runs again, and it is the tier grant that skips it, not the deduplication of the event. The `transaction` trigger carries no such guard: every transaction assigned to the member runs the campaign again, and the grant itself is the only thing that stops the second one from moving the member."},"pointsRule":{"description":"Only allowed for `give_points` and `deduct_unit` effect. Required for `deduct_unit`. On `give_points` an effect declares its payout through `pointsRule` or `unitsRule`, never both: a non-empty `pointsRule` sent together with `unitsRule` is a `400` on `rules.N.effects.M`, while an empty or `null` `pointsRule` beside it is ignored; sending neither is a `400` on `rules.N.effects.M.pointsRule`."},"unitsRule":{"description":"Only allowed for the `give_points` effect of a campaign whose trigger is `transaction` or `return_transaction`. Anywhere else the field is rejected with `400` (`This form should not contain extra fields.`) on `rules.N.effects.M`. Replaces `pointsRule`: an effect carrying `unitsRule` is returned without `pointsRule`, and switching an effect between the two on update clears the other. `null` is read as absent.","type":"object","nullable":true,"allOf":[{"$ref":"#/components/schemas/UnitsRule"}]},"walletCode":{"description":"Only allowed for `give_points` and `deduct_unit` effect","type":"string"},"relatedLineTarget":{"description":"Only allowed for the `give_points` effect, and only alongside `unitsRule`: the code of the rule's line target whose lines `unitsRule` measures. Omit it to measure the whole transaction. Sent with `pointsRule` it is rejected with `400` on `rules.N.effects.M.relatedLineTarget`, as is a code that names none of the rule's `lineTargets`.","type":"string","minLength":3,"maxLength":32},"transform":{"description":"Only allowed for `give_points` and `deduct_unit` effect","properties":{"tierMultiplier":{"description":"A tier without an entry uses 1.","required":["tierSetId"],"properties":{"tierSetId":{"type":"string","format":"uuid"},"tiers":{"type":"array","items":{"required":["tierId","factor"],"properties":{"tierId":{"type":"string","format":"uuid"},"factor":{"type":"number","minimum":0}},"type":"object","additionalProperties":false}}},"type":"object","additionalProperties":false}},"type":"object","additionalProperties":false},"unitsLockRule":{"description":"The field allows you to set a custom pending for units given by campaign effects. Only allowed for `give_points` effect","required":["lockStrategy"],"properties":{"lockStrategy":{"type":"string","enum":["expression","from_wallet","no_pending"]},"expression":{"description":"This field is required for `lockStrategy = expression`","type":"string"}},"type":"object","additionalProperties":false},"unitsExpirationRule":{"description":"The field allows you to set a custom expiration for units given by campaign effects. Only allowed for `give_points` effect","required":["expirationStrategy"],"properties":{"expirationStrategy":{"type":"string","enum":["expression","from_wallet","never_expires"]},"expression":{"description":"This field is required for `lockStrategy = expression`","type":"string"}},"type":"object","additionalProperties":false},"rewardId":{"description":"Only allowed for `give_reward` effect","type":"string","format":"uuid"},"couponValueRule":{"description":"Only allowed for `give_reward` effect","type":"string"},"customAttributeKey":{"description":"Only allowed for `assign_member_custom_attribute` effect","type":"string"},"customAttributeValueRule":{"description":"Only allowed for `assign_member_custom_attribute` effect","type":"string"},"badgeSystemCode":{"description":"Only allowed for `grant_badge` effect","type":"string"},"tierSetId":{"description":"Only allowed for the `assign_member_to_tier` effect, and required for it. The tier set the configured tier belongs to.","type":"string","format":"uuid"},"tierId":{"description":"Only allowed for the `assign_member_to_tier` effect, and required for it. The tier the member is put on. It must belong to `tierSetId` and be an active tier of this store, otherwise the request is rejected with `400` on `rules.N.effects.M.tierId`.","type":"string","format":"uuid"}},"type":"object","additionalProperties":false}},"conditions":{"type":"array","items":{"oneOf":[{"title":"Condition","required":["operator","data"],"properties":{"attribute":{"type":"string"},"operator":{"type":"string","enum":["expression","is_equal","has_at_least_one_label","contains","not_contains","is_not_equal","is_not_one_of","is_one_of","matches_regex","is_greater","is_greater_or_equal","is_less","is_less_or_equal","ends_with","starts_with","is_after","is_before","is_between","is_day_of_week","is_month_of_year","is_not_between","is_day_of_month","is_time_between","is_one_of_group_values","is_not_one_of_group_values","not_contains_one_of","contains_one_of"]},"data":{}},"type":"object"},{"$ref":"#/components/schemas/NewCondition"}]}}},"type":"object"}},"LineTarget":{"required":["code"],"properties":{"code":{"type":"string","minLength":3,"maxLength":32},"name":{"type":"string","maxLength":32},"selector":{"description":"Send lines or sets, not both. An empty selector matches every line.","properties":{"lines":{"description":"Filters are AND-ed; an empty list matches every line.","properties":{"filters":{"type":"array","items":{"required":["value","condition"],"properties":{"value":{"required":["field"],"properties":{"field":{"type":"string","enum":["sku","name","qty","grossValue","category","maker","labels","catalogSku","catalogCategory","catalogBrand","catalogAttributes"]}},"type":"object","additionalProperties":false},"condition":{"$ref":"#/components/schemas/Condition"},"dataEnteredManually":{"type":"boolean"}},"type":"object","additionalProperties":false}}},"type":"object","additionalProperties":false},"sets":{"description":"Every part must be satisfied for one set to form.","type":"array","items":{"required":["requirements","filters"],"properties":{"requirements":{"required":["qtyPerSet"],"properties":{"qtyPerSet":{"type":"number","minimum":0.001,"maximum":100000000,"exclusiveMaximum":true}},"type":"object","additionalProperties":false},"filters":{"type":"array","minItems":1,"items":{"required":["value","condition"],"properties":{"value":{"required":["field"],"properties":{"field":{"type":"string","enum":["sku","name","qty","grossValue","category","maker","labels","catalogSku","catalogCategory","catalogBrand","catalogAttributes"]}},"type":"object","additionalProperties":false},"condition":{"$ref":"#/components/schemas/Condition"},"dataEnteredManually":{"type":"boolean"}},"type":"object","additionalProperties":false}}},"type":"object","additionalProperties":false}}},"type":"object","additionalProperties":false}},"type":"object","additionalProperties":false},"Condition":{"required":["operator","value"],"properties":{"operator":{"type":"string","enum":["eq","neq","between","not_between","starts_with","ends_with","match","not_match","gt","gte","lt","lte","is_after","is_before","in","not_in","one_of","not_one_of","in_value_group","not_in_value_group","one_of_value_group","not_one_of_value_group"]},"value":{"oneOf":[{"description":"For operators `eq`, `neq`, `starts_with`, `ends_with`, `match`, `not_match` the value can be a string. For `match` and `not_match`, the string must be a valid regular expression.","type":"string"},{"description":"For operators `eq`, `neq`, `gt`, `gte`, `lt`, `lte` , the value can be a number.","type":"number"},{"description":"For operators `eq`, `neq`, the value can be a bool.","type":"boolean"},{"title":"datetime","description":"For operators `eq`, `neq` `is_after`, `is_before`, the value can be a datetime.","type":"string","format":"datetime","pattern":"^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,3})?Z$"},{"title":"array","type":"array","items":{"description":"For `in`, `not_in`, `one_of`, `not_one_of` operators, the values can be in array.","oneOf":[{"type":"number"},{"type":"string"},{"title":"custom attribute","properties":{"key":{"type":"string"},"value":{"type":"string"}},"type":"object"}]}},{"title":"between","description":"For `between`, `not_between` operators, the value is an object with 'from' and 'to'.","required":["from","to"],"properties":{"from":{"oneOf":[{"type":"number"},{"title":"datetime","type":"string","format":"datetime"}]},"to":{"oneOf":[{"type":"number"},{"title":"datetime","type":"string","format":"datetime"}]}},"type":"object"},{"title":"group of values","required":["groupValuesIds"],"properties":{"groupValuesIds":{"description":"For `in_value_group`, `not_in_value_group` operators, the value is an object with `groupValuesIds` array.","type":"array","items":{"type":"string","format":"uuid"}}},"type":"object"},{"title":"one of group of values","description":"For `one_of_value_group`, `not_one_of_value_group` operators, the value is an object with labelKey and `groupValuesIds` array.","required":["groupValuesIds","labelKey"],"properties":{"labelKey":{"type":"string"},"groupValuesIds":{"type":"array","items":{"type":"string","format":"uuid"}}},"type":"object"}]}},"type":"object","additionalProperties":false},"CampaignRuleLimit":{"description":"A cap on how much a rule rewards for the products it covers. It has nothing to do with the campaign's own `limits` object (`points`, `pointsPerMember`, `executionsPerMember`), which keeps its own shape and still applies on top. There is no SKU list: what the cap covers follows from the rule's targets.\n\nConditions read the whole transaction; the rule's effects are calculated on what the cap leaves. The cap works on the lines the rule's effects read — the lines of an effect's `relatedLineTarget`, or the whole transaction for an effect without one — so it reduces any amount computed from those lines (`qty`, `value`), down to zero above the cap, and the transaction itself is accepted. It does not reach a constant amount: `pointsRule: \"100\"` keeps paying 100 while one line is left, and an effect that reads no line at all pays it every time. `metric: units` is the one that caps an amount. A reward the cap withholds entirely leaves no campaign execution, so it does not spend `executionsPerMember` or the campaign's execution limit; a partial cut keeps the effect successful and adds a warning to the execution. A `return_transaction` neither consumes nor releases an allowance, and cancelling a transfer does not release one either. `POST /{storeCode}/campaign/simulate` does not apply the cap: it pays the untrimmed transaction.\n\nAny key other than the fields below is rejected with `400` (`This form should not contain extra fields.`) on `rules.N.limits.M`.","required":["metric","scope","value"],"properties":{"metric":{"description":"What the limit counts.\n\n* `items` — the quantity of each transaction line the rule rewarded, in the line's own unit: five bottles, or five kilograms. Counted on the lines the rule's effects read, so an effect without a `relatedLineTarget` spends the allowance of every line of the transaction. The rule books a line once, however many of its effects paid for it.\n* `units` — the reward units the rule's `give_points` effects paid for each product, across all the wallets they pay into. An effect's amount is split over the lines it was computed from, by its `unitsRule.applyTo` or by line value when it has none, and each product's share is cut to its allowance, in effect order: with 15 left, effect A asking 10 is paid 10 and effect B asking 10 is paid 5. An effect covering no line with a SKU is not capped. The cap counts units as paid, after any tier multiplier (`transform.tierMultiplier`): with 10 units a piece and a cap of 100, a member with a ×2 multiplier reaches the cap after 5 pieces, a member without one after 10. An `items` cap counts pieces, so a multiplier does not affect it.\n\nA line without a SKU is neither counted nor capped.","type":"string","enum":["items","units"]},"scope":{"description":"Whose usage the limit counts: `perMember` — each reward recipient on their own (on a rule whose `target` is `referrer`, the referrer's allowance); `allMembers` — every member together.","type":"string","enum":["perMember","allMembers"]},"countedPer":{"description":"What one allowance covers. `sku` (the default; omitting it or sending `null` means the same) gives every product the rule covers an allowance of its own: a cap of 5 on a rule covering a category means five of each product in it. `rule` gives one allowance shared by everything the rule covers, so a cap per category is a rule targeting that category; it is handed out in transaction-line order, so with 5 left a receipt of 3 milk and then 4 bread pays 3 milk and 2 bread. Always present in a response.","type":"string","nullable":true,"enum":["sku","rule"],"default":"sku"},"value":{"description":"The cap: a positive number with at most three decimal places and at most eight integer digits (99 999 999.999), the precision of a transaction line's quantity. Sent as a JSON number or a string (`5`, `\"2.350\"`); a leading zero (`05`), a trailing dot (`1.`), a fourth decimal place, zero or a boolean is rejected with `400` on `rules.N.limits.M.value` (`The limit value must be a positive number with at most three decimal places.`). A response returns it as a number.","oneOf":[{"description":"As a JSON number, e.g. `5`, with at most three decimal places; `1.0001` is rejected.","type":"number","exclusiveMinimum":true,"minimum":0,"maximum":99999999.999},{"description":"As a decimal string, e.g. `\"2.350\"`.","type":"string","pattern":"^(?=.*[1-9])(0|[1-9]\\d{0,7})(\\.\\d{1,3})?$"}]},"interval":{"description":"The calendar period the limit renews over. Absent or `null`, the limit counts over the campaign's whole lifetime; a response then carries no `interval` key at all. With one, the limit counts the current period only and starts again in the next, at midnight for a day, Monday for a week, the 1st for a month and 1 January for a year, in the store's timezone. A sale counts in the period of its `purchasedAt`, not of when it is processed, converted to the store's timezone: in a store in `Europe/Warsaw`, a sale with `purchasedAt` `2026-10-04T22:30:00Z` (Sunday) is Monday 00:30 there and counts in the new week. A store whose `timezoneStrategy` setting is `localtime` takes the time written on the receipt as it is, without converting its offset. Periods are calendar ones, not windows rolling from a member's first purchase.\n\n`interval` carries `type` alone: one period is the only length, so a `value` — even `1` — is rejected with `400` on `rules.N.limits.M.interval`. `calendarHours`, accepted by the campaign's own limits, is rejected here with `400` on `rules.N.limits.M.interval.type`.","nullable":true,"required":["type"],"properties":{"type":{"type":"string","enum":["calendarDays","calendarWeeks","calendarMonths","calendarYears"]}},"type":"object","additionalProperties":false}},"type":"object","additionalProperties":false},"UnitsRule":{"description":"How a `give_points` effect computes the units it grants.","required":["applyTo","amount"],"properties":{"applyTo":{"description":"The measure `amount` is multiplied by, read from the line target `relatedLineTarget` names, or from the whole transaction when the effect declares no `relatedLineTarget`.\n\n* `flat` — `amount` once. Booked against a line target, it pays only when the target matched at least one line, and nothing otherwise.\n* `quantity` — `amount` × the quantity bought: of the target's lines, of the units its sets consumed, or of every line of the transaction.\n* `value` — `amount` × the gross value: of the target's lines, of the lines its sets consumed, or of the whole transaction.\n* `count` — `amount` × the number of sets formed. Accepted only when `relatedLineTarget` names a line target that selects `sets`; on a `lines` target or without `relatedLineTarget` the request is rejected with `400` on `rules.N.effects.M.unitsRule.applyTo`.\n\nThe effect's `transform` scales the result afterwards.","type":"string","enum":["flat","quantity","count","value"]},"amount":{"description":"Units granted per measure unit. Must be greater than zero and at most 10 000 000; anything else is rejected with `400` on `rules.N.effects.M.unitsRule.amount`.","type":"number","exclusiveMinimum":true,"minimum":0,"maximum":10000000}},"type":"object","additionalProperties":false},"NewCondition":{"title":"New condition","required":["value","condition"],"properties":{"value":{"required":["field"],"properties":{"field":{"description":"`customer.daysSinceRegistration` is available only to `time` campaigns with the `daily` trigger strategy. It holds the whole calendar days between the member's registration and the run date, in the store timezone, and accepts only the `eq` operator with a whole number of days from 0 to 7305 (twenty years, the same ceiling as the segment criterion `member_registration_date`).","type":"string","enum":["customer.email","customer.daysSinceRegistration","customer.wallets","referrer.wallets","customer.badges","referrer.badges","transaction.items","challenge.trigger","challenge.completions","challenge.milestoneProgresses","lineTarget.count","lineTarget.qty","lineTarget.value"]},"params":{"description":"The `wallet` object applies only to the `customer.wallets` and `referrer.wallets` fields. The `walletTypeCode` field is used to precisely specify which wallet the condition should apply to. The `field` property defines which wallet field is used to retrieve the value for the condition.","type":"object","oneOf":[{"title":"wallet","required":["walletTypeCode","field"],"properties":{"walletTypeCode":{"type":"string"},"field":{"type":"string","enum":["activeUnits","spentUnits","earnedUnits","lockedUnits","blockUnits","expiredUnits"]}},"type":"object","additionalProperties":false},{"title":"badge","required":["badgeTypeId"],"properties":{"badgeTypeId":{"type":"string","format":"uuid"}},"type":"object","additionalProperties":false},{"title":"milestoneByRelationId","required":["milestoneRelationId"],"properties":{"milestoneRelationId":{"type":"string"}},"type":"object","additionalProperties":false},{"title":"milestoneById","required":["milestoneId"],"properties":{"milestoneId":{"type":"string","format":"uuid"}},"type":"object","additionalProperties":false},{"title":"lineTarget","required":["code"],"properties":{"code":{"type":"string","minLength":3,"maxLength":32}},"type":"object","additionalProperties":false}]},"filterCode":{"description":"In this field you can specify which transaction items filters you want to use in condition","type":"string"},"aggregation":{"description":"Available only for transaction.items","required":["type"],"properties":{"type":{"type":"string","enum":["sum","count"]},"field":{"type":"string"}},"type":"object","additionalProperties":false}},"type":"object","additionalProperties":false},"condition":{"$ref":"#/components/schemas/Condition"}},"type":"object"},"Limit":{"properties":{"points":{"required":["value"],"properties":{"interval":{"properties":{"type":{"type":"string"},"value":{"type":"integer","deprecated":true}},"type":"object"},"value":{"type":"number","format":"float"}},"type":"object"},"pointsPerMember":{"required":["value"],"properties":{"interval":{"properties":{"type":{"type":"string"},"value":{"type":"integer","deprecated":true}},"type":"object"},"value":{"type":"number","format":"float"}},"type":"object"},"executionsPerMember":{"required":["value"],"properties":{"interval":{"properties":{"type":{"type":"string"},"value":{"type":"integer","deprecated":true}},"type":"object"},"value":{"type":"number","format":"float"}},"type":"object"}},"type":"object"},"CampaignTranslations":{"description":"This field is used to save the name and description in the language of your choice","required":["en"],"properties":{"en":{"required":["name"],"properties":{"name":{"type":"string"},"description":{"type":"string"}},"type":"object"},"pl":{"required":["name"],"properties":{"name":{"type":"string"},"description":{"type":"string"}},"type":"object"}},"type":"object"},"PutUniqueCodeCampaignRequestBody":{"title":"RedemptionCampaign","required":["campaign"],"properties":{"campaign":{"required":["type","trigger","translations","activity","eventCodeAttribute","rules"],"properties":{"type":{"type":"string"},"trigger":{"type":"string"},"activity":{"required":["startsAt"],"properties":{"startsAt":{"type":"string"},"endsAt":{"type":"string","nullable":true}},"type":"object"},"displayOrder":{"type":"number"},"active":{"type":"boolean"},"labels":{"type":"array","items":{"properties":{"key":{"type":"string"},"value":{"type":"string"}},"type":"object"}},"translations":{"$ref":"#/components/schemas/CampaignTranslations"},"rules":{"$ref":"#/components/schemas/Rule"},"limits":{"$ref":"#/components/schemas/Limit"},"visibility":{"$ref":"#/components/schemas/Visibility"},"audience":{"$ref":"#/components/schemas/Audience"},"event":{"type":"string"},"eventCodeAttribute":{"type":"string"},"codeGenerator":{"description":"Required for generated codes.","required":["characterSet","length"],"properties":{"characterSet":{"type":"string"},"length":{"type":"integer"},"prefix":{"type":"string"}},"type":"object","additionalProperties":false},"isExternalCodeSource":{"type":"boolean"},"customFields":{"type":"object","nullable":true,"description":"Custom-field values grouped by group key. PUT semantics: omitted or null → existing values kept (no-op); empty object {} → all values cleared (validation still runs); non-empty object → wholesale entity replace (every group not in the payload is also wiped). Requires the CUSTOM_FIELD_CAMPAIGN feature flag; when disabled the field is silently ignored. See the endpoint description for full semantics.","additionalProperties":{"type":"object","additionalProperties":true}}},"type":"object","additionalProperties":false}},"type":"object","additionalProperties":false},"CampaignRequestBody":{"title":"Campaign","required":["campaign"],"properties":{"campaign":{"required":["type","trigger","translations","activity","rules"],"properties":{"triggerStrategy":{"$ref":"#/components/schemas/TriggerStrategy"},"visibility":{"$ref":"#/components/schemas/Visibility"},"audience":{"$ref":"#/components/schemas/Audience"},"rules":{"$ref":"#/components/schemas/Rule"},"transactionItemsFilters":{"description":"Field that you can use to filter transaction items, which you can then use in the condition by providing the code, only allowed for transaction trigger","type":"array","items":{"$ref":"#/components/schemas/TransactionItemsFilters"}},"limits":{"$ref":"#/components/schemas/Limit"},"type":{"type":"string","enum":["direct","referral"]},"trigger":{"type":"string","enum":["transaction","return_transaction","custom_event","internal_event","time","achievement","custom_event_unique_code"]},"event":{"description":"Allowed only for campaign with trigger event","type":"string"},"ruleClubbing":{"description":"Decides what a sale pays when several rules of this campaign cover the same transaction line. `true` — every rule pays, as campaigns always have (stacking). `false` — one benefit at a time: each line is paid by the first rule, in `rules` order, whose line-targeted `give_points` effects cover it, and the rules after it no longer see that line. Accepted only when the campaign trigger is `transaction`; on any other trigger the field is rejected with `400` (`This form should not contain extra fields.`), reported on the campaign itself with an empty `path`.\n\nNo field ranks the rules: their order in `rules` does, so put the specific rule above the general one. With `false`:\n* Resolution is per line. The jacket rule can win the jackets while the category rule still pays the gloves nothing else covers.\n* Only a rule whose conditions held, and that has a member to pay, claims lines. Lines are claimed per reward recipient: a referrer's reward for a line does not take it from the buyer.\n* A rule claims only the lines of its `give_points` effects that name a `relatedLineTarget`. An effect without one — every `pointsRule` effect — claims nothing, so it takes no line from the rules after it, and two such rules stack. It still reads what is left: placed below a rule that claimed lines, it is calculated without them. Effects other than `give_points` claim nothing.\n* A later rule's effects read the transaction without the lines claimed before it, its totals reduced accordingly; its conditions still read the whole transaction. With a condition `transaction.grossValue >= 30`, a receipt of 36 whose 20 an earlier rule claimed still passes it. A `give_points` effect all of whose lines were claimed earlier is not calculated, even one paying a flat amount; a rule left with some of its lines pays a flat amount in full.\n* There is no cascade: a winner whose per-SKU allowance (`rules[].limits`) has run out still keeps the line and pays what it can, possibly nothing. The `limits` trim what clubbing leaves.\n\nClubbing works within one campaign; two campaigns rewarding the same purchase still both pay. Omitted or `null` on `POST` means `true`. On `PUT`, omitting it or sending `null` keeps the stored value; only `true` or `false` changes it.","type":"boolean","nullable":true},"activity":{"required":["startsAt"],"properties":{"startsAt":{"type":"string"},"endsAt":{"type":"string","nullable":true}},"type":"object"},"displayOrder":{"type":"number"},"active":{"type":"boolean"},"labels":{"type":"array","items":{"required":["key","value"],"properties":{"key":{"type":"string"},"value":{"type":"string"}},"type":"object"}},"memberFilter":{"description":"Only allowed for time trigger","properties":{"strategy":{}},"type":"object"},"translations":{"$ref":"#/components/schemas/CampaignTranslations"},"customFields":{"type":"object","nullable":true,"description":"Custom-field values grouped by group key. PUT semantics: omitted or null → existing values kept (no-op); empty object {} → all values cleared (validation still runs); non-empty object → wholesale entity replace (every group not in the payload is also wiped). Requires the CUSTOM_FIELD_CAMPAIGN feature flag; when disabled the field is silently ignored. See the endpoint description for full semantics.","additionalProperties":{"type":"object","additionalProperties":true}}},"type":"object","additionalProperties":false}},"type":"object","additionalProperties":false},"TransactionItemsFilters":{"required":["code"],"properties":{"code":{"type":"string"},"name":{"type":"string"},"filters":{"type":"array","items":{"required":["value","condition"],"properties":{"value":{"required":["field"],"properties":{"field":{"type":"string","enum":["sku","name","qty","grossValue","category","maker","labels","catalogSku","catalogCategory","catalogBrand","catalogAttributes"]}},"type":"object","additionalProperties":false},"condition":{"$ref":"#/components/schemas/Condition"},"dataEnteredManually":{"type":"boolean"}},"type":"object","additionalProperties":false}}},"type":"object","additionalProperties":false},"MultiReferralCampaignRequestBody":{"title":"MultiReferralCampaign","description":"When campaign is referral type, you can specify multi level","required":["campaign"],"properties":{"campaign":{"required":["multiLevel","type","trigger","translations","activity","rules"],"properties":{"triggerStrategy":{"$ref":"#/components/schemas/TriggerStrategy"},"visibility":{"$ref":"#/components/schemas/Visibility"},"audience":{"$ref":"#/components/schemas/Audience"},"rules":{"$ref":"#/components/schemas/Rule"},"transactionItemsFilters":{"description":"Field that you can use to filter transaction items, which you can then use in the condition by providing the code, only allowed for transaction trigger","type":"array","items":{"$ref":"#/components/schemas/TransactionItemsFilters"}},"limits":{"$ref":"#/components/schemas/Limit"},"type":{"type":"string","enum":["direct","referral"]},"multiLevel":{"description":"How many members up the referrer chain one event runs the campaign for. Each level is a separate run for a different member, and every effect in `rules` is applied once per run — so a value above 1 multiplies the effects a single event produces. The chain ends early at the first member who has no referrer.","type":"number"},"trigger":{"type":"string","enum":["transaction","return_transaction","custom_event","internal_event","time","achievement","custom_event_unique_code"]},"event":{"type":"string"},"ruleClubbing":{"description":"Decides what a sale pays when several rules of this campaign cover the same transaction line. `true` — every rule pays, as campaigns always have (stacking). `false` — one benefit at a time: each line is paid by the first rule, in `rules` order, whose line-targeted `give_points` effects cover it, and the rules after it no longer see that line. Accepted only when the campaign trigger is `transaction`; on any other trigger the field is rejected with `400` (`This form should not contain extra fields.`), reported on the campaign itself with an empty `path`.\n\nNo field ranks the rules: their order in `rules` does, so put the specific rule above the general one. With `false`:\n* Resolution is per line. The jacket rule can win the jackets while the category rule still pays the gloves nothing else covers.\n* Only a rule whose conditions held, and that has a member to pay, claims lines. Lines are claimed per reward recipient: a referrer's reward for a line does not take it from the buyer.\n* A rule claims only the lines of its `give_points` effects that name a `relatedLineTarget`. An effect without one — every `pointsRule` effect — claims nothing, so it takes no line from the rules after it, and two such rules stack. It still reads what is left: placed below a rule that claimed lines, it is calculated without them. Effects other than `give_points` claim nothing.\n* A later rule's effects read the transaction without the lines claimed before it, its totals reduced accordingly; its conditions still read the whole transaction. With a condition `transaction.grossValue >= 30`, a receipt of 36 whose 20 an earlier rule claimed still passes it. A `give_points` effect all of whose lines were claimed earlier is not calculated, even one paying a flat amount; a rule left with some of its lines pays a flat amount in full.\n* There is no cascade: a winner whose per-SKU allowance (`rules[].limits`) has run out still keeps the line and pays what it can, possibly nothing. The `limits` trim what clubbing leaves.\n\nClubbing works within one campaign; two campaigns rewarding the same purchase still both pay. Omitted or `null` on `POST` means `true`. On `PUT`, omitting it or sending `null` keeps the stored value; only `true` or `false` changes it.","type":"boolean","nullable":true},"activity":{"required":["startsAt"],"properties":{"startsAt":{"type":"string"},"endsAt":{"type":"string","nullable":true}},"type":"object"},"displayOrder":{"type":"number"},"active":{"type":"boolean"},"labels":{"type":"array","items":{"required":["key","value"],"properties":{"key":{"type":"string"},"value":{"type":"string"}},"type":"object"}},"memberFilter":{"description":"Only allowed for time trigger","properties":{"strategy":{}},"type":"object"},"translations":{"$ref":"#/components/schemas/CampaignTranslations"},"customFields":{"type":"object","nullable":true,"description":"Custom-field values grouped by group key. PUT semantics: omitted or null → existing values kept (no-op); empty object {} → all values cleared (validation still runs); non-empty object → wholesale entity replace (every group not in the payload is also wiped). Requires the CUSTOM_FIELD_CAMPAIGN feature flag; when disabled the field is silently ignored. See the endpoint description for full semantics.","additionalProperties":{"type":"object","additionalProperties":true}}},"type":"object","additionalProperties":false}},"type":"object","additionalProperties":false},"LeaderboardCampaignRequestBody":{"title":"LeaderboardCampaign","required":["campaign"],"properties":{"campaign":{"required":["type","trigger","translations","activity","leaderboard"],"properties":{"triggerStrategy":{"$ref":"#/components/schemas/TriggerStrategy"},"visibility":{"$ref":"#/components/schemas/Visibility"},"audience":{"$ref":"#/components/schemas/Audience"},"rules":{"$ref":"#/components/schemas/Rule"},"type":{"type":"string","enum":["direct","referral"]},"trigger":{"type":"string"},"activity":{"required":["startsAt"],"properties":{"startsAt":{"type":"string"},"endsAt":{"type":"string","nullable":true}},"type":"object"},"displayOrder":{"type":"number"},"active":{"type":"boolean"},"labels":{"type":"array","items":{"required":["key","value"],"properties":{"key":{"type":"string"},"value":{"type":"string"}},"type":"object"}},"translations":{"$ref":"#/components/schemas/CampaignTranslations"},"leaderboard":{"$ref":"#/components/schemas/Leaderboard"},"customFields":{"type":"object","nullable":true,"description":"Custom-field values grouped by group key. PUT semantics: omitted or null → existing values kept (no-op); empty object {} → all values cleared (validation still runs); non-empty object → wholesale entity replace (every group not in the payload is also wiped). Requires the CUSTOM_FIELD_CAMPAIGN feature flag; when disabled the field is silently ignored. See the endpoint description for full semantics.","additionalProperties":{"type":"object","additionalProperties":true}}},"type":"object","additionalProperties":false}},"type":"object","additionalProperties":false},"Leaderboard":{"properties":{"metric":{"required":["type","walletTypeCode"],"properties":{"type":{"type":"string","enum":["earned_units_cumulative"]},"walletTypeCode":{"type":"string"}},"type":"object"},"cycles":{"description":"Optional cycle definition. Without `cycles` the leaderboard is timeless (a single open-ended iteration). Only one cycle is supported.\n","type":"array","maxItems":1,"items":{"required":["type"],"properties":{"type":{"type":"string","enum":["end_of_the_month"]},"code":{"description":"Caller-defined cycle code returned alongside leaderboard rankings. Optional — when omitted the server applies a default code.\n","type":"string"}},"type":"object"}},"groupingField":{"description":"Splits the leaderboard into sub-rankings per distinct group value. Members without a resolvable group value are excluded from the grouped leaderboard. The grouping configuration is set at campaign creation and is read-only afterwards. Submitting a `groupingField` on update (PUT) that differs from the stored one — including adding or removing it — is rejected with HTTP 400 to keep historical iterations consistent. An identical `groupingField` (or none) is accepted.\n","required":["type","key"],"properties":{"type":{"type":"string","description":"`address_field` groups by an address sub-field on the member profile; `custom_attribute` groups by a member custom attribute key.\n","enum":["address_field","custom_attribute"]},"key":{"type":"string","description":"For `address_field`, one of: `city`, `country`, `province`, `postal`. For `custom_attribute`, the custom attribute key on the member profile.\n"}},"type":"object"}},"type":"object"},"ChallengeCampaignRequestBody":{"title":"ChallengeCampaign","required":["campaign"],"properties":{"campaign":{"required":["type","trigger","translations","activity","rules","milestones"],"properties":{"triggerStrategy":{"$ref":"#/components/schemas/TriggerStrategy"},"visibility":{"$ref":"#/components/schemas/Visibility"},"audience":{"$ref":"#/components/schemas/Audience"},"rules":{"$ref":"#/components/schemas/Rule"},"limits":{"$ref":"#/components/schemas/Limit"},"type":{"type":"string","enum":["direct","referral"]},"trigger":{"type":"string"},"activity":{"required":["startsAt"],"properties":{"startsAt":{"type":"string"},"endsAt":{"type":"string","nullable":true}},"type":"object"},"displayOrder":{"type":"number"},"active":{"type":"boolean"},"labels":{"type":"array","items":{"required":["key","value"],"properties":{"key":{"type":"string"},"value":{"type":"string"}},"type":"object"}},"translations":{"$ref":"#/components/schemas/CampaignTranslations"},"challengeLimit":{"$ref":"#/components/schemas/ChallengeLimit"},"challengeAvailability":{"$ref":"#/components/schemas/AchievementActivity"},"milestones":{"type":"array","items":{"$ref":"#/components/schemas/Milestone"}},"customFields":{"type":"object","nullable":true,"description":"Custom-field values grouped by group key. PUT semantics: omitted or null → existing values kept (no-op); empty object {} → all values cleared (validation still runs); non-empty object → wholesale entity replace (every group not in the payload is also wiped). Requires the CUSTOM_FIELD_CAMPAIGN feature flag; when disabled the field is silently ignored. See the endpoint description for full semantics.","additionalProperties":{"type":"object","additionalProperties":true}}},"type":"object","additionalProperties":false}},"type":"object","additionalProperties":false},"ChallengeLimit":{"properties":{"interval":{"$ref":"#/components/schemas/LimitInterval"},"value":{"type":"integer"}},"type":"object"},"LimitInterval":{"properties":{"type":{"type":"string"},"value":{"type":"integer"}},"type":"object"},"AchievementActivity":{"properties":{"data":{"oneOf":[{"type":"string"},{"properties":{"from":{"type":"string"},"to":{"type":"string"}},"type":"object"},{"type":"array","items":{"type":"string"}}]},"operator":{"type":"string"}},"type":"object"},"Milestone":{"properties":{"translations":{"properties":{"en":{"properties":{"name":{"type":"string"},"description":{"type":"string"}},"type":"object"}},"type":"object"},"milestoneId":{"type":"string"},"completeRule":{"$ref":"#/components/schemas/CompleteRule"},"aggregation":{"$ref":"#/components/schemas/Aggregation"},"uniqueReferee":{"type":"boolean"},"event":{"type":"string"},"trigger":{"type":"string"},"type":{"type":"string"},"conditions":{"type":"array","items":{"type":"object"}},"limit":{"$ref":"#/components/schemas/ChallengeLimit"}},"type":"object"},"CompleteRule":{"properties":{"periodGoal":{"oneOf":[{"type":"string"},{"type":"number"}]},"period":{"$ref":"#/components/schemas/Period"},"uniqueAttribute":{"type":"string"}},"type":"object"},"Period":{"properties":{"type":{"type":"string"},"consecutive":{"type":"integer"},"value":{"type":"integer"}},"type":"object"},"Aggregation":{"properties":{"type":{"type":"string"},"rule":{"type":"string"}},"type":"object"}},"responses":{"NoContent":{"description":"Success. No content."},"BadRequest":{"description":"Bad request.","content":{"application/json":{"schema":{"required":["code","message","errors"],"properties":{"code":{"type":"integer"},"message":{"type":"string"},"errors":{"type":"array","items":{"required":["message","parameters","plural","code","path"],"properties":{"message":{"type":"string"},"parameters":{},"plural":{"type":"integer","nullable":true},"code":{"type":"string"},"path":{"type":"string"}},"type":"object"}}},"type":"object"}}}},"Unauthorized":{"description":"Unauthorized","content":{"application/json":{"schema":{"required":["code","message"],"properties":{"code":{"type":"integer"},"message":{"type":"string"}},"type":"object"}}}},"AccessDenied":{"description":"Forbidden.","content":{"application/json":{"schema":{"required":["code","message"],"properties":{"code":{"type":"integer"},"message":{"type":"string"}},"type":"object"}}}},"NotFound":{"description":"Not found.","content":{"application/json":{"schema":{"required":["code","message"],"properties":{"code":{"type":"integer"},"message":{"type":"string"}},"type":"object"}}}}}},"paths":{"/api/{storeCode}/campaign/{campaign}":{"put":{"tags":["Campaign"],"summary":"Update campaign’s configuration","description":"<label style=\"background-color: #D4EDBC;padding:5px;\">Management</label><br><br>\nThis method allows changing the information about a specific Campaign.<br><br>\n<strong>Rules semantics on PUT:</strong> <ul>\n  <li><code>rules</code> replaces the whole rule list. Edit a campaign as <code>GET</code> → change →\n    <code>PUT</code>, sending back every rule you keep with its <code>ruleKey</code> and its\n    <code>limits</code> as <code>GET</code> returned them.</li>\n  <li>A rule with its <code>ruleKey</code> is updated in place and keeps the usage its per-SKU\n    <code>limits</code> counted. A rule without one is saved as new, its members' allowances starting\n    from zero; a stored rule left out is deleted. Neither raises an error.</li>\n  <li>A rule's <code>limits</code> are replaced too: a rule sent without them loses them, and a limit\n    sent without its <code>interval</code> becomes a lifetime limit.</li>\n  <li>Rules are stored in array order, and that order decides which rule pays a line when\n    <code>ruleClubbing</code> is <code>false</code>. Omitting <code>ruleClubbing</code> keeps the\n    stored value.</li>\n  <li>A webhook payload carries no <code>ruleKey</code> and is not a basis for this request.</li>\n</ul> <strong>Custom-fields semantics on PUT:</strong> <ul>\n  <li><code>customFields</code> omitted from the request body → existing custom-field\n    values on the campaign are <strong>kept untouched</strong> (no-op; no validation\n    runs).</li>\n  <li><code>customFields: null</code> → existing values are <strong>kept untouched</strong>\n    (treated identically to omitting the field).</li>\n  <li><code>customFields: {}</code> (explicit empty object) → all existing custom-field\n    values on the campaign are <strong>cleared</strong>. Required-field validation still\n    runs against the empty payload, so this fails with 400 if any active campaign\n    custom-field group contains a required field.</li>\n  <li><code>customFields: { groupKey: { fieldKey: value, ... }, ... }</code> →\n    <strong>wholesale entity replace</strong>. The campaign's entire custom-field state\n    is deleted first, then only the groups present in the payload are persisted.\n    Consequence: any group NOT listed in the payload is also wiped, even if it had\n    values before. Required-field validation runs against the payload as a whole.</li>\n</ul> The feature flag <code>CUSTOM_FIELD_CAMPAIGN</code> must be enabled; when disabled the <code>customFields</code> key is silently ignored regardless of value.<br><br>\nFor granular per-group or per-key updates that preserve other groups, use the bulk patch endpoint (<code>PATCH /api/{storeCode}/custom-fields/campaign/{campaignId}</code>).\n","operationId":"_campaignPut","parameters":[{"$ref":"#/components/parameters/storeCode"},{"$ref":"#/components/parameters/campaign"}],"requestBody":{"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/AchievementCampaignRequestBody"},{"$ref":"#/components/schemas/PutUniqueCodeCampaignRequestBody"},{"$ref":"#/components/schemas/CampaignRequestBody"},{"$ref":"#/components/schemas/MultiReferralCampaignRequestBody"},{"$ref":"#/components/schemas/LeaderboardCampaignRequestBody"},{"$ref":"#/components/schemas/ChallengeCampaignRequestBody"}]}}}},"responses":{"204":{"$ref":"#/components/responses/NoContent"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/AccessDenied"},"404":{"$ref":"#/components/responses/NotFound"}}}}}}
````


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://help.openloyalty.io/api-reference/campaign/update-campaigns-configuration.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
