Skip to main content

What is a Domain Template?

A Domain Template is a bundle of fact definitions and sample rules for one common decision, such as checkout pricing or transfer approval. Applying a template creates:
  • The template’s facts, registered for your tenant. An input fact with a constraint comes with its allowed values or range.
  • A new policy group with the sample rules in a draft version, ready to edit and publish.
  • An entry in your group list, same as if you had built it manually.
Each template below also lists sample requests and the response each one produces. The numbers come from running those requests against the template.
Templates are opt-in. They only run when you click Apply in the Console or call the apply endpoint. Your existing facts and policy groups are never modified.

Available Templates

E-Commerce: Checkout Pricing

One order discount per order (member tier or first-order code), a capped AI-proposed discount on top of it, free shipping, and 5% member rewards.

Fintech: Transfer Approval

Risk-score bands, a review of transfers over USD 10,000, limits for new and unverified accounts, velocity checks, and limits on transfers an AI agent starts.

SaaS: Plan Limits

Seat and usage limits by plan, feature gating, trial usage caps, usage overage charges, and a cap on credits an AI support agent proposes.

Insurance: Claim Triage

Personal auto claims. Small claims with no flag are fast-tracked. Theft, losses in a policy’s first 30 days, injuries, and large or low-confidence AI estimates go to an adjuster. Fraud signals go to special investigation.

AI Agent: Action Approval

Example rules for actions an AI agent proposes: an allowlist of action types, personal-data checks, per-action and daily amount limits, human approval for refunds and purchases, and a confidence threshold.

E-Commerce: Checkout Pricing

One order discount per order (member tier or first-order code), a capped AI-proposed discount on top of it, free shipping, and 5% member rewards. Applying it creates the policy group E-Commerce Checkout Pricing with a draft version.

Facts

Send every input fact on each request, using a neutral value when one does not apply. The rules write the output facts, so do not send those. Input facts
NUMBER
required
Merchandise subtotal before discounts, tax, and shipping, in USD. Exclude gift cards. The order discount is applied to this value.Allowed values: ≥ 0
STRING
required
Customer loyalty tier: NONE (guest), BRONZE, SILVER, GOLD, PLATINUM.Allowed values: NONE, BRONZE, SILVER, GOLD, PLATINUM
STRING
required
Promo code entered at checkout. Send it in upper case with spaces removed: the rules match codes exactly. Send an empty string when none.
BOOLEAN
required
Whether this is the customer’s first order. Check past orders by account, email, payment card, and shipping address.
NUMBER
required
Total the customer has paid, minus refunds and cancellations, in USD. Send 0 for a guest: with null, the 10% AI cap for customers under USD 1,000 does not apply.Allowed values: ≥ 0
LIST_STRING
required
Category tags of items in the cart (e.g., apparel, electronics, gift_card). The rules match tags exactly: send electronics in lower case, or the 5% AI cap for electronics does not apply.
NUMBER
required
Send 0. The rewards rule writes the credit this order earns, in USD. Add it to the customer’s balance once the return window closes.Allowed values: ≥ 0
NUMBER
required
Extra discount the AI shopping assistant proposed, 0 to 100. Send 0 when there is none. The rules may lower it: read the result from mutatedFacts, and if the key is not there, use the percent you sent. LexQ does not apply it to the price. Take it off the subtotal after the order discount. The discounts compound, so the largest total discount is 27.75% (PLATINUM 15%, then AI 15% of the rest). Gift cards are never discounted.Allowed values: 0 ~ 100
Output facts
BOOLEAN
Whether this order ships free. Written by the free-shipping rule.
BOOLEAN
Set when the AI proposed more than 30%. Route the conversation to a person.

Rules

Always-on member price for the top tier. Does not stack with other order discounts.
  • When: loyaltyTier = "PLATINUM"
  • Then: purchaseSubtotalUsd -= 15% (rounded to 2 decimal places, HALF_UP)
  • Mutex group: ecom_order_discount
Welcome code for a first order of USD 50 or more. No effect on repeat orders.
  • When: appliedPromoCode = "WELCOME15" AND isFirstPurchase = true AND purchaseSubtotalUsd ≥ 50
  • Then: purchaseSubtotalUsd -= 15% (rounded to 2 decimal places, HALF_UP)
  • Mutex group: ecom_order_discount
Always-on member price for GOLD.
  • When: loyaltyTier = "GOLD"
  • Then: purchaseSubtotalUsd -= 10% (rounded to 2 decimal places, HALF_UP)
  • Mutex group: ecom_order_discount
Always-on member price for SILVER.
  • When: loyaltyTier = "SILVER"
  • Then: purchaseSubtotalUsd -= 5% (rounded to 2 decimal places, HALF_UP)
  • Mutex group: ecom_order_discount
Members always ship free. Guests need a USD 35 merchandise subtotal.
  • When: loyaltyTier in ["BRONZE", "SILVER", "GOLD", "PLATINUM"] OR purchaseSubtotalUsd ≥ 35
  • Then: freeShipping = true
