---
title: "How do I add a posting module with posting rules to a chart of accounts?"
slug: "how-do-i-add-a-posting-module-with-posting-rules-to-a-chart-of-accounts"
updated: 2025-10-15T10:45:26Z
published: 2025-10-15T10:45:26Z
canonical: "support.lusid.com/how-do-i-add-a-posting-module-with-posting-rules-to-a-chart-of-accounts"
---

> ## Documentation Index
> Fetch the complete documentation index at: https://support.lusid.com/llms.txt
> Use this file to discover all available pages before exploring further.

# How do I add a posting module with posting rules to a chart of accounts?

You can add one or more posting modules, each containing any number of posting rules, to an existing [chart of accounts](/v1/docs/how-do-i-create-a-chart-of-accounts) (CoA).

Note the **order in which posting rules are specified** in a module is significant. For each journal entry line, LUSID uses [the first matching posting rule found](/v1/docs/assigning-economic-activity-to-general-ledger-accounts-using-posting-rules) to post the debit or credit amount to a particular general ledger account.

## Methods

LUSID web appLUSID APILuminesce

Call the [CreatePostingModule](https://www.lusid.com/docs/api/lusid/endpoints/chart-of-accounts/CreatePostingModule) API, for example:

```json
curl -X POST 'https://<your-domain>.lusid.com/api/api/chartofaccounts/CoAs/DailyCoA/postingmodules'
  -H 'Content-Type: application/json-patch+json' 
  -H 'Authorization: Bearer <your-API-access-token>'
  -d '{
  "code": "DailyPM",
  "displayName": "Daily NAV posting module",
  "description": "This is a posting module for daily NAV",
  "rules": [
    {
      "ruleId": "rule_0001",
      "account": "Other-Investments",
      "ruleFilter": "EconomicBucket startswith 'NA' and HoldType neq 'P'"
    },
    {
      "ruleId": "rule_0002",
      "account": "Cash",
      "ruleFilter": "EconomicBucket startswith 'NA' and HoldType eq 'B'"
    },
    {
      "ruleId": "rule_0003",
      "account": "Commitments",
      "ruleFilter": "EconomicBucket eq 'NA_Cost' and HoldType eq 'C'"
    }
  ]
}'
```

Navigate to **Fund Accounting > Chart of Accounts**, open the **Posting Modules** tab, select the parent CoA and click the **Create posting module** button.

You can either add posting rules to the module using the **Add** button in this dialog, or alternatively navigate to the **Posting Rules** tab.

![](https://cdn.document360.io/d575ad81-c0ed-4980-bbd1-d59ac5c3de82/Images/Documentation/image(879).png)

Write a Luminesce SQL query using the [Lusid.PostingModule.Writer](/v1/docs/lusidpostingmodulewriter) provider to create a posting module and the [Lusid.PostingModule.Rule.Writer](/v1/docs/lusidpostingmodulerulewriter) provider to set posting rules, and execute it using a tool such as the LUSID web app, for example:

![](https://cdn.document360.io/d575ad81-c0ed-4980-bbd1-d59ac5c3de82/Images/Documentation/image(969).png)

## Data fields

This section supplements the [API documentation](https://www.lusid.com/docs/api/lusid/endpoints/chart-of-accounts/CreatePostingModule) and on-screen help text in the LUSID web app.

A posting module is identified by a `code` that must be unique within the parent CoA.

You can specify any number of posting rules. Note the order is significant. Each posting rule consists of:

- A `ruleId` that uniquely identifies the posting rule in the posting module.
- The code of an `account` to post the credit or debit amount to. Note you can use Mustache template syntax to [parameterise the codes of similar accounts](/v1/docs/how-do-i-add-a-posting-module-with-posting-rules-to-a-chart-of-accounts#parameterising-account-codes-to-reduce-posting-rules), potentially reducing the number of posting rules.
- A `ruleFilter` specifying a rule. [Syntax and allowed values](/v1/docs/how-do-i-add-a-posting-module-with-posting-rules-to-a-chart-of-accounts#syntax-of-a-posting-rule).

## Subsequent updates

Once created, you can edit an existing posting module as follows:

- To change the `displayName` or `description` of the module, call the [SetPostingModuleDetails](https://www.lusid.com/docs/api/lusid/endpoints/chart-of-accounts/SetPostingModuleDetails) API. You cannot change the `code`.
- To change the order of existing rules in the module, call the [SetPostingModuleRules](https://www.lusid.com/docs/api/lusid/endpoints/chart-of-accounts/SetPostingModuleRules) API. Note you must specify *all* the rules again, this time in the required order; any omitted from the request are removed.
- To insert one or more new posting rules at a specific order, call the [PatchPostingModule](https://www.lusid.com/docs/api/lusid/endpoints/chart-of-accounts/PatchPostingModule) API. For example, the following request adds a new rule as the second in the list (the index starts at 0):

```json
curl -X PATCH 'https://<your-domain>.lusid.com/api/api/chartofaccounts/CoAs/DailyCoA/postingmodules/DailyPM'
  -H 'Content-Type: application/json-patch+json' 
  -H 'Authorization: Bearer <your-API-access-token>'
  -d '[
  {
    "value": {
      "ruleId": "rule_0001a",
      "generalLedgerAccountCode": "Error",
      "ruleFilter": "EconomicBucket eq 'PL_Other'"
    },
    "path": "/rules/1",
    "op": "add"
  }
]'
```

To add a new rule as the first in the list, specify a `path` of `/rules/0`. To add a new rule last, specify `/rules/-`. LUSID automatically moves the index position of other rules.
- To delete a posting module, call the [DeletePostingModule](https://www.lusid.com/docs/api/lusid/endpoints/chart-of-accounts/DeletePostingModule) API.

Note it may be easier to change the order of posting rules in the LUSID web app using the ‘grab handles’ (in yellow) or up/down arrows (in red):

![](https://cdn.document360.io/d575ad81-c0ed-4980-bbd1-d59ac5c3de82/Images/Documentation/image(824).png)

## Parameterising account codes to reduce posting rules

You can use Mustache template syntax in the general ledger account field of a posting rule to derive account codes from [data attributes of journal entry lines](/v1/docs/how-do-i-add-a-posting-module-with-posting-rules-to-a-chart-of-accounts#syntax-of-a-posting-rule), potentially reducing the number of posting rules.

For example, imagine you have four general ledger accounts in your CoA that share the same purpose but with codes that distinguish between portfolio currency and strategy:

1. `1101-GBP-Growth`
2. `1101-USD-Growth`
3. `1101-GBP-Income`
4. `1101-USD-Income`

The following posting rule examines each journal entry line matching the `ruleFilter` expression and posts the amount to an appropriate account:

```json
"rules": [
  {
    "ruleId": "NA-Subscriptions",
    "account": "1101-{{Portfolio.BaseCurrency}}-{{Properties[Transaction/SHKs/Strategy]}}",
    "ruleFilter": "MovementName eq 'Subscriptions'"
  }
]
```

This might replace the following four posting rules with more complicated `ruleFilter` expressions:

```json
"rules": [
  {
    "ruleId": "NA-Subscriptions-01",
    "account": "1101-GBP-Growth",
    "ruleFilter": "MovementName eq 'Subscriptions' and Portfolio.BaseCurrency eq 'GBP' and Properties[Transaction/SHKs/Strategy] eq 'Growth'"
  },
  {
    "ruleId": "NA-Subscriptions-02",
    "account": "1101-USD-Growth",
    "ruleFilter": "MovementName eq 'Subscriptions' and Portfolio.BaseCurrency eq 'USD' and Properties[Transaction/SHKs/Strategy] eq 'Growth'"
  },
  {
    "ruleId": "NA-Subscriptions-03",
    "account": "1101-GBP-Income",
    "ruleFilter": "MovementName eq 'Subscriptions' and Portfolio.BaseCurrency eq 'GBP' and Properties[Transaction/SHKs/Strategy] eq 'Income'"
  },
  {
    "ruleId": "NA-Subscriptions-04",
    "account": "1101-USD-Income",
    "ruleFilter": "MovementName eq 'Subscriptions' and Portfolio.BaseCurrency eq 'USD' and Properties[Transaction/SHKs/Strategy] eq 'Income'"
  }
]
```

## Syntax of a posting rule

A posting rule consists of:

- A `ruleId` that uniquely identifies the posting rule in the posting module.
- An `account` that is the code of an account in the parent chart of accounts (CoA).
- A `ruleFilter` expression that describes criteria for matching journal entry lines to the account.

The syntax of a `ruleFilter` expression is:

`&lt;attribute&gt; &lt;operator&gt; &lt;value&gt;`

...where:

- `&lt;attribute&gt;` and `&lt;value&gt;` are explained in the table below.
- `&lt;operator&gt;` is one of the [LUSID filter operators](/v1/docs/filtering-information-retrieved-from-lusid#appendix-a-supported-operators).

Note the following:

- A `ruleFilter` expression is case-insensitive.
- A string `value` must be enclosed in single straight quote marks (the `%27` UTF-8 encoding).
- You can concatenate expressions using the `and` and `or` operators, for example: `MovementName eq 'Side1' and subholdingkeys[Transaction/Ibor/Strategy] eq 'Income'`

If you use both, [standard boolean operator precedence](/v1/docs/filtering-information-retrieved-from-lusid#logical-operators) applies: `InstrumentScope startswith 'Custom' or (LocalAmount lte 1000 and HoldType eq 'P')`

| `&lt;attribute&gt;` of a journal entry line | **Data type** | **Example expression** | **Explanation/origin of** `&lt;value&gt;` |
| --- | --- | --- | --- |
| `SourceType` | String | `SourceType eq 'LusidTransaction'` | Either `LusidTransaction` or `LusidValuation`. Use `LusidValuation` to map LUSID's automatic mark-to-market valuation activity to an account. |
| `ActivityDate` | DateTime | `ActivityDate gt 2023-01-01` | For a transaction, this is the trade date. For a valuation, this is the date of the latest detected change to market data. |
| `EconomicBucket` `EconomicBucketComponent` `EconomicBucketVariant` | String | `EconomicBucket startswith 'PL_Real'` `EconomicBucketComponent eq 'CrossGainLoss'` `EconomicBucketVariant eq 'ShortTerm'` | LUSID automatically categorises every journal entry line into a broad economic bucket. Some economic buckets are further categorised into components and variants. [More information](/v1/docs/what-are-economic-buckets). |
| `SourceId` | String | `SourceId eq 'Txn01'` | For a transaction, this is the unique identifier in the portfolio (`txnId`). For a valuation, this is the date and time of the latest change to market data (stored as a string, so [only string operators are available](/v1/docs/filtering-information-retrieved-from-lusid#storage-formats)). |
| `TaxLotId` | String | `SourceId eq 'Txn01'` | For a transaction, if the underlying instrument is a currency and the trade is settled (holding type of `B`), the value is `1` (stored as a string). Otherwise, this is the same as the `SourceId`. |
| `LocalAmount` | Decimal | `LocalAmount lte 1000` | The amount in the transaction currency. |
| `BaseAmount` | Decimal | `BaseAmount gt 450000` | The amount in the portfolio base currency. |
| `DefaultCurrency` | String | `DefaultCurrency in 'GBP','USD'` | The currency of the underlying instrument, if specified. |
| `InstrumentScope` | String | `InstrumentScope startswith 'Custom'` | The [instrument scope](/v1/docs/understanding-instrument-scopes) in which the underlying instrument is mastered. |
| `LusidInstrumentId` | String | `LusidInstrumentId not startswith 'CCY'` | The [LUID](/v1/docs/what-is-a-lusid-instrument-id-or-luid) (globally unique ID) of the underlying instrument. |
| `HoldType` | String | `HoldType eq 'P'` | One of the LUSID [holding type codes](/v1/docs/what-are-the-holding-types). |
| `MovementName` | String | `MovementName eq 'Side1'` | For a transaction, this is the name of the [movement](/v1/docs/what-is-a-movement) in the transaction type to which the transaction belongs. If the movement does not have a name (and note its `name` field is optional), then this is the name of the movement's [side](/v1/docs/what-is-a-side). For a valuation, this is always `MarkToMarket`. |
| `MovementSign` | String | `MovementSign eq 'Long'` | Either `Long` or `Short`. |
| `HoldingSign` | String | `HoldingSign eq 'Short'` | Either `Long` or `Short`. You might use this to post amounts of a single transaction that moves from long to short to different accounts. For example, if you have a holding of £100 but a transaction for -£120 then the following journal entry lines are produced: \| EconomicBucket \| Amount \| MovementSign \| HoldingSign \| \| --- \| --- \| --- \| --- \| \| `NA_COST` \| `-100` \| `Short` \| `Long` \| \| `NA_COST` \| `-20` \| `Short` \| `Short` \| |
| EconomicBucket | Amount | MovementSign | HoldingSign |
| `NA_COST` | `-100` | `Short` | `Long` |
| `NA_COST` | `-20` | `Short` | `Short` |
| A [sub-holding key](/v1/docs/how-do-i-register-sub-holding-keys-shks-with-a-portfolio) on the portfolio | User-defined | `subholdingkeys[Transaction/Ibor/Strategy] eq 'Income'` | Under-the-hood, a SHK is defined as a [custom property](/v1/docs/properties) with a 3-stage key in the `Transaction` domain. |
| A portfolio field | System-defined | `Portfolio.displayName eq 'UK-Equities'` `Portfolio.parentPortfolioId.Code eq 'FixedIncome'` | This can be any of the [stored fields for a portfolio](https://www.lusid.com/docs/api/lusid/schemas/Portfolio) except the `href`, `links` and `version` fields, prefixed by `Portfolio.` Nested fields can be accessed using dot notation. ![](https://cdn.document360.io/d575ad81-c0ed-4980-bbd1-d59ac5c3de82/Images/Documentation/a4512c98-ede4-44e5-8105-f796f49067ae.png) |
| A portfolio property | User-defined | `properties[Portfolio` `/Ibor/Manager] in 'John Smith','Jane Jones'` | This can be any property with a 3-stage key in the Portfolio domain. |
| An instrument field (see also a transaction instrument field, below) | System-defined | `Instrument.identifiers['LusidInstrumentId'] eq 'LUID_ZZZZZZZZ'` `Instrument.instrumentDefinition.instrumentType in 'Equity','Bond'` | This can be any of the [stored fields for an instrument](https://www.lusid.com/docs/api/lusid/schemas/Instrument) except the `href`, `links` and `version` fields, prefixed by `Instrument.` Nested fields can be accessed using dot notation. ![](https://cdn.document360.io/d575ad81-c0ed-4980-bbd1-d59ac5c3de82/Images/Documentation/83acee5f-9315-4e00-9e39-c0e007494e23.png) |
| An instrument property (see also a transaction instrument property, below) | User-defined | `properties[Instrument/Ibor/AnalystRating] any (~ startswith 'A')` | This can be any property with a 3-stage key in the `Instrument` domain. |
| A transaction field | System-defined | `Transaction.totalConsideration.amount gt 2000` `Transaction.instrumentScope eq 'ProtectedScope'` | This can be any of the [stored fields for a transaction](https://www.lusid.com/docs/api/lusid/schemas/Transaction), prefixed by `Transaction.` Nested fields can be accessed using dot notation. ![](https://cdn.document360.io/d575ad81-c0ed-4980-bbd1-d59ac5c3de82/Images/Documentation/1e53a2ad-e4c6-47e8-8481-1ba99c62c087.png) |
| A transaction property | User-defined | `properties[Transaction/Ibor/Broker] eq 'AcmeCorp'` | This can be any property with a 3-stage key in the `Transaction` domain. |
| An instrument field on a transaction | System-defined | `Transaction.Instrument.instrumentDefinition.instrumentType in 'Equity','Bond'` | If a journal entry line is generated in response to a currency movement, then the instrument of the line itself is a currency. The instrument of the original transaction, however, may be (for example) an equity. This gives you access to the instrument fields and properties of the equity. The available fields and properties are the same as those for the instrument of the line itself, above. |
| An instrument property on a transaction | User-defined | `Transaction.Properties[Instrument/Ibor/AnalystRating] any (~ startswith 'A')` |
| An account field | System-defined | `Account.type in 'Asset','Liabilities'` | This can be any of the [stored fields for an account](https://www.lusid.com/docs/api/lusid/schemas/Account), prefixed by `Account.` Nested fields can be accessed using dot notation. ![](https://cdn.document360.io/d575ad81-c0ed-4980-bbd1-d59ac5c3de82/Images/Documentation/e103a2fa-c7d5-4314-8813-4ca9c75c5028.png) |
| An account property | User-defined | `properties[Account/Type/AssetClass] eq 'Equity'` | This can be any property with a 3-stage key in the `Account` domain. |
| A fund property | User-defined | `properties[Fund/Managers/Name] eq 'Fred Bloggs'` | This can be any property with a 3-stage key in the `Fund` domain. |
| An ABOR property (V1 funds only) | User-defined | `properties[Abor/Client/Accountant] not in 'Fred Bloggs', 'Sarah Smart'` | This can be any property with a 3-stage key in the `Abor` domain. |
