{"openapi":"3.0.1","info":{"title":"Rules engine","description":"API for managing rules engine, allowing for creation, retrieval, updating, and deletion of rule information.\n <b><h1>Deprecated endpoints:</h1></b><table border=\"1px;\"><tr><th style=\"text-align: center; vertical-align: middle;\"><b><h3>Uri</b></h3></th><th><b>Method</b></th><th><b><h3>Since</h3></b></th><th><b><h3>End of life</h3></b></th></tr></table>","license":{"name":"Bluestone PIM"},"version":"1-apps"},"servers":[{"url":"api.test.bluestonepim.com/rules-engine"}],"tags":[{"name":"Get rules information","description":"To Get rules information."},{"name":"Create/Update/Delete rules","description":"To interact with rules."},{"name":"Manage rule groups","description":"To create, rename, reorder, delete, and assign rules to rule groups."},{"name":"Manage relation cleanup","description":"To read and switch the per-relation-type managed cleanup setting."}],"paths":{"/rules/list":{"post":{"tags":["Get rules information"],"summary":"Retrieve a list of rules based on search criteria","description":"Searches for rules matching the specified criteria, such as rule name, action, status, and more. It allows filtering, sorting, and pagination of rule results.","operationId":"getRuleList","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BodyRequestForSearchRules"},"examples":{"getAllRules":{"summary":"Get All Rules - Basic","description":"Retrieve all rules with default pagination (page 0, 20 items).","value":{}},"paginationAndSorting":{"summary":"Pagination and Sorting","description":"Get rules with custom pagination and sort by modification date descending.","value":{"page":0,"pageSize":50,"sortBy":"modifiedAt","sortDirection":"DESC"}},"filterByAction":{"summary":"Filter by Action Type","description":"Get all rules with specific action type (e.g., productToCategory).","value":{"action":"productToCategory","page":0,"pageSize":20,"sortBy":"ruleName","sortDirection":"ASC"}},"filterByStatus":{"summary":"Filter by Status","description":"Get only active or inactive rules.","value":{"status":"active","page":0,"pageSize":100}},"filterByRuleName":{"summary":"Search by Rule Name","description":"Search rules by name using partial match.","value":{"ruleName":"Auto-assign","sortBy":"ruleName","sortDirection":"ASC"}},"filterByTargetId":{"summary":"Filter by Target ID","description":"Get rules targeting a specific entity (category, attribute, or PDF template ID).","value":{"targetId":"65f8a3b2c1d4e5f6a7b8c9d0","action":"productToCategory"}},"filterByTargetName":{"summary":"Search by Target Name","description":"Search rules by target entity name (category name, attribute name, or PDF template name).","value":{"targetName":"Electronics","action":"productToCategory"}},"filterByContext":{"summary":"Filter by Context","description":"Get rules for specific language contexts.","value":{"context":["en","th"],"status":"active"}},"filterByPriority":{"summary":"Filter by Rule Priority","description":"Get rules with specific priority group.","value":{"rulePriority":"Priority group 1","action":"productToCategory","status":"active"}},"filterIssueAnyIssue":{"summary":"Issue: ANY_ISSUE - All broken rules","description":"Retrieve all rules with any broken status (broken = 1, 2, 3, or 4).","value":{"issue":"ANY_ISSUE","page":0,"pageSize":50,"sortBy":"modifiedAt","sortDirection":"DESC"}},"filterIssueBrokenReference":{"summary":"Issue: BROKEN_REFERENCE - Broken reference rules","description":"Retrieve only rules that have a broken reference (e.g. target category or attribute was deleted).","value":{"issue":"BROKEN_REFERENCE","sortBy":"modifiedAt","sortDirection":"DESC"}},"filterIssueQueryLimitExceeded":{"summary":"Issue: QUERY_LIMIT_EXCEEDED - Query limit exceeded rules","description":"Retrieve only rules where the query returned too many results and exceeded the allowed limit.","value":{"issue":"QUERY_LIMIT_EXCEEDED","sortBy":"modifiedAt","sortDirection":"DESC"}},"filterIssueInfiniteLoop":{"summary":"Issue: INFINITE_LOOP - Infinite loop rules","description":"Retrieve only rules that have been detected to cause an infinite processing loop.","value":{"issue":"INFINITE_LOOP","sortBy":"modifiedAt","sortDirection":"DESC"}},"filterIssueProductLimitExceeded":{"summary":"Issue: PRODUCT_LIMIT_EXCEEDED - Product limit exceeded rules","description":"Retrieve only rules where the number of matched products exceeded the allowed product limit.","value":{"issue":"PRODUCT_LIMIT_EXCEEDED","sortBy":"modifiedAt","sortDirection":"DESC"}},"filterByAttributeId":{"summary":"Find Rules Using Specific Attribute","description":"Get rules that use a specific attribute in their query conditions.","value":{"attributeId":"676e328cee4eaf798892ebe8","page":0,"pageSize":20}},"filterByAttributeValues":{"summary":"Find Rules with Specific Attribute Values","description":"Get rules that check for specific values of an attribute in their query.","value":{"attributeId":"676e328cee4eaf798892ebe8","attributeValues":["Electronics","Gadgets"],"action":"productToCategory"}},"excludeAttributeId":{"summary":"Exclude Rules Using Specific Attribute","description":"Get rules that do NOT use a specific attribute in their query.","value":{"excludeAttributeId":"676e328cee4eaf798892ebe9","action":"editValue"}},"excludeAttributeValues":{"summary":"Exclude Rules with Specific Attribute Values","description":"Get rules that do NOT contain specific values for an attribute.","value":{"excludeAttributeId":"676e328cee4eaf798892ebe9","excludeAttributeValues":["Deprecated","Old"],"status":"active"}},"lastUpdateDaysAgo":{"summary":"Filter by Last Update (Days Ago)","description":"Get rules updated within the last N days.","value":{"lastUpdateFilter":[{"type":"DAYS_AGO","value":7}],"sortBy":"modifiedAt","sortDirection":"DESC"}},"lastUpdateDateRange":{"summary":"Filter by Date Range","description":"Get rules updated within a specific date range using GREATER_EQUAL and LESS_EQUAL.","value":{"lastUpdateFilter":[{"type":"GREATER_EQUAL","dateMilis":1707868800000},{"type":"LESS_EQUAL","dateMilis":1708473600000}]}},"complexFilterCombination":{"summary":"Complex Filter Combination","description":"Combine multiple filters for advanced search (action + status + attribute + date range).","value":{"action":"editValue","status":"active","issue":"ANY_ISSUE","attributeId":"676e328cee4eaf798892ebea","context":["en"],"lastUpdateFilter":[{"type":"DAYS_AGO","value":30}],"page":0,"pageSize":50,"sortBy":"modifiedAt","sortDirection":"DESC"}},"aiEnrichRules":{"summary":"Get All AI Enrich Rules","description":"Retrieve all AI enrichment rules sorted by name.","value":{"action":"aiEnrich","status":"active","sortBy":"ruleName","sortDirection":"ASC"}},"aiLinguistRules":{"summary":"Get All AI Linguist Rules","description":"Retrieve all AI linguist rules sorted by name.","value":{"action":"aiLinguist","status":"active","sortBy":"ruleName","sortDirection":"ASC"}},"syncProductsRules":{"summary":"Get Sync Products Rules","description":"Get all product sync rules with their target configurations.","value":{"action":"syncProducts","status":"active","page":0,"pageSize":20}},"recentlyModified":{"summary":"Recently Modified Rules","description":"Get rules modified in the last 24 hours.","value":{"lastUpdateFilter":[{"type":"DAYS_AGO","value":1}],"sortBy":"modifiedAt","sortDirection":"DESC","pageSize":10}},"multipleActionsFilter":{"summary":"Get Rules by Action and Target","description":"Combine action type with targetId to find specific rule configurations.","value":{"action":"productToCategory","targetId":"65f8a3b2c1d4e5f6a7b8c9d0","status":"active","issue":"ANY_ISSUE"}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"oneOf":[{"$ref":"#/components/schemas/RulesForAssignToCategory"},{"$ref":"#/components/schemas/RulesForGeneratePdf"},{"$ref":"#/components/schemas/RulesEditValue"},{"$ref":"#/components/schemas/RulesAddRelation"},{"$ref":"#/components/schemas/RulesChangeProductStatus"},{"$ref":"#/components/schemas/RulesSyncProducts"},{"$ref":"#/components/schemas/RulesAiEnrich"},{"$ref":"#/components/schemas/RulesAiLinguist"}]},"title":"List of results."},"total":{"type":"integer","format":"int32","title":"Total of results."},"broken_all":{"type":"integer","format":"int32","title":"Total number of broken rules."}}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}}}}}},"security":[{"oauth2":[]}]}},"/rules":{"post":{"tags":["Create/Update/Delete rules"],"summary":"Create a new rule","description":"Creates a new rule in the rules engine, defining its action, target, query conditions, triggers, context and more.","operationId":"createRules","requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/RulesForAssignToCategory"},{"$ref":"#/components/schemas/RulesForGeneratePdf"},{"$ref":"#/components/schemas/RulesEditValue"},{"$ref":"#/components/schemas/RulesAddRelation"},{"$ref":"#/components/schemas/RulesChangeProductStatus"},{"$ref":"#/components/schemas/RulesSyncProducts"},{"$ref":"#/components/schemas/RulesAiEnrich"},{"$ref":"#/components/schemas/RulesAiLinguist"}]},"examples":{"productToCategory":{"summary":"Assign Products to Category","description":"Automatically assign products to a specific category based on query conditions and triggers.","value":{"ruleName":"Auto-assign Electronics to Category","targetId":"65f8a3b2c1d4e5f6a7b8c9d0","status":"active","action":"productToCategory","query":{"children":[{"termType":"attribute","context":"en","attributeId":"676e328cee4eaf798892ebe8","filterType":"is","value":"Electronics","attributeType":"text"}],"matchRule":"all","contextFallback":true},"triggers":[{"entity":"product","event_type":"created","id":"PRODUCT"},{"entity":"attribute","event_type":"changed","id":"676e328cee4eaf798892ebe8"}],"runtime":"event","unassigned_category":"false","context":["en"],"rulePriority":"Priority group 1","autoTrigger":"true"}},"generatePdf":{"summary":"Generate PDF for Products","description":"Generate PDFs for products matching specific criteria on a schedule.","value":{"ruleName":"Weekly Product Catalog PDF","targetId":"65f8a3b2c1d4e5f6a7b8c9d1","status":"active","action":"generatePdf","query":{"children":[{"termType":"attribute","context":"en","attributeId":"676e328cee4eaf798892ebe9","filterType":"is","value":true,"attributeType":"boolean"}],"matchRule":"all","contextFallback":true},"triggers":[{"entity":"product","event_type":"changed","id":"PRODUCT","source":"Product"}],"runtime":"schedule","context":["en"]}},"editValue":{"summary":"Edit Attribute Value - Set Value","description":"Set attribute values for products matching query conditions.","value":{"ruleName":"Set Default Brand for New Products","targetId":"676e328cee4eaf798892ebea","status":"active","action":"editValue","targetValue":["Default Brand"],"targetValueOperation":"setValue","query":{"children":[{"termType":"attribute","context":"en","attributeId":"676e328cee4eaf798892ebeb","filterType":"isEmpty","attributeType":"text"}],"matchRule":"all","contextFallback":true},"triggers":[{"entity":"product","event_type":"created","id":"PRODUCT"}],"runtime":"event","context":["en"],"rulePriority":"Priority group 1","autoTrigger":"true"}},"editValueAddRemove":{"summary":"Edit Attribute Value - Add/Remove Values","description":"Add or remove specific values from multi-value attributes.","value":{"ruleName":"Add Featured Tag to Premium Products","targetId":"676e328cee4eaf798892ebec","status":"active","action":"editValue","targetValueAdd":["Featured","Premium"],"targetValueRemove":["Basic"],"targetValueOperation":"addRemove","query":{"children":[{"termType":"attribute","context":"en","attributeId":"676e328cee4eaf798892ebed","filterType":"greaterThan","value":1000,"attributeType":"number"}],"matchRule":"all","contextFallback":true},"triggers":[{"entity":"attribute","event_type":"changed","id":"676e328cee4eaf798892ebed"}],"runtime":"event","context":["en"],"rulePriority":"Priority group 2","autoTrigger":"true"}},"addRelation":{"summary":"Add Product Relations","description":"Automatically create relations between products based on matching criteria.","value":{"ruleName":"Link Accessories to Main Products","targetId":"65f8a3b2c1d4e5f6a7b8c9d2","status":"active","action":"addRelation","queryFrom":{"children":[{"termType":"attribute","context":"en","attributeId":"676e328cee4eaf798892ebee","filterType":"is","value":"Main Product","attributeType":"text"}],"matchRule":"all","contextFallback":true},"queryTo":{"children":[{"termType":"attribute","context":"en","attributeId":"676e328cee4eaf798892ebee","filterType":"is","value":"Accessory","attributeType":"text"}],"matchRule":"all","contextFallback":true},"triggers":[{"entity":"product","event_type":"created","id":"PRODUCT"},{"entity":"attribute","event_type":"changed","id":"676e328cee4eaf798892ebee"}],"runtime":"event","context":["en"],"autoTrigger":"true"}},"changeProductStatus":{"summary":"Change Product Status","description":"Automatically publish or draft products based on conditions.","value":{"ruleName":"Auto-publish Completed Products","targetId":"published","status":"active","action":"changeProductStatus","revertStatus":"false","query":{"children":[{"termType":"attribute","context":"en","attributeId":"676e328cee4eaf798892ebef","filterType":"is","value":true,"attributeType":"boolean"}],"matchRule":"all","contextFallback":true},"triggers":[{"entity":"attribute","event_type":"changed","id":"676e328cee4eaf798892ebef"}],"runtime":"event","context":["en"],"autoTrigger":"true"}},"syncProducts":{"summary":"Sync Products - Event-based","description":"Sync products to external systems when triggered by events.","value":{"ruleName":"Sync Published Products to E-commerce","targetId":"published","status":"active","action":"syncProducts","runtime":"event","query":{"children":[{"termType":"productStatus","filterType":"is","value":"published"}],"matchRule":"all","contextFallback":true},"triggers":[{"entity":"product","event_type":"changed","id":"PRODUCT"},{"entity":"product","event_type":"published","id":"PRODUCT"}],"context":["en"],"autoTrigger":"true"}},"syncProductsSchedule":{"summary":"Sync Products - Scheduled Daily","description":"Sync products on a daily schedule at specific times.","value":{"ruleName":"Daily Product Sync at 2 AM","targetId":"published","status":"active","action":"syncProducts","runtime":"day","scheduleDay":["mon","tue","wed","thu","fri"],"scheduleTime":"02:00","query":{"children":[{"termType":"productStatus","filterType":"is","value":"published"}],"matchRule":"all","contextFallback":true},"triggers":[],"context":["en"]}},"aiEnrich":{"summary":"AI Enrich - Event-based","description":"Enrich products using AI when specific attributes change.","value":{"ruleName":"AI Enrich Product Descriptions","targetId":"be2f3708-e0b1-4481-b9f7-9bf76664f2c0","status":"active","action":"aiEnrich","productLimit":100,"runtime":"event","query":{"children":[{"termType":"attribute","context":"en","attributeId":"676e328cee4eaf798892ebf0","filterType":"isEmpty","attributeType":"text"}],"matchRule":"all","contextFallback":true},"triggers":[{"entity":"product","event_type":"changed","id":"NAME"},{"entity":"attribute","event_type":"changed","id":"676e328cee4eaf798892ebf0"}],"context":["-"]}},"aiLinguist":{"summary":"AI Linguist - Event-based","description":"Run an AI linguist template (translate | improve-text | spell-checker) against products when specific attributes change. Same payload shape as aiEnrich; targetId is an ai-linguist template and the aiAction is resolved from the template at run time.","value":{"ruleName":"AI Linguist Translate Product Descriptions","targetId":"be2f3708-e0b1-4481-b9f7-9bf76664f2c2","status":"active","action":"aiLinguist","productLimit":100,"runtime":"event","query":{"children":[{"termType":"attribute","context":"en","attributeId":"676e328cee4eaf798892ebf0","filterType":"isEmpty","attributeType":"text"}],"matchRule":"all","contextFallback":true},"triggers":[{"entity":"product","event_type":"changed","id":"NAME"},{"entity":"attribute","event_type":"changed","id":"676e328cee4eaf798892ebf0"}],"context":["-"]}},"aiEnrichSchedule":{"summary":"AI Enrich - Hourly Schedule","description":"Enrich products using AI on an hourly schedule with product limit.","value":{"ruleName":"Hourly AI Enrich for Incomplete Products","targetId":"be2f3708-e0b1-4481-b9f7-9bf76664f2c1","status":"active","action":"aiEnrich","productLimit":50,"runtime":"hourly","query":{"children":[{"termType":"attribute","context":"en","attributeId":"676e328cee4eaf798892ebf1","filterType":"isEmpty","attributeType":"text"}],"matchRule":"all","contextFallback":true},"triggers":[],"context":["-"]}},"aiLinguistSchedule":{"summary":"AI Linguist - Hourly Schedule","description":"Run an AI linguist template on an hourly schedule with product limit. targetId is an ai-linguist template; the aiAction (translate | improve-text | spell-checker) is resolved from the template at run time.","value":{"ruleName":"Hourly AI Linguist for Incomplete Products","targetId":"be2f3708-e0b1-4481-b9f7-9bf76664f2c3","status":"active","action":"aiLinguist","productLimit":50,"runtime":"hourly","query":{"children":[{"termType":"attribute","context":"en","attributeId":"676e328cee4eaf798892ebf1","filterType":"isEmpty","attributeType":"text"}],"matchRule":"all","contextFallback":true},"triggers":[],"context":["-"]}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"schema":{"anyOf":[{"$ref":"#/components/schemas/RulesForAssignToCategory"},{"$ref":"#/components/schemas/RulesForGeneratePdf"},{"$ref":"#/components/schemas/RulesEditValue"},{"$ref":"#/components/schemas/RulesAddRelation"},{"$ref":"#/components/schemas/RulesChangeProductStatus"},{"$ref":"#/components/schemas/RulesSyncProducts"},{"$ref":"#/components/schemas/RulesAiEnrich"},{"$ref":"#/components/schemas/RulesAiLinguist"}]}}},"examples":{"ruleInAGroup":{"summary":"An editValue rule that belongs to a group - both group fields come back, groupName resolved at read time","value":{"ruleId":"11f0a2b4-4b7d-11ef-9c3a-0242ac120002","ruleName":"Normalise unit of measure","action":"editValue","status":"active","rulePriority":"Priority group 3","targetId":"attr-uom","targetValue":["pcs"],"context":["en"],"modifiedAt":"1739088000000","groupId":"a3f1c8e2-4b7d-11ef-9c3a-0242ac120002","groupName":"Pricing"}},"ruleWithNoGroup":{"summary":"The same rule unassigned - BOTH group fields are absent, never null and never an empty string. Test for the key, not for a falsy value","value":{"ruleId":"11f0a2b4-4b7d-11ef-9c3a-0242ac120002","ruleName":"Normalise unit of measure","action":"editValue","status":"active","rulePriority":"Priority group 3","targetId":"attr-uom","targetValue":["pcs"],"context":["en"],"modifiedAt":"1739088000000"}}}}}},"400":{"description":"Bad Request. Each cause returns its own message. The groupId causes below are the ones a group selector can trigger. Membership is validated before anything is written, so a 400 means the rule was not modified at all.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}},"examples":{"unknownGroup":{"summary":"groupId does not match an existing group","value":{"message":"Group with ID a3f1c8e2-4b7d-11ef-9c3a-0242ac120002 not found"}},"malformedGroupId":{"summary":"groupId is neither a UUID nor the UNASSIGNED literal - same message as an unknown group, since both mean 'no such group'","value":{"message":"Group with ID not-a-uuid not found"}},"emptyGroupId":{"summary":"groupId sent as null or empty string - send \"UNASSIGNED\" to unassign, or omit the field to leave the group unchanged","value":{"message":"groupId must be a group id, or \"UNASSIGNED\" to unassign. null and empty string are not accepted -- omit the field to leave the rule's group unchanged"}}}}}},"403":{"description":"Forbidden","content":{}},"409":{"description":"Conflict. The request is well-formed, but another rule in this organization already occupies the same uniqueness slot for this action. The slot differs per action -- target template for generatePdf, target plus query hash for addRelation and changeProductStatus, target attribute plus context for editValue, template plus runtime for aiEnrich and aiLinguist. Every duplicate-rule message uses this status; it was 400 before this release.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}},"examples":{"duplicateTemplate":{"summary":"generatePdf -- another rule already targets this template","value":{"message":"Another rule for generate PDF already exists with this target template (tmpl-invoice)."}},"duplicateTemplateAndRuntime":{"summary":"aiEnrich and aiLinguist -- uniqueness is (template, runtime) per action","value":{"message":"Another rule for aiEnrich already exists with this template_id and runtime (tmpl-1, event)."}},"duplicateQueryAndTarget":{"summary":"addRelation, syncProducts and changeProductStatus -- the conflicting rule is named so the client can link to it","value":{"message":"A rule with the same query and target (rel-accessories) already exists. Please modify your query or check rule ID: 11f0a2b4-4b7d-11ef-9c3a-0242ac120002 and rule name:Accessories."}}}}}}},"security":[{"oauth2":[]}]}},"/rules/{ruleId}":{"get":{"tags":["Get rules information"],"summary":"Retrieve a specific rule by its ID","description":"Retrieves detailed information about a specific rule, identified by its unique rule ID.","operationId":"getRulesById","parameters":[{"name":"ruleId","in":"path","description":"Internal identifier for a rule to find.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/RulesForAssignToCategory"},{"$ref":"#/components/schemas/RulesForGeneratePdf"},{"$ref":"#/components/schemas/RulesEditValue"},{"$ref":"#/components/schemas/RulesAddRelation"},{"$ref":"#/components/schemas/RulesChangeProductStatus"},{"$ref":"#/components/schemas/RulesSyncProducts"},{"$ref":"#/components/schemas/RulesAiEnrich"},{"$ref":"#/components/schemas/RulesAiLinguist"}]}}}},"400":{"description":"Bad Request. ruleId in the path is not a UUID. GET, PUT and DELETE all answer the same way -- before this release GET and PUT passed the value straight to the lookup and answered 404 instead.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}},"example":{"message":"invalid ruleId"}}}},"403":{"description":"Forbidden","content":{}},"404":{"description":"Not Found. ruleId is a well-formed UUID but no rule in this organization has it. One wording on every verb -- GET, PUT and DELETE all return this message.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}},"example":{"message":"Rule with ID 11f0a2b4-4b7d-11ef-9c3a-0242ac120002 not found"}}}}},"security":[{"oauth2":[]}]},"put":{"tags":["Create/Update/Delete rules"],"summary":"Update an existing rule","description":"Updates an existing rule's details, including query, triggers, status and more.","operationId":"updateRules","parameters":[{"name":"ruleId","in":"path","description":"Internal identifier for a rule to update.","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/RulesForAssignToCategory"},{"$ref":"#/components/schemas/RulesForGeneratePdf"},{"$ref":"#/components/schemas/RulesEditValue"},{"$ref":"#/components/schemas/RulesAddRelation"},{"$ref":"#/components/schemas/RulesChangeProductStatus"},{"$ref":"#/components/schemas/RulesSyncProducts"},{"$ref":"#/components/schemas/RulesAiEnrich"},{"$ref":"#/components/schemas/RulesAiLinguist"}]},"example":{"ruleName":"string","targetId":"String","status":"active","action":"productToCategory","query":{},"triggers":[],"runtime":"event","unassigned_category":"false","context":["en"],"rulePriority":"Priority group 1"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/RulesForAssignToCategory"},{"$ref":"#/components/schemas/RulesForGeneratePdf"},{"$ref":"#/components/schemas/RulesEditValue"},{"$ref":"#/components/schemas/RulesAddRelation"},{"$ref":"#/components/schemas/RulesChangeProductStatus"},{"$ref":"#/components/schemas/RulesSyncProducts"},{"$ref":"#/components/schemas/RulesAiEnrich"},{"$ref":"#/components/schemas/RulesAiLinguist"}]},"examples":{"ruleInAGroup":{"summary":"An editValue rule that belongs to a group - both group fields come back, groupName resolved at read time","value":{"ruleId":"11f0a2b4-4b7d-11ef-9c3a-0242ac120002","ruleName":"Normalise unit of measure","action":"editValue","status":"active","rulePriority":"Priority group 3","targetId":"attr-uom","targetValue":["pcs"],"context":["en"],"modifiedAt":"1739088000000","groupId":"a3f1c8e2-4b7d-11ef-9c3a-0242ac120002","groupName":"Pricing"}},"ruleWithNoGroup":{"summary":"The same rule unassigned - BOTH group fields are absent, never null and never an empty string. Test for the key, not for a falsy value","value":{"ruleId":"11f0a2b4-4b7d-11ef-9c3a-0242ac120002","ruleName":"Normalise unit of measure","action":"editValue","status":"active","rulePriority":"Priority group 3","targetId":"attr-uom","targetValue":["pcs"],"context":["en"],"modifiedAt":"1739088000000"}}}}}},"400":{"description":"Bad Request. Each cause returns its own message. A malformed path ruleId and the groupId causes below all land here. Membership is validated before anything is written, so a 400 means the rule was not modified at all.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}},"examples":{"unknownGroup":{"summary":"groupId does not match an existing group","value":{"message":"Group with ID a3f1c8e2-4b7d-11ef-9c3a-0242ac120002 not found"}},"malformedGroupId":{"summary":"groupId is neither a UUID nor the UNASSIGNED literal - same message as an unknown group, since both mean 'no such group'","value":{"message":"Group with ID not-a-uuid not found"}},"emptyGroupId":{"summary":"groupId sent as null or empty string - send \"UNASSIGNED\" to unassign, or omit the field to leave the group unchanged","value":{"message":"groupId must be a group id, or \"UNASSIGNED\" to unassign. null and empty string are not accepted -- omit the field to leave the rule's group unchanged"}},"malformedRuleId":{"summary":"ruleId in the path is not a UUID - PUT shares its 400 with the body causes above, and this cause is new in this release","value":{"message":"invalid ruleId"}}}}}},"403":{"description":"Forbidden","content":{}},"404":{"description":"Not Found. ruleId is a well-formed UUID but no rule in this organization has it. One wording on every verb -- GET, PUT and DELETE all return this message.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}},"example":{"message":"Rule with ID 11f0a2b4-4b7d-11ef-9c3a-0242ac120002 not found"}}}},"409":{"description":"Conflict. The request is well-formed, but another rule in this organization already occupies the same uniqueness slot for this action. The slot differs per action -- target template for generatePdf, target plus query hash for addRelation and changeProductStatus, target attribute plus context for editValue, template plus runtime for aiEnrich and aiLinguist. Every duplicate-rule message uses this status; it was 400 before this release.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}},"examples":{"duplicateTemplate":{"summary":"generatePdf -- another rule already targets this template","value":{"message":"Another rule for generate PDF already exists with this target template (tmpl-invoice)."}},"duplicateTemplateAndRuntime":{"summary":"aiEnrich and aiLinguist -- uniqueness is (template, runtime) per action","value":{"message":"Another rule for aiEnrich already exists with this template_id and runtime (tmpl-1, event)."}},"duplicateQueryAndTarget":{"summary":"addRelation, syncProducts and changeProductStatus -- the conflicting rule is named so the client can link to it","value":{"message":"A rule with the same query and target (rel-accessories) already exists. Please modify your query or check rule ID: 11f0a2b4-4b7d-11ef-9c3a-0242ac120002 and rule name:Accessories."}}}}}}},"security":[{"oauth2":[]}]},"delete":{"tags":["Create/Update/Delete rules"],"summary":"Delete a rule","description":"Deletes a specified rule by its ID, removing it from the rules engine.","operationId":"deleteRulesById","parameters":[{"name":"ruleId","in":"path","description":"Internal identifier for a rule to delete.","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"No content"},"400":{"description":"Bad Request. ruleId in the path is not a UUID. GET, PUT and DELETE all answer the same way -- before this release GET and PUT passed the value straight to the lookup and answered 404 instead.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}},"example":{"message":"invalid ruleId"}}}},"403":{"description":"Forbidden","content":{}},"404":{"description":"Not Found. ruleId is a well-formed UUID but no rule in this organization has it. One wording on every verb -- GET, PUT and DELETE all return this message.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}},"example":{"message":"Rule with ID 11f0a2b4-4b7d-11ef-9c3a-0242ac120002 not found"}}}}},"security":[{"oauth2":[]}]}},"/rules/groups":{"get":{"tags":["Manage rule groups"],"summary":"List rule groups","description":"Returns every rule group for the organization, already sorted in the user-defined order. The client should render `data` in array order and must not re-sort it. This response carries no rule counts per group or for Unassigned; it reads only the group records, never the rules partition. No query parameters.","operationId":"getRuleGroups","responses":{"200":{"description":"OK. Groups are returned already sorted in the user-defined order. If the stored order row cannot be read the groups are still returned, sorted oldest-created first, rather than the request failing - losing the ordering still shows every group and nothing is written back. A failure to read the group records themselves is a 500, never an empty list.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/RuleGroup"},"title":"List of rule groups, in user-defined order."},"total":{"type":"integer","format":"int32","title":"Number of groups (data.length), not rules."}}},"example":{"data":[{"groupId":"a3f1c8e2-4b7d-11ef-9c3a-0242ac120002","groupName":"Pricing","createdAt":1739001600000,"modifiedAt":1739088000000},{"groupId":"b7e2d9f3-4b7d-11ef-9c3a-0242ac120002","groupName":"Enrichment","createdAt":1739001700000,"modifiedAt":1739001700000}],"total":2}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}}}}},"500":{"description":"Internal Server Error. The group records could not be read. No partial list is returned - retry rather than treating this as an empty organization.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}},"example":{"message":"Failed to list groups"}}}}},"security":[{"oauth2":[]}]},"post":{"tags":["Manage rule groups"],"summary":"Create a rule group","description":"Creates a new rule group. New groups are appended to the end of the user-defined order. Returns 200, not 201, matching every other write endpoint in this API. Group names are NFKC-normalised, whitespace-trimmed, and case-folded before the uniqueness check, though they are stored and returned exactly as typed.","operationId":"createRuleGroup","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BodyRequestForGroupName"},"example":{"groupName":"Pricing"}}}},"responses":{"200":{"description":"OK. The created group. New groups are appended to the end of the stored order; if that order row could not be read the group is still created successfully and simply sorts to the end of the list until the next reorder, rather than the existing order being overwritten.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RuleGroup"},"example":{"groupId":"a3f1c8e2-4b7d-11ef-9c3a-0242ac120002","groupName":"Pricing","createdAt":1739001600000,"modifiedAt":1739001600000}}}},"400":{"description":"Bad Request. Each cause returns its own message, so the message identifies the fix. The name is rejected when it is missing or not a string, when it is empty once whitespace and invisible format characters are stripped, when it exceeds 250 characters after Unicode NFKC normalisation, or when it stays within 250 characters but exceeds 1024 bytes once casefolded and UTF-8 encoded. Also returned when the organization has reached its 100-group cap.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}},"examples":{"nameNotProvided":{"summary":"Name missing or not a string","value":{"message":"group name is required"}},"nameEmpty":{"summary":"Name empty, whitespace only, or only invisible format characters","value":{"message":"group name must not be empty"}},"nameTooLong":{"summary":"Name over 250 characters after Unicode normalisation","value":{"message":"group name must be 250 characters or fewer"}},"nameKeyTooLong":{"summary":"Name within 250 characters but over 1024 bytes once casefolded and UTF-8 encoded","value":{"message":"group name is too long"}},"groupCapReached":{"summary":"Group cap reached","value":{"message":"Maximum of 100 groups per organization reached"}}}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}}}}},"409":{"description":"Conflict. A group with this name (after Unicode normalisation) already exists in the organization. Enforced atomically at the storage layer, so two simultaneous creates of the same name cannot both succeed.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}},"example":{"message":"A group named \"Pricing\" already exists"}}}},"500":{"description":"Internal Server Error. The group was not created. Either the existing groups could not be read, so the per-organization cap could not be checked, or the group record itself could not be written.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}},"examples":{"listFailed":{"summary":"Existing groups could not be read, so the per-organization cap could not be checked","value":{"message":"Failed to list groups"}},"createFailed":{"summary":"The group record could not be written","value":{"message":"Failed to create group"}}}}}}},"security":[{"oauth2":[]}]}},"/rules/groups/order":{"put":{"tags":["Manage rule groups"],"summary":"Reorder rule groups","description":"Sets the complete ordered list of group ids as currently displayed. `order` is a reserved path segment matched before `/rules/groups/{groupId}`, so `order` is never a valid groupId. Reconciliation: ids in `order` that still exist are placed in the given order; groups that exist but are missing from `order` are appended at the end (oldest createdAt first); ids in `order` that no longer exist are dropped and listed back in `notFound` - the same name the assign endpoint uses for the same concept. A reorder can never delete, hide, or duplicate a group, but the response order may differ from what was sent - always re-render from the response rather than trusting the sent array.","operationId":"reorderRuleGroups","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BodyRequestForGroupOrder"},"example":{"order":["b7e2d9f3-4b7d-11ef-9c3a-0242ac120002","a3f1c8e2-4b7d-11ef-9c3a-0242ac120002"]}}}},"responses":{"200":{"description":"OK. `data` and `total` match GET /rules/groups, reflecting the reconciled order. `notFound` additionally lists any ids from the request that no longer exist.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/RuleGroup"},"title":"List of rule groups, in the new order."},"total":{"type":"integer","format":"int32","title":"Number of groups."},"notFound":{"type":"array","items":{"type":"string"},"title":"Entries sent in `order` that matched no existing group, in the order they were sent. Empty when every id was applied. Any string is echoed back verbatim, including one that is not a well-formed id - the id FORMAT is never validated, so a stale or malformed string lands here rather than failing the request. (A non-string entry is a different matter and is rejected with a 400.) A non-empty list means the client's view was out of date; re-render from `data`."}}},"example":{"data":[{"groupId":"b7e2d9f3-4b7d-11ef-9c3a-0242ac120002","groupName":"Enrichment","createdAt":1739001700000,"modifiedAt":1739001700000},{"groupId":"a3f1c8e2-4b7d-11ef-9c3a-0242ac120002","groupName":"Pricing","createdAt":1739001600000,"modifiedAt":1739088000000}],"total":2,"notFound":[]}}}},"400":{"description":"Bad Request. Each cause returns its own message, so the message identifies the fix. Note the cap message does not say \"per request\": a reorder must send the complete list and cannot be split across calls. The cap sits above the 100-group per-organization limit, so reaching it means duplicates or ids that no longer exist.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}},"examples":{"orderMissing":{"value":{"message":"order is required"}},"orderNotAnArray":{"value":{"message":"order must be an array"}},"orderTooLong":{"value":{"message":"order must not contain more than 110 group ids"}},"orderNotStrings":{"value":{"message":"order must contain only strings"}},"orderHasDuplicates":{"value":{"message":"order must not contain duplicate group ids"}}}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}}}}},"500":{"description":"Internal Server Error. The order was not changed - the stored order is left exactly as it was, so the call is safe to retry.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}},"examples":{"listFailed":{"summary":"Existing groups could not be read, so the request could not be reconciled","value":{"message":"Failed to list groups"}},"saveFailed":{"summary":"The reconciled order could not be persisted","value":{"message":"Failed to save group order"}}}}}}},"security":[{"oauth2":[]}]}},"/rules/groups/{groupId}":{"put":{"tags":["Manage rule groups"],"summary":"Rename a rule group","description":"Renames a rule group. Position in the order and rule membership are unchanged - renaming touches only the group record, never rewrites member rules, and never changes any rule's modifiedAt.","operationId":"renameRuleGroup","parameters":[{"name":"groupId","in":"path","description":"Internal identifier (UUID) for the group to rename.","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BodyRequestForGroupName"},"example":{"groupName":"Pricing & Promotions"}}}},"responses":{"200":{"description":"OK. The updated group object.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RuleGroup"},"example":{"groupId":"a3f1c8e2-4b7d-11ef-9c3a-0242ac120002","groupName":"Pricing & Promotions","createdAt":1739001600000,"modifiedAt":1739174400000}}}},"400":{"description":"Bad Request. Each cause returns its own message, so the message identifies the fix. The name is rejected when it is missing or not a string, when it is empty once whitespace and invisible format characters are stripped, when it exceeds 250 characters after Unicode NFKC normalisation, or when it stays within 250 characters but exceeds 1024 bytes once casefolded and UTF-8 encoded.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}},"examples":{"nameNotProvided":{"summary":"Name missing or not a string","value":{"message":"group name is required"}},"nameEmpty":{"summary":"Name empty, whitespace only, or only invisible format characters","value":{"message":"group name must not be empty"}},"nameTooLong":{"summary":"Name over 250 characters after Unicode normalisation","value":{"message":"group name must be 250 characters or fewer"}},"nameKeyTooLong":{"summary":"Name within 250 characters but over 1024 bytes once casefolded and UTF-8 encoded","value":{"message":"group name is too long"}}}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}}}}},"404":{"description":"Not Found. Returned when groupId does not match an existing group.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}},"example":{"message":"Group with ID a3f1c8e2-4b7d-11ef-9c3a-0242ac120002 not found"}}}},"409":{"description":"Conflict. A group with this name (after Unicode normalisation) already exists in the organization.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}},"example":{"message":"A group named \"Pricing & Promotions\" already exists"}}}},"500":{"description":"Internal Server Error. The group was not renamed; its name and position are unchanged, so the call is safe to retry.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}},"examples":{"lookupFailed":{"summary":"The existing group record could not be read","value":{"message":"Failed to look up group"}},"renameFailed":{"summary":"The rename could not be written","value":{"message":"Failed to rename group"}}}}}}},"security":[{"oauth2":[]}]},"delete":{"tags":["Manage rule groups"],"summary":"Delete a rule group","description":"Deletes the group and cascade-clears the group from its member rules, which become unassigned. On 409 (too many members), nothing is deleted and no rule is modified - the caller must move rules out of the group first, then retry the delete.","operationId":"deleteRuleGroup","parameters":[{"name":"groupId","in":"path","description":"Internal identifier (UUID) for the group to delete.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK. Group deleted.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"},"unassignedRules":{"type":"integer","format":"int32","title":"Number of rules cleared from this group by the cascade."}}},"example":{"message":"Group deleted","unassignedRules":24}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}}}}},"404":{"description":"Not Found. Returned when groupId does not match an existing group.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}},"example":{"message":"Group with ID a3f1c8e2-4b7d-11ef-9c3a-0242ac120002 not found"}}}},"409":{"description":"Conflict. The group has more member rules than can be cleared in one call; move them out first.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"},"ruleCount":{"type":"integer","format":"int32","title":"Number of member rules currently in the group."}}},"example":{"message":"This group has 2431 rules — move them out first","ruleCount":2431}}}},"500":{"description":"Internal Server Error. The group still exists in every case and retrying the delete is the correct recovery, but how far the cascade got differs by message. A lookup, member-count, or member-load failure changes nothing at all. A partial member-clear aborts the delete deliberately, so some member rules are already unassigned and the rest are not - that abort is what keeps a rule from ever pointing at a group that no longer exists. A failure of the delete itself comes after every member rule was successfully unassigned, leaving the group in place with no members.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}},"examples":{"lookupFailed":{"summary":"The group record could not be read","value":{"message":"Failed to look up group"}},"countFailed":{"summary":"The member count could not be read, so the delete cap could not be checked","value":{"message":"Failed to count group members"}},"loadMembersFailed":{"summary":"The member rules could not be listed","value":{"message":"Failed to load group members; the group was not deleted"}},"clearMembersFailed":{"summary":"Some member rules were unassigned and the rest were not; the delete was aborted deliberately","value":{"message":"Failed to clear all member rules; the group was not deleted"}},"deleteFailed":{"summary":"Every member rule was already unassigned; only the group record could not be removed","value":{"message":"Failed to delete group"}}}}}}},"security":[{"oauth2":[]}]}},"/rules/groups/{groupId}/rules":{"put":{"tags":["Manage rule groups"],"summary":"Bulk assign or unassign rules to a group","description":"Assigns the given rules to the group in the path, or unassigns them when {groupId} is the literal \"UNASSIGNED\". Assigning replaces the previous group, since membership is exclusive. Partial success is normal: valid ids are applied, unknown ids are reported in notFound, and the call still returns 200. This endpoint does not touch a rule's modifiedAt and does not trigger rule execution - it is metadata-only. The client must chunk bulk assignment into batches of 100; there is no server-side chunking.","operationId":"assignRulesToGroup","parameters":[{"name":"groupId","in":"path","description":"Internal identifier (UUID) of the target group, or the literal string \"UNASSIGNED\" to remove the given rules from whatever group they are in.","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BodyRequestForAssignRules"},"example":{"ruleIds":["11f0a2b4-4b7d-11ef-9c3a-0242ac120002","22f0a2b4-4b7d-11ef-9c3a-0242ac120002"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"updated":{"type":"integer","format":"int32","title":"Count of rules whose group actually changed. A rule already in the target group counts as updated."},"notFound":{"type":"array","items":{"type":"string"},"title":"Rule ids that don't exist in this organization. These will never succeed - do not retry them."},"failed":{"type":"array","items":{"type":"string"},"title":"Always empty on a 200, and present only so that a success and a partial failure carry the same four keys - read the body without branching on the status code first. Any rule that hit a transient fault makes the whole call a 500, where this list is populated and is exactly what to retry."},"groupId":{"type":"string","title":"Echo of the path param, or \"UNASSIGNED\"."}}},"example":{"updated":1,"notFound":["99999999-4b7d-11ef-9c3a-0242ac120002"],"failed":[],"groupId":"a3f1c8e2-4b7d-11ef-9c3a-0242ac120002"}}}},"400":{"description":"Bad Request. Each cause returns its own message: ruleIds missing or empty, any entry not a valid rule id, or more than 100 ruleIds in one call. The batch size is checked before per-id validity, so an over-cap request reports the cap even if it also contains a malformed id.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}},"examples":{"ruleIdsRequired":{"summary":"ruleIds missing or empty","value":{"message":"ruleIds is required"}},"invalidRuleId":{"summary":"An entry is not a valid rule id","value":{"message":"ruleIds must contain valid rule ids"}},"batchCapExceeded":{"summary":"Over 100 ruleIds in one call","value":{"message":"Maximum of 100 rules per request"}}}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}}}}},"404":{"description":"Not Found. Returned when groupId does not match an existing group and is not the literal \"UNASSIGNED\".","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}},"example":{"message":"Group with ID a3f1c8e2-4b7d-11ef-9c3a-0242ac120002 not found"}}}},"500":{"description":"Internal Server Error. On the partial-assign message this is a PARTIAL result: some rules in the batch were already assigned and their history events already published. That body carries updated/notFound/failed/groupId alongside the message, so the partial state is reported rather than left to be discovered - `failed` is exactly the set to retry. The writes are idempotent, so retrying the whole batch is also safe and is the intended recovery. The group-validation message is message-only: nothing was written.","content":{"application/json":{"schema":{"type":"object","required":["message"],"properties":{"message":{"type":"string"},"updated":{"type":"integer","format":"int32","title":"Partial-assign message only. Count of rules that were assigned before the fault."},"notFound":{"type":"array","items":{"type":"string"},"title":"Partial-assign message only. Rule ids that don't exist in this organization - permanent, do not retry these."},"failed":{"type":"array","items":{"type":"string"},"title":"Partial-assign message only. Rule ids that hit a transient fault. Kept separate from notFound because they need the opposite handling: these are the ids to retry."},"groupId":{"type":"string","title":"Partial-assign message only. Echo of the path param, or \"UNASSIGNED\"."}}},"examples":{"groupValidationFailed":{"summary":"The group in the path could not be looked up","value":{"message":"Failed to validate group"}},"partialAssignFailure":{"summary":"A transient fault part-way through the batch - carries the partial result","value":{"message":"Failed to assign some rules; please retry","updated":1,"notFound":[],"failed":["22f0a2b4-4b7d-11ef-9c3a-0242ac120002"],"groupId":"a3f1c8e2-4b7d-11ef-9c3a-0242ac120002"}}}}}}},"security":[{"oauth2":[]}]}},"/relations/{relationId}/settings":{"get":{"tags":["Manage relation cleanup"],"summary":"Read the managed cleanup setting for one relation type","description":"Returns the cleanup switch for one relation type. A relation type the Rules Engine has never seen returns defaults (removeProductsFromRelation false, activeRuleCount 0, no modifiedAt), not 404, so the rule editor can call this for any relation without special handling. Requires read permission only.","operationId":"getRelationCleanupSetting","parameters":[{"name":"relationId","in":"path","description":"The PIM relation type id: a MongoDB ObjectId, 24 hex characters. Either letter case is accepted; the id is used and returned in lowercase. Any other value is answered with 404, with no call to PIM.","required":true,"schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}}],"responses":{"200":{"description":"OK. The setting for the relation type.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RelationCleanupSetting"},"example":{"relationId":"6a86d5403918b2d23e0c60cd","removeProductsFromRelation":true,"activeRuleCount":3,"modifiedAt":1755820800000}}}},"404":{"description":"Not Found. PIM does not know this relation type, or the id is not a PIM relation id (24 hex characters). This never means \"the Rules Engine has no setting saved\" - that case returns defaults with a 200.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}},"example":{"message":"Relation with ID 6a86d5403918b2d23e0c60cd not found"}}}},"500":{"description":"Internal Server Error. A DynamoDB read failed (the setting or activeRuleCount). The switch value is unknown, not off - retry rather than showing it as off.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}},"example":{"message":"Could not read the relation cleanup setting"}}}},"502":{"description":"Bad Gateway. PIM could not be reached, so the relation could not be checked. API Gateway sends this body. The switch value is unknown, not off - retry.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}},"example":{"message":"Internal server error"}}}}},"security":[{"oauth2":[]}]},"put":{"tags":["Manage relation cleanup"],"summary":"Switch managed cleanup on or off for one relation type","description":"Switches managed cleanup on or off. Sending the same value again is a 200, not an error. This does not start a run - the next weekly run picks the change up. A history event is written only when the value changes; sending the same value again writes none. Requires write permission.","operationId":"updateRelationCleanupSetting","parameters":[{"name":"relationId","in":"path","description":"The PIM relation type id: a MongoDB ObjectId, 24 hex characters. Either letter case is accepted; the id is used and returned in lowercase. Any other value is answered with 404, with no call to PIM.","required":true,"schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BodyRequestForRelationCleanupSetting"},"example":{"removeProductsFromRelation":true}}}},"responses":{"200":{"description":"OK. The saved setting.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RelationCleanupSetting"},"example":{"relationId":"6a86d5403918b2d23e0c60cd","removeProductsFromRelation":true,"activeRuleCount":3,"modifiedAt":1758153600000}}}},"400":{"description":"Bad Request. removeProductsFromRelation is missing, or is not true or false (only the JSON booleans are accepted; 1, 0 and \"true\" are rejected). Or the body is missing, is not valid JSON, or is not a JSON object.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}},"examples":{"notBoolean":{"summary":"removeProductsFromRelation missing or not a boolean","value":{"message":"removeProductsFromRelation must be true or false"}},"emptyBody":{"summary":"No body","value":{"message":"empty body request data"}},"notObject":{"summary":"Body is valid JSON but not an object, for example []","value":{"message":"request body must be a JSON object"}},"invalidJson":{"summary":"Body is not valid JSON","value":{"message":"invalid json data error Expecting value: line 1 column 1 (char 0)"}}}}}},"403":{"description":"Forbidden. Nothing was saved. The value is true and managed cleanup is not enabled for this organization. Sending false is always allowed. A caller with no write permission also gets 403, with a different message.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}},"example":{"message":"Relation cleanup is not enabled for this organization"}}}},"404":{"description":"Not Found. PIM does not know this relation type, or the id is not a PIM relation id (24 hex characters).","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}},"example":{"message":"Relation with ID 6a86d5403918b2d23e0c60cd not found"}}}},"500":{"description":"Internal Server Error. The activeRuleCount read failed (nothing was saved), or the write failed. Sending the same request again is safe.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}},"examples":{"writeFailed":{"summary":"The write failed","value":{"message":"Could not save the relation cleanup setting"}},"readFailed":{"summary":"The activeRuleCount read failed; nothing was saved","value":{"message":"Could not read the relation cleanup setting"}}}}}},"502":{"description":"Bad Gateway. PIM could not be reached, so the relation could not be checked. API Gateway sends this body. The switch value is unknown, not off - retry.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}},"example":{"message":"Internal server error"}}}}},"security":[{"oauth2":[]}]}},"/relations/settings":{"post":{"tags":["Manage relation cleanup"],"summary":"Read the managed cleanup setting for many relation types","description":"Reads up to 100 relation types in one call, for the rules list and the rule editor. POST rather than GET because the request carries a list of ids in a body; it is a read and requires read permission only. Every id sent comes back, with defaults for the ones the Rules Engine has no record of, so match on relationId rather than on position. Unlike the single read, this endpoint does not check the ids against PIM and so never returns 404.","operationId":"getRelationCleanupSettings","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BodyRequestForRelationCleanupSettings"},"example":{"relationIds":["6a86d5403918b2d23e0c60cd","70b1e2f4c8a90d1b2c3d4e5f"]}}}},"responses":{"200":{"description":"OK. One entry per requested relation type.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/RelationCleanupSetting"}},"total":{"type":"integer"}}},"example":{"data":[{"relationId":"6a86d5403918b2d23e0c60cd","removeProductsFromRelation":true,"activeRuleCount":3,"modifiedAt":1755820800000},{"relationId":"70b1e2f4c8a90d1b2c3d4e5f","removeProductsFromRelation":false,"activeRuleCount":0}],"total":2}}}},"400":{"description":"Bad Request. relationIds is missing or empty, holds an id that is not a PIM relation id (24 hex characters), or holds more than 100 ids. Or the body is missing, is not valid JSON, or is not a JSON object.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}},"examples":{"relationIdsRequired":{"summary":"relationIds missing or empty","value":{"message":"relationIds is required"}},"relationIdInvalid":{"summary":"An id in relationIds is not a PIM relation id","value":{"message":"Each relationId must be a PIM relation id: 24 hex characters"}},"tooManyIds":{"summary":"More than 100 ids","value":{"message":"Maximum of 100 relations per request"}},"emptyBody":{"summary":"No body","value":{"message":"empty body request data"}},"notObject":{"summary":"Body is valid JSON but not an object, for example []","value":{"message":"request body must be a JSON object"}},"invalidJson":{"summary":"Body is not valid JSON","value":{"message":"invalid json data error Expecting value: line 1 column 1 (char 0)"}}}}}},"500":{"description":"Internal Server Error. A DynamoDB read failed. The switch values are unknown, not off - retry rather than showing them as off.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}},"example":{"message":"Could not read the relation cleanup setting"}}}}},"security":[{"oauth2":[]}]},"put":{"tags":["Manage relation cleanup"],"summary":"Switch managed cleanup on or off for many relation types","description":"The bulk write for the Rules Engine settings page, which saves every changed row with one Save button. Only the listed relation types change; a relation type that is not listed keeps its value. Every check (body, ids, values, duplicates, PIM, activeRuleCount) runs before the first write, so a 400, 404, 502 or read-fault 500 saves nothing. The writes are not atomic: a write fault stops at that item, the items before it stay saved, and the response is 500 - load the settings again with POST /relations/settings. Sending the same request again is safe. A history event is written for each relation type whose value changes. This does not start a run. Requires write permission.","operationId":"updateRelationCleanupSettings","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BodyRequestForUpdateRelationCleanupSettings"},"example":{"settings":[{"relationId":"6a86d5403918b2d23e0c60cd","removeProductsFromRelation":true},{"relationId":"70b1e2f4c8a90d1b2c3d4e5f","removeProductsFromRelation":false}]}}}},"responses":{"200":{"description":"OK. One entry per relation type sent, in the order sent.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/RelationCleanupSetting"}},"total":{"type":"integer"}}},"example":{"data":[{"relationId":"6a86d5403918b2d23e0c60cd","removeProductsFromRelation":true,"activeRuleCount":3,"modifiedAt":1758153600000},{"relationId":"70b1e2f4c8a90d1b2c3d4e5f","removeProductsFromRelation":false,"activeRuleCount":0,"modifiedAt":1758153600000}],"total":2}}}},"400":{"description":"Bad Request. Nothing was saved. settings is missing or empty, is not a list, or an item is not an object or has no valid relationId (24 hex characters). Or a removeProductsFromRelation is not true or false, a relationId appears twice (letter case is ignored), or there are more than 100 items. Or the body is missing, is not valid JSON, or is not a JSON object.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}},"examples":{"settingsRequired":{"summary":"settings missing, empty or not a list, or an item is not an object","value":{"message":"settings is required"}},"relationIdInvalid":{"summary":"An item has no relationId, or its relationId is not a PIM relation id","value":{"message":"Each relationId must be a PIM relation id: 24 hex characters"}},"notBoolean":{"summary":"A removeProductsFromRelation is missing or not a boolean","value":{"message":"removeProductsFromRelation must be true or false"}},"duplicateIds":{"summary":"The same relationId appears twice, in any letter case","value":{"message":"Each relationId may appear only once"}},"tooManyIds":{"summary":"More than 100 items","value":{"message":"Maximum of 100 relations per request"}},"emptyBody":{"summary":"No body","value":{"message":"empty body request data"}},"notObject":{"summary":"Body is valid JSON but not an object, for example []","value":{"message":"request body must be a JSON object"}},"invalidJson":{"summary":"Body is not valid JSON","value":{"message":"invalid json data error Expecting value: line 1 column 1 (char 0)"}}}}}},"403":{"description":"Forbidden. Nothing was saved. At least one item is true and managed cleanup is not enabled for this organization. A request that only sends false is allowed. A caller with no write permission also gets 403, with a different message.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}},"example":{"message":"Relation cleanup is not enabled for this organization"}}}},"404":{"description":"Not Found. Nothing was saved. PIM does not know one of the relation types. The message names the first one.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}},"example":{"message":"Relation with ID 6a86d5403918b2d23e0c60cd not found"}}}},"500":{"description":"Internal Server Error. The activeRuleCount read failed (nothing was saved), or a write failed (the items before it are saved). The switch values are unknown, not off - load them again. Sending the same request again is safe.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}},"examples":{"writeFailed":{"summary":"A write failed; the items before it are saved","value":{"message":"Could not save the relation cleanup setting"}},"readFailed":{"summary":"The activeRuleCount read failed; nothing was saved","value":{"message":"Could not read the relation cleanup setting"}}}}}},"502":{"description":"Bad Gateway. The PIM check failed: PIM could not be reached, or answered with an error (for example 401). Nothing was saved. The switch values are unknown, not off - retry. Unlike the single PUT, the Rules Engine sends this body itself.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}},"example":{"message":"Could not check the relations in PIM"}}}}},"security":[{"oauth2":[]}]}},"/actions":{"get":{"tags":["Get available actions"],"summary":"List available actions","description":"Retrieves a list of all actions available for use in rules, such as assigning to category or editing attribute values for example.","operationId":"getActions","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AvailableActions"}}}},"403":{"description":"Forbidden","content":{}}},"security":[{"oauth2":[]}]}},"/autoTrigger/{actionType}":{"post":{"tags":["Get rules information"],"summary":"Get triggers for a specific action type","description":"Returns a list of triggers for the specified actionType.\n\n- For `aiEnrich` and `aiLinguist`, the request body must be `{ templateId: string }`.\n- For all other actions, the request body must be a query builder object.\n\nQuery builder value: see https://docs.api.test.bluestonepim.com/reference/search-2#body-params ","operationId":"getAutoTriggerByActionType","parameters":[{"name":"actionType","in":"path","description":"The action type for which to get triggers. Supported: aiEnrich, aiLinguist, productToCategory, generatePdf, editValue, addRelation, changeProductStatus, syncProducts.","required":true,"schema":{"type":"string","enum":["aiEnrich","aiLinguist","productToCategory","generatePdf","editValue","addRelation","changeProductStatus","syncProducts"]}}],"requestBody":{"content":{"application/json":{"schema":{"oneOf":[{"type":"object","required":["templateId"],"properties":{"templateId":{"type":"string","description":"Template ID for aiEnrich or aiLinguist action."}},"description":"Request body for aiEnrich or aiLinguist actionType."},{"$ref":"#/components/schemas/AnyValue"}]},"examples":{"aiEnrich":{"summary":"AI Enrich trigger request","value":{"templateId":"be2f3708-e0b1-4481-b9f7-9bf76664f2c"}},"aiLinguist":{"summary":"AI Linguist trigger request","value":{"templateId":"be2f3708-e0b1-4481-b9f7-9bf76664f2c2"}},"queryBuilder":{"summary":"Query builder trigger request (for all other actions)","value":{"children":[{"termType":"attribute","context":"en","attributeId":"676e328cee4eaf798892ebe8","filterType":"is","value":false,"attributeType":"boolean"}],"matchRule":"all","contextFallback":true}}}}}},"responses":{"200":{"description":"List of triggers for the actionType.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"entity":{"type":"string"},"event_type":{"type":"string"},"id":{"type":"string"},"context":{"type":"string","default":"en","pattern":"^(en|l[0-9]+)$","description":"Language context for the trigger. Defaults to 'en'. Other valid values follow the pattern 'l' followed by an integer (e.g. l1, l42)."}},"required":["entity","event_type","id"]}},"examples":{"aiEnrich":{"summary":"AI Enrich triggers (without context, defaults to 'en')","value":[{"entity":"product","event_type":"changed","id":"NUMBER"},{"entity":"product","event_type":"changed","id":"NAME"},{"entity":"product","event_type":"changed","id":"DESCRIPTION"},{"entity":"attribute","event_type":"changed","id":"68414e401c11e451c2b3f623"},{"entity":"attribute","event_type":"changed","id":"6850fe117ba5cf0bcc9c57d3"}]},"aiEnrichWithContext":{"summary":"AI Enrich triggers with explicit context","value":[{"entity":"product","event_type":"changed","id":"NUMBER","context":"en"},{"entity":"attribute","event_type":"changed","id":"68414e401c11e451c2b3f623","context":"l1"},{"entity":"attribute","event_type":"changed","id":"6850fe117ba5cf0bcc9c57d3","context":"l42"}]}}}}},"400":{"description":"Bad Request. Returned if the request is invalid or required fields are missing.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}}}}},"401":{"description":"Unauthorized. Returned if the request is not authenticated.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}}}}},"404":{"description":"Not Found. Returned if the resource does not exist.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}}}}},"500":{"description":"Internal Server Error. Returned if an unexpected error occurs.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}}}}}},"security":[{"oauth2":[]}]}},"/docs":{"get":{"tags":["API documentation"],"summary":"Serve this OpenAPI document","description":"Returns the rules engine OpenAPI specification. No bearer token is required: this route carries no authorizer, by decision, so that the specification can be read before a caller has credentials. It exposes the shape of the API only -- no rule, organization or user data passes through it.","operationId":"getDocs","security":[{"oauth2":[]}],"responses":{"200":{"description":"OK. The OpenAPI document, as JSON.","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/swagger-ui":{"get":{"tags":["API documentation"],"summary":"Serve the Swagger UI page for this API","description":"Returns an HTML page that renders the specification from GET /docs. No bearer token is required: this route carries no authorizer, by decision. The page itself asks the reader to sign in before it can call any gated endpoint, and it resolves the specification URL against its own origin.","operationId":"getSwaggerUi","security":[{"oauth2":[]}],"responses":{"200":{"description":"OK. An HTML page.","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/validateQuery":{"post":{"tags":["Validate queries"],"summary":"Check a query builder payload before saving it on a rule","description":"Validates a query builder payload against the organization's data. Note the convention: a validation failure is reported with HTTP 200 and a non-zero \"error\" field in the body, not with a 4xx status. Test the body, not the status code. This is unchanged by decision -- existing callers depend on it. A 4xx here means the request itself was rejected (bad JSON, or no permission), not that the query is invalid.","operationId":"validateQuery","requestBody":{"description":"The query builder payload to validate.","required":true,"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"200":{"description":"OK. Returned for BOTH a valid and an invalid query -- read the \"error\" field to tell them apart.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"integer","title":"0 when the query is valid; non-zero when it is not."},"error_details":{"type":"string","title":"Present only when error is non-zero."}}},"examples":{"valid":{"summary":"The query is valid","value":{"error":0}},"invalid":{"summary":"The query is invalid - still HTTP 200","value":{"error":1,"error_details":"attribute attr-uom does not exist"}}}}}},"400":{"description":"Bad Request. The request body is missing or is not valid JSON. This is not how an invalid query is reported.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}},"example":{"message":"empty body request data"}}}},"403":{"description":"Forbidden","content":{}}},"security":[{"oauth2":[]}]}}},"components":{"schemas":{"BodyRequestForSearchRules":{"type":"object","properties":{"ruleName":{"type":"string","title":"Search by rule name."},"action":{"type":"string","enum":["productToCategory","generatePdf","editValue","addRelation","changeProductStatus","syncProducts","aiEnrich","aiLinguist"],"title":"Search by action. Action which will be performed on product. Available values: productToCategory (Move product to category), generatePdf (Generate PDF), editValue (Edit attribute value), addRelation (Add relation), changeProductStatus (Change product status), syncProducts (Sync products), aiEnrich (AI Enrich), aiLinguist (AI Linguist)."},"status":{"type":"string","enum":["active","inactive"],"title":"Search by status. Available values: active, inactive."},"sortBy":{"type":"string","enum":["modifiedAt","ruleName","groupName"],"title":"The sorting field. Available values: modifiedAt, ruleName, groupName. Within a group, rules are ordered by ruleName ascending; unassigned rules sort last in both directions."},"sortDirection":{"type":"string","enum":["ASC","DESC"],"title":"The sorting order for a list or collection of items. Available values: ASC (ascending order), DESC (descending order)."},"targetId":{"type":"string","title":"Search by target ID. Filter rules by target entity ID."},"targetName":{"type":"string","title":"Search by target name. A entity which will be updated on rule command. Available values are category name or attribute name or PDF template that depend on action."},"issue":{"type":"string","enum":["ANY_ISSUE","BROKEN_REFERENCE","QUERY_LIMIT_EXCEEDED","INFINITE_LOOP","PRODUCT_LIMIT_EXCEEDED"],"title":"Search by issue type. Use ANY_ISSUE to include all broken statuses. Request-only; broken/brokenReason are response-only."},"context":{"type":"array","items":{"type":"string"},"title":"Search by context. Context of presented entity."},"attributeId":{"type":"string","title":"Search by attributeId to find attribute that using it in Query."},"attributeValues":{"type":"array","items":{"type":"string"},"title":"Search by attributeId and attributeValues to find rules that contain specific attribute values in Query."},"excludeAttributeId":{"type":"string","title":"Search by excludeAttributeId to find rules that do NOT have this attribute in Query."},"excludeAttributeValues":{"type":"array","items":{"type":"string"},"title":"Search by excludeAttributeId and excludeAttributeValues to find rules that do NOT contain specific attribute values in Query."},"lastUpdateFilter":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["DAYS_AGO","GREATER_EQUAL","LESS_EQUAL"]},"value":{"type":"integer"},"dateMilis":{"type":"integer"}}},"title":"Filter rules by last update time."},"rulePriority":{"type":"string","enum":["Priority group 1","Priority group 2","Priority group 3","Priority group 4","Priority group 5"],"title":"Search by rule priority. Rule priority name Available values: Priority group 1-5."},"groupId":{"type":"string","title":"Search by groupId. A group's UUID to return rules in that group, or the literal string \"UNASSIGNED\" to return rules with no groupId attribute. Omit to return all rules. Do not send null or empty string for unassigned - use \"UNASSIGNED\"."},"page":{"type":"integer","format":"int32","title":"Page to be returned.","default":0},"pageSize":{"type":"integer","format":"int32","title":"Number of items to be returned. Maximum 100.","default":20,"maximum":100}},"example":{"ruleName":"string","action":"productToCategory","status":"active","page":0,"pageSize":100,"sortBy":"modifiedAt","sortDirection":"DESC","attributeId":"65b344396f1768217d43d191","attributeValues":["value1","values2"],"excludeAttributeId":"65b344396f1768217d43d192","excludeAttributeValues":["excludeValue1"],"targetId":"categoryId123","issue":"ANY_ISSUE","lastUpdateFilter":[{"type":"DAYS_AGO","value":7}]}},"RulesForAssignToCategory":{"type":"object","properties":{"ruleId":{"type":"string","title":"ID of a rule."},"ruleName":{"type":"string","title":"Name of a rule."},"action":{"type":"string","enum":["productToCategory"],"title":"Action which will be performed on product. Available values: productToCategory (Move product to category), generatePdf (Generate PDF) or editValue (Edit attribute value)."},"runtime":{"type":"string","enum":["event"],"title":"Run time for the rule is triggered. Available value: event."},"rulePriority":{"type":"string","enum":["Priority group 1","Priority group 2","Priority group 3","Priority group 4","Priority group 5"],"title":"Priority of rule when rule engine is running. Available values: Priority group 1-5."},"status":{"type":"string","enum":["active","inactive"],"title":"Status of a rule. Available values: active, inactive."},"targetId":{"type":"string","title":"A entity which will be updated on rule command. Available values are category name or attribute name or PDF template that depend on action."},"targetName":{"type":"string","title":"Name of the target entity."},"unassigned_category":{"type":"string","enum":["true","false"],"title":"Whether to unassign from other categories. Available values: \"true\", \"false\"."},"issue":{"type":"string","title":"Derived issue type from broken status."},"broken":{"type":"integer","title":"Broken status of the rule.","readOnly":true},"brokenReason":{"type":"string","title":"Reason why the rule is broken.","readOnly":true},"organizationId":{"type":"string","title":"Organization ID."},"triggers":{"type":"array","items":{"oneOf":[{"$ref":"#/components/schemas/triggers"}]},"title":"The system events that will cause the rule to be evaluated for a product."},"query":{"$ref":"#/components/schemas/AnyValue"},"context":{"type":"array","items":{"type":"string"},"title":"Context of presented entity."},"autoTrigger":{"type":"string","enum":["true","false"],"title":"Automatic trigger value. Available values: \"true\", \"false\"."},"createdAt":{"type":"string","title":"Date time when created a rule."},"modifiedAt":{"type":"string","title":"Date time when modified a rule."},"groupId":{"type":"string","title":"ID of the rule group this rule is assigned to. Writable on create and update: send a group's UUID to assign, or the literal string \"UNASSIGNED\" to remove the rule from its group. Omit the field to leave membership unchanged - this endpoint merges rather than replaces, so a client that never sends groupId can never unassign a rule by accident. Do not send null or empty string; both are 400. Omitted entirely from responses (never null or empty string) when the rule is unassigned."},"groupName":{"type":"string","readOnly":true,"title":"Name of the rule group this rule is assigned to, resolved at read time. Omitted entirely when the rule is unassigned."}}},"RulesAddRelation":{"type":"object","properties":{"ruleId":{"type":"string","title":"ID of a rule."},"ruleName":{"type":"string","title":"Name of a rule."},"action":{"type":"string","enum":["addRelation"],"title":"Action which will be performed on product. Available values: addRelation (Add relation between products)."},"runtime":{"type":"string","enum":["event"],"title":"Run time for the rule is triggered. Available value: event."},"status":{"type":"string","enum":["active","inactive"],"title":"Status of a rule. Available values: active, inactive."},"targetId":{"type":"string","title":"A entity which will be updated on rule command. Available values are relations Id."},"targetName":{"type":"string","title":"A entity which will be updated on rule command. Available values are relations name."},"targetDirection":{"type":"string","readOnly":true,"enum":["TWO_WAY","ONE_WAY"],"title":"Direction of the target relation, derived from PIM at validation time. Available values: TWO_WAY, ONE_WAY. Defaults to TWO_WAY when absent."},"issue":{"type":"string","title":"Derived issue type from broken status."},"triggers":{"type":"array","items":{"oneOf":[{"$ref":"#/components/schemas/triggers"}]},"title":"The system events that will cause the rule to be evaluated for a product."},"queryFrom":{"$ref":"#/components/schemas/AnyValue"},"queryTo":{"$ref":"#/components/schemas/AnyValue"},"context":{"type":"array","items":{"type":"string"},"title":"Context of presented entity."},"autoTrigger":{"type":"string","enum":["true","false"],"title":"Automatic trigger value. Available values: \"true\", \"false\"."},"createdAt":{"type":"string","title":"Date time when created a rule."},"modifiedAt":{"type":"string","title":"Date time when modified a rule."},"groupId":{"type":"string","title":"ID of the rule group this rule is assigned to. Writable on create and update: send a group's UUID to assign, or the literal string \"UNASSIGNED\" to remove the rule from its group. Omit the field to leave membership unchanged - this endpoint merges rather than replaces, so a client that never sends groupId can never unassign a rule by accident. Do not send null or empty string; both are 400. Omitted entirely from responses (never null or empty string) when the rule is unassigned."},"groupName":{"type":"string","readOnly":true,"title":"Name of the rule group this rule is assigned to, resolved at read time. Omitted entirely when the rule is unassigned."}}},"RulesForGeneratePdf":{"type":"object","properties":{"ruleId":{"type":"string","title":"ID of a rule."},"ruleName":{"type":"string","title":"Name of a rule."},"action":{"type":"string","enum":["generatePdf"],"title":"Action which will be performed on product. Available values: productToCategory (Move product to category), generatePdf (Generate PDF) or editValue (Edit attribute value)."},"runtime":{"type":"string","enum":["schedule"],"title":"Run time for the rule is triggered. Available value: event."},"status":{"type":"string","enum":["active","inactive"],"title":"Status of a rule. Available values: active, inactive."},"targetId":{"type":"string","title":"A entity which will be updated on rule command. Available values are category name or attribute name or PDF template that depend on action."},"issue":{"type":"string","title":"Derived issue type from broken status."},"triggers":{"type":"array","items":{"oneOf":[{"$ref":"#/components/schemas/triggerGeneratePdf"}]},"title":"The system events that will cause the rule to be evaluated for a product."},"query":{"$ref":"#/components/schemas/AnyValue"},"context":{"type":"array","items":{"type":"string"},"title":"Context of presented entity."},"createdAt":{"type":"string","title":"Date time when created a rule."},"modifiedAt":{"type":"string","title":"Date time when modified a rule."},"groupId":{"type":"string","title":"ID of the rule group this rule is assigned to. Writable on create and update: send a group's UUID to assign, or the literal string \"UNASSIGNED\" to remove the rule from its group. Omit the field to leave membership unchanged - this endpoint merges rather than replaces, so a client that never sends groupId can never unassign a rule by accident. Do not send null or empty string; both are 400. Omitted entirely from responses (never null or empty string) when the rule is unassigned."},"groupName":{"type":"string","readOnly":true,"title":"Name of the rule group this rule is assigned to, resolved at read time. Omitted entirely when the rule is unassigned."}}},"RulesEditValue":{"type":"object","properties":{"ruleId":{"type":"string","title":"ID of a rule."},"ruleName":{"type":"string","title":"Name of a rule."},"action":{"type":"string","enum":["editValue"],"title":"Action which will be performed on product. Available values: productToCategory (Move product to category), generatePdf (Generate PDF) or editValue (Edit attribute value)."},"runtime":{"type":"string","enum":["event"],"title":"Run time for the rule is triggered. Available value: event."},"rulePriority":{"type":"string","enum":["Priority group 1","Priority group 2","Priority group 3","Priority group 4","Priority group 5"],"title":"Priority of rule when rule engine is running. Available values: Priority group 1-5."},"status":{"type":"string","enum":["active","inactive"],"title":"Status of a rule. Available values: active, inactive."},"targetId":{"type":"string","title":"A entity which will be updated on rule command. Available values are category name or attribute name or PDF template that depend on action."},"targetValue":{"type":"array","items":{"type":"string"},"title":"Value of target. It depends on targetId"},"targetValueAdd":{"type":"array","items":{"type":"string"},"title":"Value of target add value. It depends on targetId"},"targetValueRemove":{"type":"array","items":{"type":"string"},"title":"Value of target remove value. It depends on targetId"},"targetValueOperation":{"type":"string","enum":["setValue","addRemove"],"title":"Value of target type operation"},"issue":{"type":"string","title":"Derived issue type from broken status."},"triggers":{"type":"array","items":{"oneOf":[{"$ref":"#/components/schemas/triggers"}]},"title":"The system events that will cause the rule to be evaluated for a product."},"query":{"$ref":"#/components/schemas/AnyValue"},"context":{"type":"array","items":{"type":"string"},"title":"Context of presented entity."},"autoTrigger":{"type":"string","enum":["true","false"],"title":"Automatic trigger value. Available values: \"true\", \"false\"."},"createdAt":{"type":"string","title":"Date time when created a rule."},"modifiedAt":{"type":"string","title":"Date time when modified a rule."},"groupId":{"type":"string","title":"ID of the rule group this rule is assigned to. Writable on create and update: send a group's UUID to assign, or the literal string \"UNASSIGNED\" to remove the rule from its group. Omit the field to leave membership unchanged - this endpoint merges rather than replaces, so a client that never sends groupId can never unassign a rule by accident. Do not send null or empty string; both are 400. Omitted entirely from responses (never null or empty string) when the rule is unassigned."},"groupName":{"type":"string","readOnly":true,"title":"Name of the rule group this rule is assigned to, resolved at read time. Omitted entirely when the rule is unassigned."}}},"RulesChangeProductStatus":{"type":"object","properties":{"ruleId":{"type":"string","title":"ID of a rule."},"ruleName":{"type":"string","title":"Name of a rule."},"action":{"type":"string","enum":["changeProductStatus"],"title":"Action which will be performed on product. Available values: changeProductStatus (Change product status)."},"runtime":{"type":"string","enum":["event"],"title":"Run time for the rule is triggered. Available value: event."},"status":{"type":"string","enum":["active","inactive"],"title":"Status of a rule. Available values: active, inactive."},"targetId":{"type":"string","enum":["published","draft"],"title":"Target for to be published or draft."},"revertStatus":{"type":"string","enum":["true","false"],"title":"Revert status value. Available values: \"true\", \"false\"."},"issue":{"type":"string","title":"Derived issue type from broken status."},"triggers":{"type":"array","items":{"oneOf":[{"$ref":"#/components/schemas/triggers"}]},"title":"The system events that will cause the rule to be evaluated for a product."},"query":{"$ref":"#/components/schemas/AnyValue"},"context":{"type":"array","items":{"type":"string"},"title":"Context of presented entity."},"autoTrigger":{"type":"string","enum":["true","false"],"title":"Automatic trigger value. Available values: \"true\", \"false\"."},"createdAt":{"type":"string","title":"Date time when created a rule."},"modifiedAt":{"type":"string","title":"Date time when modified a rule."},"groupId":{"type":"string","title":"ID of the rule group this rule is assigned to. Writable on create and update: send a group's UUID to assign, or the literal string \"UNASSIGNED\" to remove the rule from its group. Omit the field to leave membership unchanged - this endpoint merges rather than replaces, so a client that never sends groupId can never unassign a rule by accident. Do not send null or empty string; both are 400. Omitted entirely from responses (never null or empty string) when the rule is unassigned."},"groupName":{"type":"string","readOnly":true,"title":"Name of the rule group this rule is assigned to, resolved at read time. Omitted entirely when the rule is unassigned."}}},"RulesSyncProducts":{"type":"object","properties":{"ruleId":{"type":"string","title":"ID of a rule."},"ruleName":{"type":"string","title":"Name of a rule."},"action":{"type":"string","enum":["syncProducts"],"title":"Action which will be performed on product. Available values: syncProducts (Sync products)."},"runtime":{"type":"string","enum":["event","hourly","day"],"title":"Run time for the rule is triggered. Available value: event, hourly, day."},"scheduleDay":{"type":"array","items":{"oneOf":[{"$ref":"#/components/schemas/days"}]},"title":"Day of the week for schedule. Available values: mon-sun."},"scheduleTime":{"type":"string","title":"Time of the day for schedule. in format HH:MM."},"status":{"type":"string","enum":["active","inactive"],"title":"Status of a rule. Available values: active, inactive."},"targetId":{"type":"string","enum":["published","draft"],"title":"Target for to be published or draft."},"issue":{"type":"string","title":"Derived issue type from broken status."},"triggers":{"type":"array","items":{"oneOf":[{"$ref":"#/components/schemas/triggers"}]},"title":"The system events that will cause the rule to be evaluated for a product."},"query":{"$ref":"#/components/schemas/AnyValue"},"context":{"type":"array","items":{"type":"string"},"title":"Context of presented entity."},"autoTrigger":{"type":"string","enum":["true","false"],"title":"Automatic trigger value. Available values: \"true\", \"false\"."},"createdAt":{"type":"string","title":"Date time when created a rule."},"modifiedAt":{"type":"string","title":"Date time when modified a rule."},"groupId":{"type":"string","title":"ID of the rule group this rule is assigned to. Writable on create and update: send a group's UUID to assign, or the literal string \"UNASSIGNED\" to remove the rule from its group. Omit the field to leave membership unchanged - this endpoint merges rather than replaces, so a client that never sends groupId can never unassign a rule by accident. Do not send null or empty string; both are 400. Omitted entirely from responses (never null or empty string) when the rule is unassigned."},"groupName":{"type":"string","readOnly":true,"title":"Name of the rule group this rule is assigned to, resolved at read time. Omitted entirely when the rule is unassigned."}}},"RulesAiEnrich":{"type":"object","properties":{"ruleId":{"type":"string","title":"ID of a rule."},"ruleName":{"type":"string","title":"Name of a rule."},"action":{"type":"string","enum":["aiEnrich"],"title":"Action which will be performed on product. Available values: aiEnrich (AI Enrich)."},"runtime":{"type":"string","enum":["event","hourly","day"],"title":"Run time for the rule is triggered. Available value: event, hourly, day."},"productLimit":{"type":"integer","title":"Limit number of products to process."},"scheduleDay":{"type":"array","items":{"oneOf":[{"$ref":"#/components/schemas/days"}]},"title":"Day of the week for schedule. Available values: mon-sun."},"scheduleTime":{"type":"string","title":"Time of the day for schedule. in format HH:MM."},"status":{"type":"string","enum":["active","inactive"],"title":"Status of a rule. Available values: active, inactive."},"targetId":{"type":"string","title":"Target for AI Enrich."},"issue":{"type":"string","title":"Derived issue type from broken status."},"triggers":{"type":"array","items":{"oneOf":[{"$ref":"#/components/schemas/triggers"}]},"title":"The system events that will cause the rule to be evaluated for a product."},"query":{"$ref":"#/components/schemas/AnyValue"},"context":{"type":"array","items":{"type":"string"},"title":"Context of presented entity."},"createdAt":{"type":"string","title":"Date time when created a rule."},"modifiedAt":{"type":"string","title":"Date time when modified a rule."},"groupId":{"type":"string","title":"ID of the rule group this rule is assigned to. Writable on create and update: send a group's UUID to assign, or the literal string \"UNASSIGNED\" to remove the rule from its group. Omit the field to leave membership unchanged - this endpoint merges rather than replaces, so a client that never sends groupId can never unassign a rule by accident. Do not send null or empty string; both are 400. Omitted entirely from responses (never null or empty string) when the rule is unassigned."},"groupName":{"type":"string","readOnly":true,"title":"Name of the rule group this rule is assigned to, resolved at read time. Omitted entirely when the rule is unassigned."}}},"RulesAiLinguist":{"type":"object","properties":{"ruleId":{"type":"string","title":"ID of a rule."},"ruleName":{"type":"string","title":"Name of a rule."},"action":{"type":"string","enum":["aiLinguist"],"title":"Action which will be performed on product. Available values: aiLinguist (AI Linguist). aiLinguist rules reference an ai-linguist template (targetId = templateId); the template's aiAction (translate | improve-text | spell-checker) is resolved at run time."},"runtime":{"type":"string","enum":["event","hourly","day"],"title":"Run time for the rule is triggered. Available value: event, hourly, day."},"productLimit":{"type":"integer","title":"Limit number of products to process."},"scheduleDay":{"type":"array","items":{"oneOf":[{"$ref":"#/components/schemas/days"}]},"title":"Day of the week for schedule. Available values: mon-sun."},"scheduleTime":{"type":"string","title":"Time of the day for schedule. in format HH:MM."},"status":{"type":"string","enum":["active","inactive"],"title":"Status of a rule. Available values: active, inactive."},"targetId":{"type":"string","title":"Target for AI Linguist (ai-linguist template id)."},"issue":{"type":"string","title":"Derived issue type from broken status."},"subType":{"type":"string","readOnly":true,"enum":["Translate","Improve text","Spell checker"],"title":"Derived AI action label from the linked ai-linguist template. Available values: Translate, Improve text, Spell checker. Derived from the template's aiAction (translate, improve-text, spell-checker). Server-derived; ignored if supplied in a request."},"triggers":{"type":"array","items":{"oneOf":[{"$ref":"#/components/schemas/triggers"}]},"title":"The system events that will cause the rule to be evaluated for a product."},"query":{"$ref":"#/components/schemas/AnyValue"},"context":{"type":"array","items":{"type":"string"},"title":"Context of presented entity."},"createdAt":{"type":"string","title":"Date time when created a rule."},"modifiedAt":{"type":"string","title":"Date time when modified a rule."},"groupId":{"type":"string","title":"ID of the rule group this rule is assigned to. Writable on create and update: send a group's UUID to assign, or the literal string \"UNASSIGNED\" to remove the rule from its group. Omit the field to leave membership unchanged - this endpoint merges rather than replaces, so a client that never sends groupId can never unassign a rule by accident. Do not send null or empty string; both are 400. Omitted entirely from responses (never null or empty string) when the rule is unassigned."},"groupName":{"type":"string","readOnly":true,"title":"Name of the rule group this rule is assigned to, resolved at read time. Omitted entirely when the rule is unassigned."}}},"RuleGroup":{"type":"object","properties":{"groupId":{"type":"string","title":"ID of a rule group (UUID, uuid1 format)."},"groupName":{"type":"string","title":"Display name of the rule group. 1-250 characters, stored and returned exactly as typed."},"createdAt":{"type":"integer","format":"int64","title":"Epoch milliseconds when the group was created."},"modifiedAt":{"type":"integer","format":"int64","title":"Epoch milliseconds when the group was last modified."}},"example":{"groupId":"a3f1c8e2-4b7d-11ef-9c3a-0242ac120002","groupName":"Pricing","createdAt":1739001600000,"modifiedAt":1739088000000}},"BodyRequestForGroupName":{"type":"object","required":["groupName"],"properties":{"groupName":{"type":"string","title":"Display name for the group. 1-250 characters after Unicode (NFKC) normalisation, whitespace trimming, and case-folding."}},"example":{"groupName":"Pricing"}},"BodyRequestForGroupOrder":{"type":"object","required":["order"],"properties":{"order":{"type":"array","maxItems":110,"items":{"type":"string"},"title":"Complete ordered list of group ids as currently displayed. Missing or unknown ids are reconciled server-side; see the reorder endpoint description. Capped at 110, deliberately ten above the 100-group per-organization limit: the group cap is not atomic, so a race can leave an organization at 101 groups, and an order cap equal to it would reject every reorder from then on. Because the list must be complete, reaching this cap means duplicates or ids that no longer exist, not a batch to split."}},"example":{"order":["b7e2d9f3-4b7d-11ef-9c3a-0242ac120002","a3f1c8e2-4b7d-11ef-9c3a-0242ac120002"]}},"BodyRequestForAssignRules":{"type":"object","required":["ruleIds"],"properties":{"ruleIds":{"type":"array","items":{"type":"string"},"title":"Rule ids to assign to (or unassign from) the group. Maximum 100 per call; the caller must chunk larger batches."}},"example":{"ruleIds":["11f0a2b4-4b7d-11ef-9c3a-0242ac120002","22f0a2b4-4b7d-11ef-9c3a-0242ac120002"]}},"days":{"type":"object","properties":{"entity":{"type":"string","enum":["mon","tue","wed","thu","fri","sat","sun"],"title":"Day of the week for schedule. Available values: mon-sun."}}},"triggers":{"type":"object","properties":{"entity":{"type":"string","title":"Entity for event that trigger the rule. Available values: Attribute, Category, Product."},"event_type":{"type":"string","title":"Event type of entity that trigger the rule."},"id":{"type":"string","title":"Entity id that trigger the rule. Available value is attribute id"},"context":{"type":"string","default":"en","pattern":"^(en|l[0-9]+)$","title":"Language context for the trigger. Defaults to 'en'. Other valid values follow the pattern 'l' followed by an integer (e.g. l1, l42)."}},"required":["entity","event_type","id"]},"triggerGeneratePdf":{"type":"object","properties":{"entity":{"type":"string","title":"Entity for event that trigger the rule. Available values: Asset with Labels, Attribute, Attribute Group, Category, Product."},"event_type":{"type":"string","title":"Event type of entity that trigger the rule."},"id":{"type":"string","title":"Entity id that trigger the rule."},"source":{"type":"string","title":"Source that trigger the rule. Available values: [Product, Variant group of product, Variant of product] that triggered event."}}},"AvailableActions":{"type":"string","enum":["productToCategory","generatePdf","editValue","addRelation","changeProductStatus","syncProducts","aiEnrich","aiLinguist"]},"AnyValue":{"anyOf":[{"type":"object"}],"title":"Query builder value. Read more: https://docs.api.test.bluestonepim.com/reference/search-2#body-params ."},"RelationCleanupSetting":{"type":"object","properties":{"relationId":{"type":"string","title":"The PIM relation type id the setting belongs to, in lowercase."},"removeProductsFromRelation":{"type":"boolean","title":"The cleanup switch. Always present and always true or false. A relation type the Rules Engine has never seen reads as false; there is no third state."},"activeRuleCount":{"type":"integer","title":"How many active addRelation rules use this relation type. Zero means cleanup would skip the relation type completely."},"modifiedAt":{"type":"integer","format":"int64","title":"Epoch milliseconds when the setting was last saved. A PUT that sends the same value again also updates it. Absent when the setting was never saved."}},"example":{"relationId":"6a86d5403918b2d23e0c60cd","removeProductsFromRelation":true,"activeRuleCount":3,"modifiedAt":1755820800000}},"BodyRequestForRelationCleanupSetting":{"type":"object","required":["removeProductsFromRelation"],"properties":{"removeProductsFromRelation":{"type":"boolean","title":"true switches managed cleanup on for this relation type, false switches it off."}},"example":{"removeProductsFromRelation":true}},"BodyRequestForRelationCleanupSettings":{"type":"object","required":["relationIds"],"properties":{"relationIds":{"type":"array","items":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"},"maxItems":100,"title":"PIM relation type ids to read, in either letter case. At most 100 per call. Duplicates collapse, also two spellings of one id, and the response order is not guaranteed - match on relationId, which is returned in lowercase."}},"example":{"relationIds":["6a86d5403918b2d23e0c60cd","70b1e2f4c8a90d1b2c3d4e5f"]}},"BodyRequestForUpdateRelationCleanupSettings":{"type":"object","required":["settings"],"properties":{"settings":{"type":"array","minItems":1,"maxItems":100,"items":{"type":"object","required":["relationId","removeProductsFromRelation"],"properties":{"relationId":{"type":"string","pattern":"^[0-9a-fA-F]{24}$","title":"PIM relation type id, in either letter case. Each id may appear only once; two spellings of one id count as the same id."},"removeProductsFromRelation":{"type":"boolean","title":"true switches managed cleanup on, false switches it off. Only the JSON booleans are accepted."}}},"title":"The relation types to change. At most 100 per call. A relation type that is not listed keeps its value."}},"example":{"settings":[{"relationId":"6a86d5403918b2d23e0c60cd","removeProductsFromRelation":true},{"relationId":"70b1e2f4c8a90d1b2c3d4e5f","removeProductsFromRelation":false}]}}},"securitySchemes":{"oauth2":{"type":"oauth2","flows":{"clientCredentials":{"tokenUrl":"https://idp.test.bluestonepim.com/op/token","scopes":{"openid profile systemRoles permissions organization email name nickname":"All needed scopes."}}}}}}}