Credit is 5% of the subtotal after the order discount, before any AI-proposed discount.
  • When: loyaltyTier in ["BRONZE", "SILVER", "GOLD", "PLATINUM"]
  • Then: loyaltyCreditUsd += 5% × purchaseSubtotalUsd (rounded to 2 decimal places, HALF_UP)
Electronics carry low margins, so carts that include them get at most 5% from the AI assistant.
  • When: aiSuggestedDiscountPercent > 5 AND cartCategoryTags has any of ["electronics"]
  • Then: aiSuggestedDiscountPercent = 5
  • Mutex group: ecom_ai_offer_cap
Customers with less than USD 1,000 lifetime spend get at most 10%.
  • When: aiSuggestedDiscountPercent > 10 AND customerLtvUsd < 1000
  • Then: aiSuggestedDiscountPercent = 10
  • Mutex group: ecom_ai_offer_cap
Caps the AI offer at 15%. It stacks on any order discount.
  • When: aiSuggestedDiscountPercent > 15
  • Then: aiSuggestedDiscountPercent = 15
  • Mutex group: ecom_ai_offer_cap
A person checks offers above 30%. The cap still applies.
  • When: aiSuggestedDiscountPercent > 30
  • Then: aiOfferReviewRequired = true

Samples

Guest’s first order with WELCOME15

WELCOME15 takes 15% off the USD 64 subtotal. The free-shipping rule checks the USD 64 in the request, not the discounted USD 54.40. The code’s USD 50 minimum keeps both above USD 35. Fired: WELCOME15: 15% off first order (USD 50+), Free shipping: members or USD 35+

PLATINUM member, AI proposes 25% off

Actions run in priority order, and each one works on the values the earlier ones changed. The reward is 5% of the discounted USD 204.00. The AI offer is cut to the 15% ceiling. Fired: PLATINUM member: 15% off, Free shipping: members or USD 35+, Member rewards: 5% back, AI offer ceiling: 15%

GOLD repeat order with electronics, AI proposes 60% off

The strictest cap, 5% for electronics, wins. The other two caps lose and stay in the decision traces with status Blocked. Conditions read only the request, so the review flag sees the AI’s original 60%, not the capped 5%. Fired: GOLD member: 10% off, Free shipping: members or USD 35+, Member rewards: 5% back, AI offer cap: 5% on electronics, Flag AI offers above 30% for review Blocked (matched, but lost to a higher-priority rule): AI offer cap: 10% under USD 1,000 lifetime spend, AI offer ceiling: 15%

Fintech: Transfer Approval

Risk-score bands, a review of transfers over USD 10,000, limits for new and unverified accounts, velocity checks, and limits on transfers an AI agent starts. Applying it creates the policy group Fintech Transfer Risk & Limits with a draft version.

Facts

Send every input fact on each request, using a neutral value when one does not apply. The rules write the output facts, so do not send those. Input facts
NUMBER
required
Amount of this outgoing transfer, in USD.Allowed values: ≥ 0
NUMBER
required
Total sent in the last 7 days including this transfer, in USD. Computed by the caller.Allowed values: ≥ 0
NUMBER
required
Days since the account was opened.Allowed values: ≥ 0
BOOLEAN
required
Whether the account holder completed identity verification.
NUMBER
required
Fraud model score as a whole number from 0 (lowest risk) to 99 (highest risk). If the model gives a probability between 0 and 1, multiply it by 100 first: 0.82 becomes 82. If the model failed or returned no score, send null. The transfer is then neither approved nor sent after step-up: unless a decline or review rule matches, the decision is REVIEW with blockReason INPUT_MISSING.Allowed values: 0 ~ 99
NUMBER
required
Outgoing transfers in the last 24 hours, including this one. Counted by the caller.Allowed values: ≥ 1
BOOLEAN
required
Whether this is the first transfer to this recipient.
STRING
required
Who started the transfer: USER, AI_AGENT, or SCHEDULED. Set it from the credential that sent the request, such as the agent’s API key. A transfer an AI agent scheduled stays AI_AGENT when it runs.Allowed values: USER, AI_AGENT, SCHEDULED
Output facts
STRING
Decision written by the rules: APPROVE, STEP_UP, REVIEW, or DECLINE. Send the transfer automatically only on APPROVE. On STEP_UP, send it after the account holder passes extra verification. On REVIEW, hold it until a person approves. isBlocked is true for every decision except APPROVE, and blockReason says why. APPROVE and STEP_UP need every input: with a null input and no decline or review match, the decision is REVIEW with blockReason INPUT_MISSING.

Rules

Highest-risk transfers are declined.
  • When: paymentRiskScore ≥ 75
  • Then: transferDecision = "DECLINE", BLOCK RISK_SCORE_HIGHEST
  • Mutex group: fin_transfer_decision
Unverified accounts can send up to USD 300 per rolling 7 days.
  • When: isIdentityVerified = false AND weeklyOutflowUsd > 300
  • Then: transferDecision = "DECLINE", BLOCK UNVERIFIED_WEEKLY_LIMIT
  • Mutex group: fin_transfer_decision
AI-initiated transfers over USD 1,000 are declined.
  • When: initiatorType = "AI_AGENT" AND transferAmountUsd > 1000
  • Then: transferDecision = "DECLINE", BLOCK AI_TRANSFER_OVER_LIMIT
  • Mutex group: fin_transfer_decision
