Skip to content

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:

  1. Hook Triggers: A system event occurs (e.g., order created)
  2. Component Evaluates: Rule component receives event data and evaluates conditions
  3. 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:

  1. Create the rule component:

    order-confirmation.hrule
    import 'resources'
    hook OnOrderCreated
    let customer = order.customer
    from customer.email match
    nothing |> []
    email |> [
    sendEmail {
    to = email
    subject = $'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
    }
    }
    ]
  2. Deploy via manifest:

    uri: /resources/rules/order-confirmation
    spec:
    codeFile: order-confirmation.hrule
  3. 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.

See all hooks →

Rule Effects

Rules return effects - instructions for what should happen.

Effects include:

Actor Commands

Apply commands to actors:

  • orderCommand - Modify orders
  • paymentCommand - Modify payments
  • ticketCommand - Modify tickets
  • skuCommand - Modify SKUs
  • assetCommand - Modify assets

System Operations

  • messageActor - Send messages to other actors
  • scheduleJob - Schedule background jobs

Validation

  • validationError - Prevent operation and return error

See all effects →

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 configurations
rules:write # Create, update, and delete rules

Rules 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 email
hook OnOrderCreated
from [sendEmail { ... }]
// ❌ Bad - Doing too much
hook OnOrderCreated
from [sendEmail { ... }, createTicket(...), updateInventory(...)]

2. Use Before Hooks for Validation

Prevent invalid operations with before hooks:

hook OnOrderBeforeCreated
from validateOrder(order) match
Error |> [{ effect = 'validationError', code = error.code, message = error.message }]
|> []

3. Use After Hooks for Automation

Trigger workflows after successful operations:

hook OnOrderCreated
from [
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