TripleBooks

Creating custom DSL rules

Write posting rules that turn your events into journal entries.

Overview

The posting engine uses a DSL to map events to journal lines. Built-in packs cover standard cases; custom rules handle your categories, vendors, and exceptions.

Open Accounting → Rules (/accounting/rules). The page has Categorization (counterparty patterns → event type) and Posting Rules (DSL packs that write journal lines).

Rules

You can also ask Luca (G then L) to draft a rule and review the proposal.

Rule shape

rule "Rule Name" {
  on "event_type"
  basis cash|accrual
  sources [DEPOSITORY, CREDIT_CARD, ...]
  priority 10
  where <condition>

  let variable = <expression>

  post {
    debit {
      account: account("role.subrole")
      amount: <expression>
      currency: <expression>
      memo: <expression>
    }
    credit {
      account: account("cash.operating")
      amount: <expression>
      currency: <expression>
    }
  }
}

Example: SaaS subscription payment

rule "SaaS Subscription Payment" {
  on "vendor_payment"
  basis cash
  sources [DEPOSITORY]
  priority 15
  where contains(payload.category, "software")

  let amt = coalesce(payload.total_amount, payload.amount)
  let ccy = coalesce(payload.currency, "USD")

  post {
    debit {
      account: account("expense.software")
      amount: amt
      currency: ccy
      memo: coalesce(payload.vendor, "") ++ " — " ++ coalesce(payload.description, "")
    }
    credit {
      account: account("cash.operating")
      amount: amt
      currency: ccy
    }
  }
}
ClauseRole
on "vendor_payment"Event type this rule handles
basis cashRecognize expense when cash moves
sources [DEPOSITORY]Bank accounts, not cards
priority 15Higher wins when several rules match
where …Extra filter on the payload
post { … }Debit expense, credit cash

Account roles

account("role.subrole") resolves to your chart via account mappings (workspace settings). Common roles include cash.operating, expense.software, expense.general, revenue.services, liability.accounts_payable, and asset.accounts_receivable.

Conditions

FunctionExample
contains(str, substr)contains(payload.category, "legal")
starts_with(str, prefix)starts_with(payload.vendor, "AWS")
between(val, lo, hi)between(payload.amount, 0, 1000)
is_null(val)is_null(payload.tax_amount)
coalesce(a, b)coalesce(payload.memo, "No memo")

Combine with and, or, and not.

Give specialized rules a higher priority so they win over a general vendor_payment rule. You can also requires balance("liability.accounts_payable") > 0 when a payment should only post against an open payable.

Test before publish

In Luca:

Test this rule against a sample event with amount 500 and category software

Or POST /api/dsl/evaluate with the rule source and a sample payload. The response shows whether the rule matched and which journal lines it would post.

Publish

  1. Open Accounting → Rules
  2. Edit your custom pack (or create one)
  3. Add the rule — packs are versioned on each edit
  4. Published rules apply to future events

Your custom pack sits above standard and industry packs. See Industry rule packs and the rules reference.

Next steps

On this page