Elevated-risk transfers wait for manual review.
  • When: paymentRiskScore ≥ 65
  • Then: transferDecision = "REVIEW", BLOCK RISK_SCORE_ELEVATED
  • Mutex group: fin_transfer_decision
Transfers over USD 10,000 wait for manual review, even at a low risk score.
  • When: transferAmountUsd > 10000
  • Then: transferDecision = "REVIEW", BLOCK HIGH_VALUE_TRANSFER
  • Mutex group: fin_transfer_decision
A transfer from a new account is held for review when it is over USD 500 or the last 7 days add up to over USD 1,000. The 7-day total catches a large amount split into smaller transfers.
  • When: accountAgeDays < 30 AND (transferAmountUsd > 500 OR weeklyOutflowUsd > 1000)
  • Then: transferDecision = "REVIEW", BLOCK NEW_ACCOUNT_OUTFLOW
  • Mutex group: fin_transfer_decision
Bursts of transfers can mean account takeover.
  • When: transfersLast24hCount ≥ 10
  • Then: transferDecision = "REVIEW", BLOCK VELOCITY_24H
  • Mutex group: fin_transfer_decision
The account holder confirms AI-initiated transfers over USD 100 or to a new payee. Matches only when every input has a value.
  • When: initiatorType = "AI_AGENT" AND (transferAmountUsd > 100 OR isNewPayee = true) AND (transferAmountUsd ≥ 0 AND weeklyOutflowUsd ≥ 0 AND accountAgeDays ≥ 0 AND (isIdentityVerified = true OR isIdentityVerified = false) AND paymentRiskScore ≥ 0 AND transfersLast24hCount ≥ 1 AND (isNewPayee = true OR isNewPayee = false) AND initiatorType in ["USER", "AI_AGENT", "SCHEDULED"])
  • Then: transferDecision = "STEP_UP", BLOCK AI_TRANSFER_CONFIRMATION
  • Mutex group: fin_transfer_decision
First transfers over USD 200 to a new payee need extra verification. Matches only when every input has a value.
  • When: isNewPayee = true AND transferAmountUsd > 200 AND (transferAmountUsd ≥ 0 AND weeklyOutflowUsd ≥ 0 AND accountAgeDays ≥ 0 AND (isIdentityVerified = true OR isIdentityVerified = false) AND paymentRiskScore ≥ 0 AND transfersLast24hCount ≥ 1 AND (isNewPayee = true OR isNewPayee = false) AND initiatorType in ["USER", "AI_AGENT", "SCHEDULED"])
  • Then: transferDecision = "STEP_UP", BLOCK NEW_PAYEE_STEP_UP
  • Mutex group: fin_transfer_decision
Records an explicit approval when every input has a value and nothing above matched.
  • When: transferAmountUsd ≥ 0 AND weeklyOutflowUsd ≥ 0 AND accountAgeDays ≥ 0 AND (isIdentityVerified = true OR isIdentityVerified = false) AND paymentRiskScore ≥ 0 AND transfersLast24hCount ≥ 1 AND (isNewPayee = true OR isNewPayee = false) AND initiatorType in ["USER", "AI_AGENT", "SCHEDULED"]
  • Then: transferDecision = "APPROVE"
  • Mutex group: fin_transfer_decision
Holds any transfer no rule above decided. Step-up and approval need every input, so a transfer with a null input ends here unless a decline or review rule matches. blockReason is INPUT_MISSING.
  • When: Catch-all
  • Then: transferDecision = "REVIEW", BLOCK INPUT_MISSING
  • Mutex group: fin_transfer_decision

Samples

Routine transfer

No risk rule matches and every input has a value, so the default approval wins. The approval still shows up by rule name in the decision traces. Fired: Approve: every input present, no risk rule matched Blocked (matched, but lost to a higher-priority rule): Review: default hold

Risk model returns no score

The risk model failed, so paymentRiskScore is null. No condition matches null, and the default approval needs every input, so the default hold wins: REVIEW with blockReason INPUT_MISSING. Hold the transfer until a person reviews it. Fired: Review: default hold

AI agent sends USD 85 to a new payee

The amount is small, but an AI agent paying a new payee needs the account holder’s confirmation. The response sets isBlocked to true with blockReason AI_TRANSFER_CONFIRMATION. Send the transfer only after the account holder confirms. Fired: Step-up: AI-initiated over USD 100 or to a new payee Blocked (matched, but lost to a higher-priority rule): Approve: every input present, no risk rule matched, Review: default hold

AI agent sends USD 1,800 from a new account

Seven rules match. The response carries one decision, DECLINE, with its blockReason. The four other risk rules, the default approval, and the default hold lose to it and stay in the decision traces with status Blocked. Fired: Decline: AI-initiated transfer over USD 1,000 Blocked (matched, but lost to a higher-priority rule): Review: risk score 65+, Review: account under 30 days, over USD 500 per transfer or USD 1,000 in 7 days, Step-up: AI-initiated over USD 100 or to a new payee, Step-up: new payee over USD 200, Approve: every input present, no risk rule matched, Review: default hold

