Skip to content

Custom Hooks - triggerHook ​

triggerHook enables apps to define custom hook points that other rules can listen to, without requiring platform changes. It triggers a secondary rule evaluation inline and returns the resulting effects to the caller.

Availability ​

ContextAvailable
Rules (.hrl)✅
Ingresses (.hrc)✅
Jobs (.hrc)✅
Modules (.module.hrc)✅ (when imported by an ingress or job)

Syntax ​

triggerHook is a filter that takes hook data as input and the hook name as argument:

filtrera
let hookEffects = { some = 'data', more = 'fields' } triggerHook 'OnMyHook'

Input ​

Any record value. The fields become directly accessible to listening rules alongside a hook field that the runtime adds automatically.

Argument ​

A text literal — the hook name. Convention: On prefix, PascalCase (e.g., OnClaimResolve, OnCartMutation).

Return Value ​

An iterator of records — the effects emitted by all rules that matched the hook. Returns an empty iterator [] when called inside a secondary evaluation (recursion prevention).

Hook Input Construction ​

The runtime adds a hook field to the input record before evaluating rules:

filtrera
// Caller:
{ orderId = '...' } triggerHook 'OnOrderValidated'

// What listening rules receive:
{
  hook = 'OnOrderValidated'
  orderId = '...'
}

There is no data wrapper — the hook data fields are at the top level alongside hook.

Listening Rules ​

Rules listen to a custom hook by declaring a param input with a literal hook type:

filtrera
param input: {
  hook: 'OnOrderValidated'
  orderId: uuid
}

import 'iterators'

from {
  effect = 'custom'
  type = 'addComplianceFlag'
  flag = 'validated'
}

The literal type hook: 'OnOrderValidated' ensures this rule only fires for that specific hook. It is automatically skipped for all native hooks and other custom hooks.

TIP

Listening rules can declare a subset of the input fields. As long as the types match, Filtrera's type system will accept the rule. You don't need to match every field the caller provides.

Recursion Prevention ​

triggerHook is single-level only. If a rule triggered by triggerHook calls triggerHook again, it immediately returns an empty iterator. This is enforced by the runtime — there is no configuration.

Using triggerHook in Rules ​

In rules, triggerHook returns effects that the calling rule can forward, filter, or batch. The actor spec processes all forwarded effects.

Forwarding All Effects ​

filtrera
param input: OnOrderCreated

let hookEffects = { orderId = input.order.orderId } triggerHook 'OnOrderValidated'

// Forward everything
from hookEffects

Filtering Effects ​

filtrera
param input: OnOrderCreated

let hookEffects = { orderId = input.order.orderId } triggerHook 'OnOrderValidated'

// Only forward validation errors
from hookEffects where is { effect: 'validationError' }

Batching orderCommand Effects ​

A common pattern: collect orderCommand effects from hook listeners and batch them into a single messageActor:

filtrera
let hookEffects = { orderId = orderId, lines = lines } triggerHook 'OnClaimResolve'

let hookOrderCommands = hookEffects where e => e.effect == 'orderCommand'

from hookOrderCommands count > 0 match
  true |> {
    effect = 'messageActor'
    actorType = 'order'
    actorId = orderId
    messageType = 'applyCommands'
    body = { commands = hookOrderCommands }
  }

Using triggerHook in Ingresses and Jobs ​

New

In the reactor runtime (ingresses, jobs, modules), triggerHook works the same way — it triggers rule evaluation and returns the effects. However, there is no actor spec to automatically process the returned effects. The calling component must explicitly apply them.

Effect Processing ​

The caller receives effect records and must use its own runtime capabilities (messageActor, scheduleJob, etc.) to apply them:

filtrera
import 'iterators'

let hookEffects = { cartId = cartId, cart = renderedCart } triggerHook 'OnCartMutation'
let effects = hookEffects buffer

// Apply ticketCommand effects via messageActor
let commandEffects = effects where is { effect: 'ticketCommand' } buffer
from commandEffects count > 0 match
  true |>
    from messageActor ('ticket', cartId, [{
      type = 'applyCommands'
      body = {
        commands = commandEffects
          select e => { type = e.type, fields = e.fields }
          as `list`
      }
    }])

// Schedule jobs from scheduleJob effects
let jobEffects = effects where is { effect: 'scheduleJob' } buffer
from jobEffects select j =>
  from scheduleJob (j.definition, j.at, j.parameters)

Custom Effects ​

When defining hook points, listeners often use the custom effect type to return data to the caller without triggering platform-level processing:

filtrera
// Listener returns custom effect
from {
  effect = 'custom'
  type = 'priceLookup'
  priceListId = 'wholesale-2025'
  discount = 0.15
}
filtrera
// Caller collects custom effects
let hookEffects = { currencyCode = 'SEK' } triggerHook 'OnPriceListSelect'

let priceList = hookEffects
  where is { effect: 'custom', type: 'priceLookup' }
  first

Custom effects are silently ignored by the platform's effect processor — they only have meaning when collected by the caller.

Hook Naming Convention ​

ConventionExample
Use On prefixOnCartMutation, OnClaimResolve
PascalCaseOnOrderValidated, OnPriceListSelect
Describe the eventWhat happened, not what should happen

See Also ​

© 2026 Hantera AB. Org. no.: 559242-9582