Approval Rules: predicates and requesters

Author an Approval Rule that matches the right Orders with a currency-safe predicate and applies to the right requester roles.

Ask about this Page
Copy for LLM
View as Markdown

After completing this page, you should be able to:

  • Describe the fields of an Approval Rule and the role of its predicate, requesters, and status.

  • Write Order Predicates that avoid the currency-precision trap.

  • Choose the requester roles whose Orders a rule should govern.

A rule that triggers approval on totalPrice.centAmount > 5000000 looks correct until Atlas Corporate places an order in New Zealand dollars and the same number means a different amount of money. Approval Rules make governance precise, but a small predicate mistake can make a rule fire on the wrong orders or fail to fire at all.

The Approval Rule resource

An Approval Rule belongs to one Business Unit and combines four governance decisions: whether the rule is active, which Order Predicate it evaluates, which requester roles it governs, and which approver hierarchy must be satisfied. For the full creation payload, see ApprovalRuleDraft.
You create a rule through the as-associate endpoint, scoped to the Business Unit, that you have used throughout this path.
POST /{projectKey}/as-associate/{associateId}/in-business-unit/key=atlas-apac/approval-rules HTTP/1.1
Content-Type: application/json

{
  "name": "APAC orders over 50,000 AUD",
  "status": "Active",
  "predicate": "totalPrice > \"50000.00 AUD\"",
  "requesters": [
    { "associateRole": { "typeId": "associate-role", "key": "apac-buyer" } }
  ],
  "approvers": {
    "tiers": [
      { "and": [ { "or": [ { "associateRole": { "typeId": "associate-role", "key": "apac-manager" } } ] } ] }
    ]
  }
}
Creating or changing rules requires the manage_approval_rules scope; reading them requires view_approval_rules. In practice, only a high-authority role such as Business Administrator should hold manage_approval_rules.

Writing the predicate: the currency-precision trap

The predicate is an Order Predicate, so it can reference any field on the Order. The most common field is the order total, and it is also where the most common mistake lives.
totalPrice.centAmount is a raw integer in the order's currency. Comparing it on its own ignores currency entirely:
totalPrice.centAmount > 5000000

This matches 5,000,000 cents whether the Order is in Australian dollars, New Zealand dollars, or British pounds, which is almost never what you want for a multi-currency buyer like Atlas. There are two correct ways to make the comparison currency-safe.

Pair the amount with the currency explicitly:

totalPrice.centAmount > 5000000 and totalPrice.currencyCode = "AUD"

Or use the money form, which carries the currency in the literal:

totalPrice > "50000.00 AUD"
Always constrain currency in a threshold predicate for a multi-currency Business Unit. A bare centAmount comparison will misfire across currencies, either demanding approval where it should not or, worse, letting a large order through because the numeric threshold did not match the currency you assumed.

Predicates beyond the order total

Order Predicates can inspect Order totals, shipping information, and Line Items. For the full list of supported Order fields and functions, see Order Predicates. Use lineItemExists when the presence of a matching Line Item is enough, and use the Line Item quantity field inside the predicate when the rule depends on units within a matching Line Item.
lineItemExists(sku = "ZET-HAZMAT-01") = true
lineItemExists(custom.category = "controlled" and quantity > 10) = true
Use lineItemCount only when the policy depends on the number of separate matching Line Items, not the units on one Line Item. These patterns mirror real customer rules: approval for orders containing specific products, tiered total thresholds, quantity-sensitive controlled products, and approval when shipping cost exceeds a limit.

Predicates on custom business context

When a policy depends on business context that has no native Order field, model that context as a Custom Field on the Cart or Order, then reference it in the predicate through the custom.<fieldName> path. For example, have your middleware tag an Order with a custom.procurementCategory field, and gate approval on it:
custom.procurementCategory = "capital-expenditure"

This is the mechanism for requirements that out-of-the-box fields do not express, such as internal budget categories, contract references, or a risk classification your systems already compute. Keep the Custom Field definition and the predicate in step: a predicate that references a field the Order does not carry never matches.

Choosing requesters

The requesters array lists the Associate Roles a rule applies to. It holds at least one RuleRequester, each pointing at an Associate Role by key. An Order needs approval under this rule only if the Associate who placed it holds one of these roles.
Separation of duties becomes concrete in requesters. If apac-buyer is a requester and apac-manager is an approver, a buyer cannot self-approve, because the rule only applies to buyers and only managers can approve it. Design the two role sets so they do not overlap for any spend you want a second pair of eyes on.

Worked example: Atlas APAC spending thresholds

Atlas Corporate's APAC division, which trades in Australian dollars, wants two thresholds and one content-based rule. Model each as a separate Approval Rule on the atlas-apac Business Unit.
  1. Single-approver threshold. Orders over AUD 2,000 placed by an apac-buyer need a manager. Predicate: totalPrice > "2000.00 AUD"; requester apac-buyer; a one-tier hierarchy requiring apac-manager.
  2. Dual-approver threshold. Orders over AUD 5,000 additionally need the finance director. This uses the same requester and a higher threshold, but a different approver hierarchy. The next page covers that hierarchy. Predicate: totalPrice > "5000.00 AUD".
  3. Controlled-product rule. Any order containing a controlled item needs compliance sign-off regardless of value. Predicate: lineItemExists(sku = "ZET-HAZMAT-01") = true; requester apac-buyer; approver compliance-reviewer.
Each rule sets status: "Active", scopes its currency in the predicate, and names the requester role explicitly, so a multi-currency buyer's orders are governed precisely.

Key takeaways

  • An Approval Rule belongs to one Business Unit and combines a predicate, requesters, an approver hierarchy, and a status; only Active rules are evaluated.
  • Constrain currency in any threshold predicate: pair totalPrice.centAmount with totalPrice.currencyCode, or use the money form such as totalPrice > "50000.00 AUD".
  • Use lineItemExists (Boolean) and lineItemCount (Number) to trigger approval on order contents, and shippingInfo.price.centAmount to gate on shipping cost.
  • For business context with no native field, set a Custom Field on the Cart or Order and reference it in the predicate as custom.<fieldName>.
  • requesters are Associate Roles; keep requester and approver roles non-overlapping to enforce separation of duties.

Test your knowledge