SaaS: Plan Limits

Seat and usage limits by plan, feature gating, trial usage caps, usage overage charges, and a cap on credits an AI support agent proposes. Applying it creates the policy group SaaS Plan Limits & Credits with a draft version.

Facts

Send every input fact on each request, using a neutral value when one does not apply. The rules write the output facts, so do not send those. Input facts
STRING
required
Subscription plan: FREE, TEAM, BUSINESS, ENTERPRISE. This template sets no usage limit for paid BUSINESS and ENTERPRISE plans; add the limits from your price list. Do not send null: with null, none of the FREE or TEAM rules apply, and TEAM overage is not computed. A proposed credit is then never granted as proposed, and creditApprovalRequired is true.Allowed values: FREE, TEAM, BUSINESS, ENTERPRISE
NUMBER
required
Seats in the workspace after the requested change.Allowed values: ≥ 1
NUMBER
required
API calls used in the current billing period, including this request. Counted by the caller.Allowed values: ≥ 0
STRING
required
Gated feature this request needs: SSO, AUDIT_LOG, or NONE.Allowed values: SSO, AUDIT_LOG, NONE
BOOLEAN
required
Whether the workspace is on a free trial of a paid plan. Send true or false, never null: with null, the trial usage cap and TEAM overage are skipped. A proposed credit is then never granted as proposed, and creditApprovalRequired is true.
NUMBER
required
Send 0. The overage rules write the total overage for the billing period so far, in USD. Record the latest value and bill it once at period close. Do not add up overageChargeUsd__delta across requests.Allowed values: ≥ 0
NUMBER
required
Service credit an AI support agent proposes to grant, in USD. Send 0 on calls that grant no credit. On credit calls, read only aiGrantedCreditUsd and creditApprovalRequired, and ignore isBlocked: only the plan rules set it. Grant mutatedFacts.aiGrantedCreditUsd if it is there, otherwise the amount you sent.Allowed values: ≥ 0
Output facts
BOOLEAN
True when the AI proposed more than the cap, or any credit on a FREE plan or trial or with the plan or trial status unknown. False when a paid plan that is not on a trial gets a credit within the cap. Not set on calls with no credit. A person approves what the response did not grant.

Rules

FREE and TEAM plans cannot use SSO or audit logs.
  • When: requestedFeature in ["SSO", "AUDIT_LOG"] AND planTier in ["FREE", "TEAM"]
  • Then: BLOCK FEATURE_NOT_IN_PLAN
  • Mutex group: saas_entitlement_block
FREE workspaces hold up to 10 seats.
  • When: planTier = "FREE" AND seatCount > 10
  • Then: BLOCK SEAT_LIMIT_REACHED
  • Mutex group: saas_entitlement_block
FREE plans stop at the monthly quota instead of billing overage.
  • When: planTier = "FREE" AND monthlyApiCalls > 10000
  • Then: BLOCK USAGE_LIMIT_REACHED
  • Mutex group: saas_entitlement_block
Trials get the FREE plan’s monthly quota until a paid plan starts.
  • When: isTrial = true AND monthlyApiCalls > 10000
  • Then: BLOCK TRIAL_USAGE_LIMIT
  • Mutex group: saas_entitlement_block
A hard ceiling at 3x the included usage stops runaway bills.
  • When: planTier = "TEAM" AND monthlyApiCalls > 300000
  • Then: BLOCK USAGE_HARD_CAP
  • Mutex group: saas_entitlement_block
Usage above the included 100,000 calls is billed at USD 1 per 1,000 calls: calls × USD 0.001 - USD 100. The USD 100 is 100,000 calls at that rate, and the USD 200 in team_overage_at_cap is the charge at 300,000 calls. 300,000 also appears in team_usage_hard_cap and team_overage_at_cap. If you change the rate, the 100,000, or the 300,000, update the USD 100 and the USD 200 too.
  • When: planTier = "TEAM" AND isTrial = false AND monthlyApiCalls > 100000 AND monthlyApiCalls ≤ 300000
  • Then: overageChargeUsd = 0.1% × monthlyApiCalls (rounded to 2 decimal places, HALF_UP), overageChargeUsd -= 100
Past 300,000 calls, new calls are blocked and this period’s overage stays at USD 200.
  • When: planTier = "TEAM" AND isTrial = false AND monthlyApiCalls > 300000
  • Then: overageChargeUsd = 200
FREE and trial workspaces have no bill to credit, so a person approves any proposed credit.
  • When: (planTier = "FREE" OR isTrial = true) AND aiGrantedCreditUsd > 0
  • Then: aiGrantedCreditUsd = 0, creditApprovalRequired = true
  • Mutex group: saas_credit_cap
On a paid plan that is not on a trial, a credit an AI support agent proposes is cut to USD 100, and a person approves anything more. If the plan or trial status is unknown, the credit is held at 0 for a person to approve instead.
  • When: (planTier in ["TEAM", "BUSINESS", "ENTERPRISE"] AND isTrial = false) AND aiGrantedCreditUsd > 100
  • Then: aiGrantedCreditUsd = 100, creditApprovalRequired = true
  • Mutex group: saas_credit_cap
