xMatix
Sign in Request demo
xMatix
PRODUCTS
SalesField Sales & SFACRMRewardsClaimsInventoryProcurementWarehouse ManagementField ServiceServiceSupportTelephony & MessagingFinance & AccountingPayrollExpense ManagementCommercePortalsAnalytics & ReportingData StudioMobile AppSee all products →
PLATFORM
Platform overviewApp BuilderAutomationIntegrationsSecurity & GovernanceChange ManagementDevelopers
SENSE AI
Sense AI overviewSense AssistSense ControlSense VisionAI StudioTrust & governanceIn Claude & ChatGPTUse cases
SOLUTIONS
Auto DMSConsumer Goods DMSSales Force Automation (SFA) FMCG & DistributionManufacturing & Dealer NetworksAutomotive & DealershipsPharma & HealthcareConsumer DurablesAgri-InputsBuilding MaterialsService NetworksWarehousing & 3PLFinancial AccountingERP SoftwareIndia GST ComplianceUAE VAT & e-InvoicingSaudi ZATCA & VATAll solutions →
RESOURCES
Knowledge CenterDeveloper & CLIBlogGuidesWhat is xMatix?Company facts
COMPANY
AboutCareersPartnersEventsContactAuthorsLegal
Sign in Request demo
Home/Docs/Developer/Business rules in the CLI
REFERENCE · Last reviewed

Business rules in the CLI

xmatix rules copies business rules between a tenant and your workspace as one JSON file per rule, so a rule can be reviewed in a pull request and pushed back as a single unit. Rules are usually authored in Setup → Process Studio → Business Rules. The CLI is how you put them in git, compare them, and push a reviewed change. Snapshots, migrations and xmatix watch use the same commands for their rules step.

xmatix rules list                          # every rule in the tenant
xmatix rules list --entity Order           # rules on one entity
xmatix rules pull                          # → xmatix/automation/rules/<Entity>/<Rule>.json
xmatix rules pull --entity Order
xmatix rules pull CreditCheck              # one rule, matched by exact name
xmatix rules push xmatix/automation/rules/Order/CreditCheck.json
xmatix rules push <file.json> --yes        # replace actions without asking

Overview

There are three commands:

  • rules list prints a table of rules with the columns Name, Active, Scope, Triggers, Order, Actions (the number of actions) and Id, sorted by name. Triggers is shown as a number (see File fields). The table has no entity column.
  • rules pull writes each rule, with its actions, to xmatix/automation/rules/<Entity>/<Rule>.json. It overwrites files that already exist and never deletes a file.
  • rules push reads one file and creates or updates that rule on the tenant. Then it replaces the rule's actions with the ones in the file.

There is no delete command. Delete or deactivate rules in the product.

When to use it

  • To keep a tenant's rules in source control and see in a diff exactly which condition or action changed.
  • To make the same edit to many rules (an expression, a message, an order) in an editor, then push them.
  • To promote a reviewed rule from one tenant to another, within the limits in Common problems.

