Rules
Rules are event-driven automation components that react to system events in Hantera. They enable you to validate state changes, enforce business logic, and trigger automated workflows.
What are Rules?
A rule is a component that executes when specific events occur in the system. Rules can:
- Validate operations before they complete (prevent invalid states)
- Automate workflows when entities change
- Enforce business logic across the platform
- Integrate with external systems via effects
Unlike Jobs which are time-triggered, rules are event-triggered - they run automatically when specific system events occur.
How Rules Work
Rules follow a three-step pattern:
- Hook Triggers: A system event occurs (e.g., order created)
- Component Evaluates: Rule component receives event data and evaluates conditions
- Effects Execute: Component returns effects to execute (commands, validations, integrations)
graph LR A[Actor Event] --> B[Rule Hook Triggers] B --> C[Component Evaluates] C --> D[Return Effects] D --> E[Effects Execute]Simple Example: Order Confirmation Email
Here’s a complete example that sends an email when an order is created:
-
Create the rule component:
order-confirmation.hrule import 'resources'hook OnOrderCreatedlet customer = order.customerfrom customer.email matchnothing |> []email |> [sendEmail {to = emailsubject = $'Order #{order.orderNumber} Confirmed'body = {html = $'<h1>Thank you for your order!</h1><p>Order number: {order.orderNumber}</p>'}category = 'order_confirmation'dynamic = {orderId = order.id}}] -
Deploy via manifest:
uri: /resources/rules/order-confirmationspec:codeFile: order-confirmation.hrule -
Apply the manifest:
Terminal window h_ manage apply h_manifest.yaml
Now every time an order is created, the customer receives a confirmation email automatically.
Rule Hooks
Rules respond to actor lifecycle events called hooks. Hooks are categorized by timing and actor type:
Hook Timing
Before Hooks (OnXxxBeforeCreated, OnXxxBeforeDeleted):
- Execute before the operation completes
- Can prevent the operation with validation errors
- Changes aren’t yet persisted
After Hooks (OnXxxCreated, OnXxxDeleted):
- Execute after the operation completes
- Changes are persisted
- Can trigger side effects and automation
Command Hooks (OnXxxCommands):
- Execute when commands are applied
- Can add additional commands
- Useful for cascading logic
Available Hooks
Currently available hooks focus on actor lifecycle events:
- Order: 6 hooks (BeforeCreated, Created, BeforeDeleted, Deleted, Commands, Journal)
- Payment: 7 hooks (BeforeCreated, Created, BeforeDeleted, Deleted, Commands, Capture, Journal)
- Ticket: 8 hooks (BeforeCreated, Created, BeforeDeleted, Deleted, Commands, Complete, Rejected)
- Sku: 5 hooks (BeforeCreated, Created, BeforeDeleted, Deleted, Commands)
- Asset: 5 hooks (BeforeCreated, Created, BeforeDeleted, Deleted, Commands)
Additional hooks for other resource types (files, registry, etc.) may be added in the future.
Rule Effects
Rules return effects - instructions for what should happen.
Effects include:
Actor Commands
Apply commands to actors:
orderCommand- Modify orderspaymentCommand- Modify paymentsticketCommand- Modify ticketsskuCommand- Modify SKUsassetCommand- Modify assets
System Operations
messageActor- Send messages to other actorsscheduleJob- Schedule background jobs
Validation
validationError- Prevent operation and return error
Common Use Cases
Validation Rules
Prevent invalid state changes:
hook OnOrderBeforeCreated
let hasItems = order.orderLines count > 0
from hasItems match false |> [{ effect = 'validationError' code = 'EMPTY_ORDER' message = 'Orders must have at least one item' }] true |> []Automation Rules
Trigger workflows automatically:
hook OnPaymentCapture
from payment.amount > 10000 match false |> [] true |> [{ effect = 'messageActor' actorType = 'ticket' actorId = 'new' messages = [{ type = 'create' body = { title = 'Large Payment Review' description = $'Payment {payment.id} for {payment.amount} needs review' } }] }]Integration Rules
Sync with external systems:
hook OnOrderCreated
from [{ effect = 'scheduleJob' definition = 'sync-to-erp' at = datetime.now addMinutes 5 parameters = { orderId = order.id }}]Multiple Effects
Rules can return multiple effects as an array:
hook OnOrderCreated
from [ { effect = 'orderCommand' type = 'createComputedOrderDiscount' componentId = 'free-shipping' description = 'Free Shipping' parameters = { threshold = '500' } }, sendEmail { to = order.customer.email subject = 'Order Confirmed' body = { html = '<h1>Thank you!</h1>' } dynamic = {} }]All effects execute in the order specified.
Access Control
Managing rule resources requires standard permissions:
rules:read # View rule configurationsrules:write # Create, update, and delete rulesRules inherit the execution permissions of the system - they run with elevated privileges to enforce business logic across the platform.
Best Practices
1. Keep Rules Focused
One rule, one responsibility:
// ✅ Good - Focused on emailhook OnOrderCreatedfrom [sendEmail { ... }]
// ❌ Bad - Doing too muchhook OnOrderCreatedfrom [sendEmail { ... }, createTicket(...), updateInventory(...)]2. Use Before Hooks for Validation
Prevent invalid operations with before hooks:
hook OnOrderBeforeCreatedfrom validateOrder(order) match Error |> [{ effect = 'validationError', code = error.code, message = error.message }] |> []3. Use After Hooks for Automation
Trigger workflows after successful operations:
hook OnOrderCreatedfrom [ sendEmail { ... }, scheduleJob { ... }]4. Handle Missing Data Gracefully
Always handle optional data:
from order.customer.email match nothing |> [] // No email, no effect email |> [sendEmail { to = email, ... }]5. Return Empty Array for No Effect
When no effects should execute:
from condition match false |> [] true |> [{ effect = 'orderCommand', ... }]See Also
- Components - Learn about components and runtimes
- Rule Hooks - Available lifecycle hooks
- Rule Effects - Available effects
- Common Patterns - Recipe-style examples
- Runtime Reference - Detailed runtime documentation