A paid workspace that is not on a trial gets the proposed credit as is, up to the cap. Granting needs both the plan and the trial status.
  • When: (planTier in ["TEAM", "BUSINESS", "ENTERPRISE"] AND isTrial = false) AND aiGrantedCreditUsd > 0
  • Then: creditApprovalRequired = false
  • Mutex group: saas_credit_cap
Any credit no rule above decided is set to 0 for a person to approve. A credit ends here when the plan or the trial status is null.
  • When: aiGrantedCreditUsd > 0
  • Then: aiGrantedCreditUsd = 0, creditApprovalRequired = true
  • Mutex group: saas_credit_cap

Samples

TEAM workspace over its included usage

Two actions in one rule run in order: 135,000 calls × USD 0.001 = USD 135.00, minus the included USD 100, leaves USD 35.00. Fired: TEAM overage: USD 1 per 1,000 calls over 100,000

FREE workspace adds an 11th seat

Both the seat limit and the usage limit match. Only the seat limit’s blockReason goes out. The usage limit loses to the seat limit and stays in the decision traces with status Blocked. Fired: Block: FREE plan over 10 seats Blocked (matched, but lost to a higher-priority rule): Block: FREE plan over 10,000 API calls

AI support agent offers a USD 250 credit

The credit is cut to USD 100. The response records the cut in aiGrantedCreditUsd__delta as -150, and a person approves that USD 150. Fired: AI credit cap: USD 100 per grant Blocked (matched, but lost to a higher-priority rule): AI credit: paid plans get it as proposed, up to the cap, AI credit: default hold

USD 80 credit offer when the plan is unknown

planTier is null, so no plan rule matches and the credit is not granted as proposed. The default hold sets it to 0 and turns on creditApprovalRequired, so a person decides. The plan limits do not match null either, so never send planTier as null. Fired: AI credit: default hold

Insurance: Claim Triage

Personal auto claims. Small claims with no flag are fast-tracked. Theft, losses in a policy’s first 30 days, injuries, and large or low-confidence AI estimates go to an adjuster. Fraud signals go to special investigation. Applying it creates the policy group Insurance Claim Triage with a draft version.

Facts

Send every input fact on each request, using a neutral value when one does not apply. The rules write the output facts, so do not send those. Input facts
STRING
required
Type of loss: GLASS, COLLISION, COMPREHENSIVE, THEFT.Allowed values: GLASS, COLLISION, COMPREHENSIVE, THEFT
NUMBER
required
Payout the AI damage-estimation model suggests, after deductible, in USD.Allowed values: ≥ 0
NUMBER
required
Model confidence in its estimate, from 0 to 1. If the model failed or returned no confidence, send null. The claim is then never fast-tracked: unless another rule matches, it goes to an adjuster with blockReason INPUT_MISSING.Allowed values: 0 ~ 1
NUMBER
required
Days between policy start and the date of loss.Allowed values: ≥ 0
NUMBER
required
Claims by this insured in the last 3 years, from your records and an industry claims-history database, excluding this one.Allowed values: ≥ 0
BOOLEAN
required
Whether anyone was reported injured.
NUMBER
required
Fraud model score as a whole number from 0 (lowest) to 100 (highest). If the model gives a probability between 0 and 1, multiply it by 100 first: 0.86 becomes 86. If the model failed or returned no score, send null. The claim is then never fast-tracked: unless another rule matches, it goes to an adjuster with blockReason INPUT_MISSING.Allowed values: 0 ~ 100
Output facts
STRING
Route written by the rules: FAST_TRACK, ADJUSTER_REVIEW, or SPECIAL_INVESTIGATION. Pay automatically only on FAST_TRACK. isBlocked is true for every route except FAST_TRACK, and blockReason says why. If the route is SPECIAL_INVESTIGATION and hasInjury is true, assign an adjuster as well. FAST_TRACK needs every input: with a null input and no other match, the route is ADJUSTER_REVIEW with blockReason INPUT_MISSING.

Rules

Claims with a high fraud score go to the special investigation unit.
  • When: claimFraudScore ≥ 80
  • Then: claimRoute = "SPECIAL_INVESTIGATION", BLOCK SIU_REFERRAL_FRAUD_SCORE
  • Mutex group: ins_claim_route
Theft right after the policy starts is a classic red flag.
  • When: claimType = "THEFT" AND policyAgeDays < 30
  • Then: claimRoute = "SPECIAL_INVESTIGATION", BLOCK SIU_REFERRAL_EARLY_THEFT
  • Mutex group: ins_claim_route
An adjuster reviews any claim with a reported injury.
  • When: hasInjury = true
  • Then: claimRoute = "ADJUSTER_REVIEW", BLOCK INJURY_REPORTED
  • Mutex group: ins_claim_route
Theft claims are never fast-tracked. There is no damage to estimate from photos, and the police report and vehicle recovery need checking.
  • When: claimType = "THEFT"
  • Then: claimRoute = "ADJUSTER_REVIEW", BLOCK THEFT_CLAIM
  • Mutex group: ins_claim_route
Losses soon after a policy starts get an adjuster, whatever the claim type.
  • When: policyAgeDays < 30
  • Then: claimRoute = "ADJUSTER_REVIEW", BLOCK EARLY_LOSS
  • Mutex group: ins_claim_route