Use the product's rule editor for everything else. It checks the condition while you type, it sets a rule's Delay and Sequence (which the CLI can't push), and its Test Runner tab is the way to check what a rule does.

How xMatix handles it

Entities are found by name, from a capped list. Every command that takes an entity name (list --entity, pull, push) first reads the tenant's entity list in one request. It matches names exactly, so case matters. The CLI asks for up to 10,000 entities, but the server returns at most 250 rows per request, which is its default page-size limit. A standard tenant has more than 400 entities, so the CLI sees only some of them. Which ones it sees depends on internal ids, not on names. For an entity outside that set:

  • rules list --entity and rules pull --entity stop with "Entity not found: <name>".
  • rules pull without --entity still writes the entity's rules, but into xmatix/automation/rules/_unknown/. Two rules with the same name on different hidden entities overwrite each other there.
  • rules push stops with "Entity '<name>' not found on server." This also happens for files in _unknown/, because their entityName is _unknown.

Manage those rules in the product. rules list without --entity doesn't need the entity list, so it still shows every rule.

Pull writes the server's own view of the rule. The file is an envelope with the entity name and the rule exactly as the server returns it. That includes its id, timestamps, delay and sequence settings, and every action with its own id. Actions are listed by Order, then by name.

Push matches by entity and rule name. Push looks for a rule with exactly the file's name (case-sensitive) on the file's entity:

  1. If there isn't one, push creates the rule. If there is, push updates that rule's fields from the file. The rule's id in the file is ignored.
  2. If the file's rule has no actions array, push stops there and prints "Rule has no actions; leaving server actions untouched." The server's actions stay as they are.
  3. Otherwise push deletes every action the rule has on the server, then creates the file's actions one by one, in file order. If the rule already had actions, push first asks "Replace N existing action(s) on '<rule>' with M from file?". --yes skips this question.

Push sends only part of the rule to the server. It doesn't send Delay or Sequence settings, so they behave differently from the other fields:

  • Sequence is cleared on every update. The server treats a missing sequence as "use the default slot". Each push of an existing rule therefore moves it back to the default position, right after platform validation.
  • Delay is kept on update and lost on create. An update leaves the server's delay settings alone. A rule that push creates always runs immediately, even if the file came from a delayed rule.

After pushing a rule that uses either setting, set it again in the rule editor.

The server checks what you push. The same checks apply as in the rule editor. A condition that can't be compiled for the entity is refused, and the error names the identifier. An action that lacks what its type needs, such as a target field for a Set Value action, is refused. Actions are deleted before the new ones are created. So if the server refuses an action partway through, the rule is left with only the actions created up to that point. Fix the file and push again.

Platform rules can be locked. Where the standard-metadata lock is enforced, a rule that ships with the platform can only have its Active switch changed. Pushing any other change to it is refused with a message that starts "[STANDARD_METADATA_LOCKED]". So is pushing a file with actions, because push deletes and recreates them. To change a locked rule, clone it in the product and edit the copy.

Prerequisites

  • A signed-in workspace (see Authentication).
  • The business-rules capability (setup.automation.businessrules.manage), the same one the Business Rules setup screen needs. Without it every command fails with HTTP 403.
  • The commands that look up entities also read the entity list. That list can require the data-model capability (setup.metadata.model.manage). If you lack it, the server answers 403 with "Missing required capability 'setup.metadata.model.manage' for collection 'Entity'."

Procedure

Step 1 — Find the rules

Run xmatix rules list, or xmatix rules list --entity Order for one entity. Read the Active and Actions columns before you pull. A rule with zero actions does nothing even when it is active. If you get "(no rules)", the tenant or entity has no rules. If you get "Entity not found", check the spelling and case of the entity's name. If the name is right, the entity is outside the set the CLI can see (see How xMatix handles it).

Step 2 — Pull them

Run xmatix rules pull for every rule, xmatix rules pull --entity Order for one entity, or xmatix rules pull CreditCheck for rules with that exact name. Each file written is listed with a ✓, then "Wrote N rule file(s) under xmatix/automation/rules." If nothing matches, you see "No rules matched." and nothing is written.

Pull never removes files. If a rule was deleted or renamed in the product, its old file stays in your workspace until you delete it. Commit right after pulling so the next diff shows only real changes.

Step 3 — Edit the file

Change the fields you need: label, description, isActive, triggers, conditionFormula, order, priority, stopOnFalse, triggerFields and the actions array. Keep entityName and rule.name unless you mean to target a different rule (see Common questions). You don't need to remove ids, timestamps or delay fields, because push ignores them.

Step 4 — Push the file

Run xmatix rules push xmatix/automation/rules/Order/CreditCheck.json. The CLI prints "Creating new rule CreditCheck on Order…" or "Updating rule CreditCheck (<id>)…". If the rule already has actions, answer the replace question with y. The push ends with "Push complete. Replaced N → M action(s)."

If you answer n, the CLI prints "Aborted before action replacement (rule fields were saved)." and exits with code 1. The rule's fields have already been saved, and its actions are unchanged.

In a script or pipeline, add --yes. Push one file at a time. To push a whole folder, use snapshot import or migrate apply, and read their notes on confirmation questions.

Step 5 — Check the result

Open the rule in Setup → Process Studio → Business Rules. Set Delay or Sequence again if the rule uses them, then run the Test Runner with one record that should match and one that should not. Pull the rule again and commit. The new file has new action ids and a new updatedDateTime, which is expected after a push.

File fields

Each file looks like this, shortened:

{
  "entityName": "Order",
  "rule": {
    "id": "…",
    "name": "CreditCheck",
    "label": "Credit check",
    "description": "…",
    "entityId": "…",
    "isActive": true,
    "isStandard": false,
    "scope": 0,
    "triggers": 1,
    "conditionFormula": "…",
    "order": 0,
    "priority": 0,
    "stopOnFalse": false,
    "sequence": null,
    "triggerFields": "",
    "delayType": 0,
    "delayValue": null,
    "delayUnit": null,
    "delayDirection": 0,
    "delayDateField": null,
    "createdDateTime": "…",
    "updatedDateTime": "…",
    "actionCount": 1,
    "actions": [
      {
        "id": "…",
        "name": "Block over limit",
        "businessRuleId": "…",
        "actionType": 0,
        "targetFieldId": null,
        "expression": "…",
        "messageSeverity": 0,
        "overwriteExisting": true,
        "order": 0
      }
    ]
  }
}

The file name is the rule name, with characters that aren't allowed in file names replaced by _.

Rule fields

FieldWhat it meansNotes
entityNameThe entity the rule belongs toRequired for push. Matched exactly, including case
nameThe rule's name, and the key push matches onRequired for push. Case-sensitive
label, descriptionDisplay text from the rule editorA missing label or description is left unchanged on update and saved blank on create
isActiveWhether the rule runsA missing value is pushed as false, so a hand-written file without it saves an inactive rule
scopeAlways 0 (Entity)Rules act on records of one entity
triggersWhen the rule runs, as a sum of flags: 1 OnSave, 2 OnFieldChange, 4 OnInsert, 8 OnUpdate, 16 OnDelete, 32 OnSavedChanges, 64 OnPhaseTransitionFor example, 33 is OnSave plus OnSavedChanges. If it's missing, a new rule gets OnSave and an existing rule keeps its triggers
conditionFormulaThe condition that must hold for the actions to runChecked by the server on push. See Expressions
orderThe rule's position among rules that share a SequenceLower numbers run first
priorityThe rule's priorityRound-tripped as is
stopOnFalseWhen the condition is false, skip the remaining rules for the triggerA missing value is pushed as false
triggerFieldsThe tracked fields for OnFieldChangeA text value, as the server stores it
sequenceThe rule's slot relative to platform and domain stepsNot pushed. Every update clears it (see How xMatix handles it)
delayType, delayValue, delayUnit, delayDirection, delayDateFieldThe rule editor's Delay setting: 0 run immediately, 1 wait after the save, 2 count from a date field. Units 0 minutes, 1 hours, 2 days. Direction 0 after, 1 beforeNot pushed. Kept on update, lost on create
id, entityId, isStandard, createdDateTime, updatedDateTime, actionCountServer valuesIgnored by push. Their changes show up in diffs after every push

Action fields

FieldWhat it meansNotes
nameThe action's nameRequired. The server refuses an action without one ("Name is required.")
actionTypeWhat the action does: 0 Block Save, 1 Set Value, 2 Set Required, 3 Show Message, 4 Copy From Field, 5 Enforce Unique, 6 Lookup Value, 7 Set Read Only, 8 Lock Record, 9 Format Value, 10 Invoke Automation Script, 11 Request Approval, 12 Raise Event, 13 Assignment, 14 Create Activity, 15 Send Message, 16 Enqueue Job, 17 Create Record, 18 Raise Nudge, 19 Run Assignment, 20 Apply Field Mappings, 21 Push Field Mappings, 22 Trigger Entity ActionA missing value is pushed as 0, Block Save
targetFieldIdThe field the action writes or guards, by its idField ids belong to the tenant they came from. See Common problems
expressionThe action's value, message or directiveIts format depends on the action type, as on the business rules page
messageSeverityFor messages: 0 Error, 1 Warning, 2 InfoOptional
overwriteExistingWhether a value action replaces a value that's already therePushed as true unless the file says false
orderThe action's position within the ruleA missing value is pushed as 0
id, businessRuleIdServer valuesIgnored. Every push creates new actions with new ids

Business rules

  • Push matches a rule by entity and exact name. It never renames, moves or deletes a rule.
  • The file's actions replace the server's actions completely. Push never merges them. A file without an actions array leaves the server's actions as they are.
  • Push asks before replacing actions only when the rule already has some on the server. When it creates a rule, or the rule has no actions, it doesn't ask.
  • Push never sends Delay or Sequence. Every update clears Sequence, and a rule that push creates runs immediately.
  • Pull overwrites existing files and never deletes any.
  • Entity lookups see at most the first 250 entities the server returns. Rules on other entities go to _unknown/ and can't be pushed.

Example

A finance team keeps its rules in git. An administrator raises the order credit limit used by the CreditCheck rule on Order:

  1. They run xmatix rules pull --entity Order. The CLI writes xmatix/automation/rules/Order/CreditCheck.json and two other files. git status shows no changes, because the folder was committed after the last pull.
  2. They change the amount in conditionFormula and the expression of the rule's Show Message action. They open a pull request, and a colleague approves the two-line diff.
  3. They run xmatix rules push xmatix/automation/rules/Order/CreditCheck.json. The CLI prints "Updating rule CreditCheck (…)…", asks "Replace 1 existing action(s) on 'CreditCheck' with 1 from file?", and they answer y. The push ends with "Push complete. Replaced 1 → 1 action(s)."
  4. The rule runs at Sequence 20,000 so that it comes after a domain handler. The push cleared that, so they open the rule in Setup → Process Studio → Business Rules and set Sequence back to 20000.
  5. In the Test Runner, an order above the new limit is blocked with the new message, and an order below it passes.
  6. They run xmatix rules pull --entity Order again. The diff shows only the new action id and the new updatedDateTime, and they commit it.

Training

Practice exercise

In a sandbox tenant, create an inactive test rule on an entity, with a Show Message action. Run xmatix rules list --entity <Entity> and find it. If you get "Entity not found", pick another entity. Pull it, change the action's expression, and push it with --yes. Pull it again.

Expected result: the push prints "Updating rule …" and "Replaced 1 → 1 action(s)". The new file shows your expression, a different action id and a newer updatedDateTime. The rule is still inactive. Then delete isActive from the file, push again, and check that the rule is still inactive, because a missing isActive is pushed as false. Delete the rule in the product when you're done.

Quick reference

  • rules list, rules pull and rules push are the only rule commands. Delete rules in the product.
  • Files live at xmatix/automation/rules/<Entity>/<Rule>.json.
  • Push matches by entity and exact rule name, case-sensitive.
  • A file with an actions array replaces every action on the server. Without one, the actions are left alone.
  • --yes skips the replace question. Use it in scripts.
  • After a push, set Sequence again. On a newly created rule, set Delay again too.
  • Rules on entities the CLI can't see land in _unknown/ and can't be pushed.
  • Missing isActive or stopOnFalse is pushed as false. A missing actionType becomes Block Save.

Permissions

TaskWhat it needs
List, pull or push rulesThe business-rules capability, setup.automation.businessrules.manage
Use --entity, or pull or push (any command that looks up entities)Read access to the entity list, which can require setup.metadata.model.manage
Change a platform-shipped ruleOnly its Active switch, where the standard-metadata lock is enforced. Clone it to change anything else

A user without the capability gets HTTP 403, and the command stops with an error that names the status.

Configuration

The CLI has no rule-specific settings. Everything it reads comes from the workspace configuration (see Workspace layout):

  • Tenant and API: the workspace's API address and version decide which tenant you pull from and push to. Check them before you push.
  • Rule settings the file doesn't carry: Delay and Sequence are set in Setup → Process Studio → Business Rules, in the rule's dialog and its condition tab.
  • xmatix watch pushes a rule file each time you save it, unless you pass --no-rules. It doesn't pass --yes, so for a rule that already has actions the push stops at the replace question. Watch is most useful for rules without actions, or turn the rules group off.

Common problems

"Entity not found" for an entity I know exists

Check the exact name and its case, for example Order, not order. If the name is right, the entity is outside the first 250 entities the server returns to the CLI. The CLI can't work with that entity's rules. Edit them in the product.

My pull created an _unknown folder

Those rules belong to entities the CLI couldn't see. Their files can't be pushed, because push looks for an entity named _unknown. Two rules with the same name may have overwritten each other in that folder, so don't treat it as a backup. Leave _unknown/ out of source control, or delete it after each pull.

My rule runs in the wrong place, or no longer waits, after a push

Push doesn't send Sequence or Delay. An update clears Sequence, and a newly created rule runs immediately. Set both again in the rule editor after the push.

The push stopped halfway and the rule lost actions

Push deletes all existing actions before it creates the file's actions. If the server refuses one of them, for example an action without a name or without a required target field, the rule keeps only the actions created before it. Read the error, fix that action in the file, and push again. The next push replaces whatever is there.

The push was refused with "[STANDARD_METADATA_LOCKED]"

The rule ships with the platform, and the lock lets you change only its Active switch. Push writes more than that: it deletes and recreates the actions, and it clears Sequence. Turn the rule on or off in the product instead. To change it, clone the rule in the product and keep the copy in git.

The rule pushed to another tenant writes the wrong field

Actions name their target field by id, and ids belong to the tenant they came from. Before you push a file to another tenant, pull any rule on the same entity there to find that tenant's field ids. Then update each targetFieldId.

The rule pushed fine but never fires

Check the rule's own settings: isActive false (including a file without isActive), triggers that don't include the kind of change you're testing, or a condition that never matches. Run the rule editor's Test Runner and read its Diagnostics list.

"Aborted before action replacement (rule fields were saved)."

You answered n to the replace question. The rule's fields were already saved, but its actions were not changed. Push again and answer y, or use --yes.

Common questions

How do I rename a rule?

Push can't rename anything. A file with a new name creates a second rule, and the old rule stays on the tenant. Rename the rule in the product, then pull. Delete the old file from your workspace yourself, because pull never removes files. The same goes for moving a rule to another entity: create it there, then delete the old one in the product.

Why does every push change the file when I pull again?

Push deletes and recreates the rule's actions, so they get new ids, and the server stamps a new updatedDateTime. Those changes are expected. When you review a diff after a push, look at conditionFormula, triggers and the actions' actionType, expression and targetFieldId. Ids and timestamps can be ignored.

Can I move rules from one tenant to another with the CLI?

Partly. Push matches by entity and rule name, so a rule file can be pushed to another tenant that has the same entity. Before you rely on it, check three things. The entity must be one the CLI can see on the target tenant. Each targetFieldId must be valid there. Delay settings are dropped when the rule is created, so set them again. Test the rule on the target tenant before you activate it.

Should rules be edited as JSON or in the product?

Use both, for different jobs. The product's editor checks the condition as you type, sets Delay and Sequence, and has the Test Runner. JSON is better for review, bulk edits and promotion. Whichever side you edit, pull after changes in the product and push after changes to files, so the two don't drift apart.