An adjuster reviews glass claims with an AI payout above USD 1,000.
  • When: claimType = "GLASS" AND aiEstimatedPayoutUsd > 1000
  • Then: claimRoute = "ADJUSTER_REVIEW", BLOCK GLASS_PAYOUT_OVER_LIMIT
  • Mutex group: ins_claim_route
Fast-track only pays AI estimates up to USD 2,500.
  • When: aiEstimatedPayoutUsd > 2500
  • Then: claimRoute = "ADJUSTER_REVIEW", BLOCK PAYOUT_OVER_FAST_TRACK_LIMIT
  • Mutex group: ins_claim_route
Low-confidence estimates are reviewed before payment.
  • When: aiEstimateConfidence < 0.8
  • Then: claimRoute = "ADJUSTER_REVIEW", BLOCK LOW_ESTIMATE_CONFIDENCE
  • Mutex group: ins_claim_route
Repeat claimants are reviewed by an adjuster.
  • When: priorClaimsCount ≥ 2
  • Then: claimRoute = "ADJUSTER_REVIEW", BLOCK FREQUENT_CLAIMANT
  • Mutex group: ins_claim_route
Routes the claim to FAST_TRACK, where the AI estimate is paid without manual review. Matches only when every input has a value.
  • When: claimType in ["GLASS", "COLLISION", "COMPREHENSIVE", "THEFT"] AND aiEstimatedPayoutUsd ≥ 0 AND aiEstimateConfidence ≥ 0 AND policyAgeDays ≥ 0 AND priorClaimsCount ≥ 0 AND (hasInjury = true OR hasInjury = false) AND claimFraudScore ≥ 0
  • Then: claimRoute = "FAST_TRACK"
  • Mutex group: ins_claim_route
Sends any claim no rule above routed to an adjuster. Fast-track needs every input, so a claim with a null input ends here. blockReason is INPUT_MISSING.
  • When: Catch-all
  • Then: claimRoute = "ADJUSTER_REVIEW", BLOCK INPUT_MISSING
  • Mutex group: ins_claim_route

Samples

Small windshield claim

No flag is raised and every input has a value, so the claim is fast-tracked. Fired: Fast-track: every input present, no flag raised Blocked (matched, but lost to a higher-priority rule): Adjuster: default hold

Windshield claim without an estimate confidence

The estimate model returned no confidence, so aiEstimateConfidence is null. The confidence rule does not match null, and fast-track needs every input, so the default hold wins: ADJUSTER_REVIEW with blockReason INPUT_MISSING. Do not pay until an adjuster reviews it. Fired: Adjuster: default hold

Collision with a USD 6,800 AI estimate

Confidence is high, but the payout is above the USD 2,500 fast-track ceiling, so an adjuster reviews it. The response sets isBlocked to true. Do not pay until the review is done. Fired: Adjuster: AI payout over USD 2,500 Blocked (matched, but lost to a higher-priority rule): Fast-track: every input present, no flag raised, Adjuster: default hold

Theft 12 days after policy start, fraud score 86

Special investigation wins, so claimRoute is SPECIAL_INVESTIGATION and isBlocked is true. The early-theft, theft, early-loss, payout, and confidence flags also match and stay in the decision traces for the investigator. Fired: Investigate: fraud score 80+ Blocked (matched, but lost to a higher-priority rule): Investigate: theft on a policy under 30 days old, Adjuster: theft claim, Adjuster: loss in the policy’s first 30 days, Adjuster: AI payout over USD 2,500, Adjuster: AI confidence below 0.8, Fast-track: every input present, no flag raised, Adjuster: default hold

AI Agent: Action Approval

Example rules for actions an AI agent proposes: an allowlist of action types, personal-data checks, per-action and daily amount limits, human approval for refunds and purchases, and a confidence threshold. Applying it creates the policy group AI Agent Action Approval with a draft version.

Facts

Send every input fact on each request, using a neutral value when one does not apply. The rules write the output facts, so do not send those. Input facts
STRING
required
Action the agent wants to run, for example ISSUE_REFUND, APPLY_CREDIT, PURCHASE, SEND_MESSAGE, UPDATE_TICKET, DELETE_RECORD, or CHANGE_PERMISSION. Set it from the tool your runtime is about to call. The agent’s own text can be manipulated, so take no fact values from it. A type not on the allowlist is denied. A null type is never allowed either: the decision is DENY or NEEDS_APPROVAL.
NUMBER
required
Money this action moves, in USD. Send 0 when no money moves. Take it from the tool call’s arguments. The credit cap can lower it: run the action with mutatedFacts.actionAmountUsd if it is there, otherwise with the amount you sent.Allowed values: ≥ 0
NUMBER
required
Money the agent moved for this customer in the last 24 hours, including this action, in USD. Computed by the caller. This template has no 30-day or all-customer limit.Allowed values: ≥ 0
NUMBER
required
Score for this action from a separate checker model, 0 to 1. Do not send the agent’s own confidence: a manipulated agent can report a high one. If the checker failed or returned no score, send null. The action is then never allowed: unless another rule matches, it waits for approval with blockReason INPUT_MISSING.Allowed values: 0 ~ 1
BOOLEAN
required
Whether a detector found personal data in the action payload or message. If the detector failed or did not run, send true, so an action going to a third party is denied. With null, the personal-data rule does not match, and unless another rule matches, the action waits for approval instead of being denied.
BOOLEAN
required
Whether the recipient is neither your organization nor the person the data is about. Set it from the recipient in the tool call. If you cannot tell who the recipient is, send true, so an action carrying personal data is denied. With null, the personal-data rule does not match, and unless another rule matches, the action waits for approval instead of being denied.
Output facts
STRING
Decision written by the rules: ALLOW, NEEDS_APPROVAL, or DENY. Run the tool automatically only on ALLOW, with the amount described for actionAmountUsd. On NEEDS_APPROVAL, wait for a person’s approval. On DENY, stop the agent and hand the customer’s request to a person. isBlocked is true for every decision except ALLOW, and blockReason says why. ALLOW needs every input: an allowlisted action with a null input and no other match gets NEEDS_APPROVAL and INPUT_MISSING.

Rules

Only refunds, credits, purchases, messages, and ticket updates are allowed. Any other type is denied, including deleting records, changing permissions or account details, and new tools the agent gains.
  • When: agentActionType not in ["ISSUE_REFUND", "APPLY_CREDIT", "PURCHASE", "SEND_MESSAGE", "UPDATE_TICKET"]
  • Then: agentDecision = "DENY", BLOCK ACTION_NOT_ALLOWED_FOR_AGENT
  • Mutex group: agent_action_decision
Denies actions that would send personal data to a third party.
  • When: containsPii = true AND isThirdPartyRecipient = true
  • Then: agentDecision = "DENY", BLOCK PII_TO_THIRD_PARTY
  • Mutex group: agent_action_decision
One action above USD 1,000 is treated as a manipulated request.
  • When: actionAmountUsd > 1000
  • Then: agentDecision = "DENY", BLOCK AMOUNT_OVER_HARD_LIMIT
  • Mutex group: agent_action_decision
An action that moves money and takes one customer past USD 500 in 24 hours waits for a person’s approval. Actions that move no money are never held by this limit.
  • When: dailyAgentAmountUsd > 500 AND actionAmountUsd > 0
  • Then: agentDecision = "NEEDS_APPROVAL", BLOCK DAILY_AMOUNT_LIMIT
  • Mutex group: agent_action_decision
A person approves refunds and purchases above USD 100.
  • When: agentActionType in ["ISSUE_REFUND", "PURCHASE"] AND actionAmountUsd > 100
  • Then: agentDecision = "NEEDS_APPROVAL", BLOCK AMOUNT_NEEDS_APPROVAL
  • Mutex group: agent_action_decision
Uncertain actions wait for a person’s approval.
  • When: checkerConfidence < 0.7
  • Then: agentDecision = "NEEDS_APPROVAL", BLOCK LOW_CONFIDENCE
  • Mutex group: agent_action_decision
If every input has a value and no deny or approval rule matches, a credit above USD 50 is cut to USD 50 and allowed.
  • When: agentActionType = "APPLY_CREDIT" AND actionAmountUsd > 50 AND (actionAmountUsd ≥ 0 AND dailyAgentAmountUsd ≥ 0 AND checkerConfidence ≥ 0 AND (containsPii = true OR containsPii = false) AND (isThirdPartyRecipient = true OR isThirdPartyRecipient = false))
  • Then: actionAmountUsd = 50, agentDecision = "ALLOW"
  • Mutex group: agent_action_decision
Records an explicit ALLOW for an allowlisted action when every input has a value and nothing above matched.
  • When: agentActionType in ["ISSUE_REFUND", "APPLY_CREDIT", "PURCHASE", "SEND_MESSAGE", "UPDATE_TICKET"] AND (actionAmountUsd ≥ 0 AND dailyAgentAmountUsd ≥ 0 AND checkerConfidence ≥ 0 AND (containsPii = true OR containsPii = false) AND (isThirdPartyRecipient = true OR isThirdPartyRecipient = false))
  • Then: agentDecision = "ALLOW"
  • Mutex group: agent_action_decision
Sends any allowlisted action the two allow rules above did not take to a person. Both need every input, so an allowlisted action with a null input ends here. blockReason is INPUT_MISSING.
  • When: agentActionType in ["ISSUE_REFUND", "APPLY_CREDIT", "PURCHASE", "SEND_MESSAGE", "UPDATE_TICKET"]
  • Then: agentDecision = "NEEDS_APPROVAL", BLOCK INPUT_MISSING
  • Mutex group: agent_action_decision
Denies any action no rule above decided. Every allowlisted type is decided above, so an action ends here when its type is null or not text.
  • When: Catch-all
  • Then: agentDecision = "DENY", BLOCK ACTION_NOT_ALLOWED_FOR_AGENT
  • Mutex group: agent_action_decision

Samples

USD 45 refund

The action is within every limit and every input has a value, so it is allowed. Fired: Allow: allowlisted action, every input present, no other rule matched Blocked (matched, but lost to a higher-priority rule): Needs approval: default hold for allowlisted actions, Deny: no rule above matched

USD 45 refund while the checker model is down

The checker model returned no score, so checkerConfidence is null. The confidence rule does not match null, and both allow rules need every input, so the default hold wins: NEEDS_APPROVAL with blockReason INPUT_MISSING. Do not run the refund until a person approves it. Fired: Needs approval: default hold for allowlisted actions Blocked (matched, but lost to a higher-priority rule): Deny: no rule above matched

USD 180 refund

Refunds over USD 100 wait for a person’s approval. The response sets isBlocked to true. Do not run the refund until someone approves it. Fired: Needs approval: refunds or purchases over USD 100 Blocked (matched, but lost to a higher-priority rule): Allow: allowlisted action, every input present, no other rule matched, Needs approval: default hold for allowlisted actions, Deny: no rule above matched

Agent in a manipulated chat offers a USD 1,500 credit

The checker model scored the action 0.95, and the per-action limit still denies it. The other matching rules, including the daily limit and the credit cap, lose to the per-action limit and stay in the decision traces with status Blocked. The amount stays at USD 1,500. Fired: Deny: single action over USD 1,000 Blocked (matched, but lost to a higher-priority rule): Needs approval: over USD 500 per customer in 24 hours, Allow: goodwill credit capped at USD 50, Allow: allowlisted action, every input present, no other rule matched, Needs approval: default hold for allowlisted actions, Deny: no rule above matched

USD 80 goodwill credit

No deny or approval rule matches, so the credit cap wins. The amount is cut to USD 50 and the action is allowed. Fired: Allow: goodwill credit capped at USD 50 Blocked (matched, but lost to a higher-priority rule): Allow: allowlisted action, every input present, no other rule matched, Needs approval: default hold for allowlisted actions, Deny: no rule above matched

Agent calls a tool not on the allowlist

EXPORT_DATA is not on the allowlist, so the action is denied with blockReason ACTION_NOT_ALLOWED_FOR_AGENT. A new tool the agent gains gets the same answer until you add it to the lists in action_not_allowlisted and default_allow. Add it to default_approval too, or a call for that tool with a null input is denied instead of waiting for approval. Fired: Deny: action type not on the allowlist Blocked (matched, but lost to a higher-priority rule): Deny: no rule above matched

Applying a Template

  1. Go to Settings → Domain Templates.
  2. Click Preview on a template card to inspect the facts and rules before committing. If one of the template’s keys already exists in your tenant with a different definition, the preview lists it, and the template cannot be applied until you delete that fact or change it to match.
  3. Click Apply to create the policy group.
  4. Optionally provide a custom group name. If left blank, a default name is generated (with a date suffix if the name already exists).
After apply, you’re navigated to the new policy group where you can edit rules, set effective dates, and publish.

What Happens on Apply

The entire operation is transactional: if any step fails, nothing is created.
The created policy version starts as DRAFT. To make it live, publish it from the policy group page or via the deployment API.
A request that sends an input value outside its allowed values fails with P-052, for example loyaltyTier: "Gold" instead of "GOLD". Without the allowed values, the rule that checks for "GOLD" would quietly not match. You can widen or remove the allowed values in Fact Definitions.

Customizing After Apply

Templates are starting points. After applying, you can freely:
  • Edit any rule (condition, action, priority, mutex).
  • Add or remove rules in the version.
  • Delete facts you don’t need once no rule references them. Rules in deleted groups don’t count, and the facts have no special protection.
  • Rename the policy group.
  • Adjust the version’s effective dates before publishing.

FAQ

Can I apply the same template twice? Yes. Each apply creates a new policy group, and the facts from the first apply are reused, not duplicated. If you have since changed the type or allowed values of one of them, the second apply fails with P-061 until it matches the template again. If the default group name is taken, a date suffix is appended automatically. What if my tenant already has a fact with the same key? If it has the same definition, apply keeps it as it is. The definition differs when the type differs, or, for an input fact, when the allowed values or range differ. Then the preview lists the key, the Console disables Apply, and the apply endpoint fails with P-061 without creating anything. Delete that fact or change it to match the template, then apply. A fact that rules use cannot be deleted or retyped (FD-006, FD-007), but you can change its allowed values or range. Policies you have already published pick up the change when you publish them again. Can I delete a template’s facts later? Yes, once no rule references them. Rules in a deleted group don’t count. Template-applied facts are regular tenant facts with no special protection, and the same guard applies: a fact that rules reference cannot be deleted (FD-006) or retyped (FD-007). The error reports how many rules reference it. Remove those rules, or edit them to stop using the fact, and the delete goes through. See Managing Facts. Can I roll back an apply? Not as a single operation. To undo, delete the policy group the template created, then delete the facts you don’t need. If you deployed the group, undeploy it first. A deleted group’s rules no longer block deleting a fact, but a fact that another group’s rules use stays blocked. A group that was never executed, deployed, or replayed is permanently deleted 30 days after you delete it, together with its application history; otherwise the history stays for audit.

Next Steps

Policy Groups

Learn how policy groups and versions work.

Fact Definitions

Understand the fact model in depth.