--- title: "How do I create a matching ruleset?" slug: "how-do-i-create-an-advanced-reconciliation-matching-ruleset" updated: 2026-10-23T16:00:24Z published: 2026-10-23T16:00:24Z canonical: "support.lusid.com/how-do-i-create-an-advanced-reconciliation-matching-ruleset" --- > ## 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 create a matching ruleset? You can create a matching ruleset to define how LUSID compares data for a reconciliation (rec) type, such as holding, input transaction, or valuation. Once you create a matching ruleset, you can use it in any number of [reconciliation definitions](/v1/docs/how-do-i-configure-an-advanced-reconciliation-definition). **Recommended reading**: [Understanding advanced reconciliations](/v1/docs/advanced-reconciliations-1) ## Methods **LUSID web app** 1. Navigate to **Advanced Recs > Matching Rulesets**. 2. Click the **Create matching ruleset** button and populate the form. Continue reading this article to learn more about the fields you can populate in a matching ruleset. ![](https://cdn.document360.io/d575ad81-c0ed-4980-bbd1-d59ac5c3de82/Images/Documentation/image-L67O445G.png?sv=2026-02-06&spr=https&st=2026-11-12T13%3A12%3A39Z&se=2026-11-12T14%3A12%3A39Z&sr=c&sp=r&sig=xLQ8GSx2eVfweZHl4f%2BC1UFHKScOqMK91FY5vvyLnZQ%3D) **LUSID API** Call the [CreateMatchingRuleset](https://www.lusid.com/docs/api/lusid/endpoints/recs/CreateMatchingRuleset) API, for example: ```json curl -X POST "https://.lusid.com/api/api/recs/matchingrulesets"  -H "Authorization: Bearer "  -d "{  "id": {    "scope": "Finbourne-Examples",    "code": "HoldingUnits"  },  "displayName": "Compare holding units",  "recType": "Holding",  "filters": {    "left": "holdingType eq 'P'",    "right": "holdingType eq 'P'"  },  "coreRules": [    {      "ruleName": "Instrument",      "leftFormula": "Properties[Instrument/default/Figi]",      "rightFormula": "Properties[Instrument/default/Figi]",      "isCaseSensitive": false    }  ],  "aggregateRules": [    {      "ruleName": "Units",      "leftFormula": "units",      "leftOperation": "Sum",      "rightFormula": "units",      "rightOperation": "Sum"    }  ],  "allowPartialMatching": true }" ``` ## Fields ### Rec type Specifies the reconciliation type this matching ruleset applies to, out of: - Holding - Cash holding - Valuation - Input transaction - Output transaction - Settlement activity ### Pre-filters You can specify a left and right [LUSID filtering expression](/v1/docs/filtering-information-retrieved-from-lusid) that LUSID applies to that side’s data before comparing anything. ### Core rules Core rules tell LUSID how to pair up records from each side of the reconciliation. Each core rule defines a value that must agree on both sides for LUSID to treat two records as the same. For a holdings reconciliation, for example, this could be an [instrument identifier](/v1/docs/understanding-instrument-identifiers) such as a FIGI or ISIN. Each core rule has: - A **Name**, which you see as a column heading in your results. - A **Left formula** and **Right formula** which use [LUSID filtering syntax](/v1/docs/filtering-information-retrieved-from-lusid) to tell LUSID where to find the value on each side. See [Appendix A](/v1/docs/how-do-i-create-an-advanced-reconciliation-matching-ruleset#appendix-a-builtin-fields-for-each-rec-type) for more information on accepted formula values. If you add more than one core rule, LUSID only pairs two records together when the values for every core rule agree, or fall within that rule’s [core tolerances](/v1/docs/how-do-i-create-an-advanced-reconciliation-matching-ruleset#core-tolerances). > [!TIP] > 💡 Tip > > Choose a core rule (or set of core rules) that: > > - [x] Identifies each record uniquely > - [x] Contains a value for every record on both sides > > For example, cash balances don’t have a FIGI, so a FIGI core rule gives every cash balance the same empty value. Use [pre-filters](/v1/docs/how-do-i-create-an-advanced-reconciliation-matching-ruleset#filters) to exclude records like these, and reconcile them separately with a suitable rec type, such as **Cash holding**. #### Core tolerances You can add core tolerances to relax how your core rules behave, so LUSID can pair records whose values for that rule aren’t identical. LUSID always attempts to pair exact records first; it only uses core tolerances for records it can’t pair exactly. You can add up to five core tolerances to a matching ruleset. For each core tolerance, you must specify: - The **Rule name** it relaxes. - A **Tolerance type** and its relevant fields. There are three core tolerance types: For what each core tolerance type does, see [Appendix B: Tolerances](/v1/docs/how-do-i-create-an-advanced-reconciliation-matching-ruleset#appendix-b-tolerances). - [String cross](/v1/docs/how-do-i-create-an-advanced-reconciliation-matching-ruleset#tolerance-type-string-cross) - [Attribute optionality](/v1/docs/how-do-i-create-an-advanced-reconciliation-matching-ruleset#tolerance-type-attribute-optionality) - [Date](/v1/docs/how-do-i-create-an-advanced-reconciliation-matching-ruleset#tolerance-type-date) ### Aggregate rules Aggregate rules tell LUSID which values to compare on records it has already paired, such as the number of units held. For each pair, LUSID shows you the following: - The value from each side - The difference between them For each aggregate rule, you specify: - A **Name**, which you see as a column heading in your results. - A **Left formula** and **Right formula** which tell LUSID where to find the value to compare on each side. See [Appendix A](/v1/docs/how-do-i-create-an-advanced-reconciliation-matching-ruleset#appendix-a-builtin-fields-for-each-rec-type) for more information on accepted formula values. - A **Left operation** and **Right operation** which tell LUSID how to combine the values on each side when more than one record shares the same core rule values. For example, **Sum** adds up the units across all those records. ![](https://cdn.document360.io/d575ad81-c0ed-4980-bbd1-d59ac5c3de82/Images/Documentation/image-2VSB0KXW.png?sv=2026-02-06&spr=https&st=2026-11-12T13%3A12%3A39Z&se=2026-11-12T14%3A12%3A39Z&sr=c&sp=r&sig=xLQ8GSx2eVfweZHl4f%2BC1UFHKScOqMK91FY5vvyLnZQ%3D) #### Aggregate tolerances You can add aggregate tolerances to allow compared values to differ by an acceptable amount without the result becoming a partial match. You can only use the [numeric tolerance type](/v1/docs/how-do-i-create-an-advanced-reconciliation-matching-ruleset#tolerance-type-numeric) for aggregate rules. LUSID applies the numeric tolerance to each side’s total, after it has combined the values using the rule’s operation. See [Appendix B: Tolerances](/v1/docs/how-do-i-create-an-advanced-reconciliation-matching-ruleset#appendix-b-tolerances) for more information. ## Updating a matching ruleset Once created, you can update a matching ruleset in the following ways: - Via the LUSID web app: Find the ruleset you need to update and click the **Edit** icon. ![](https://cdn.document360.io/d575ad81-c0ed-4980-bbd1-d59ac5c3de82/Images/Documentation/image-V1AU78A3.png?sv=2026-02-06&spr=https&st=2026-11-12T13%3A12%3A39Z&se=2026-11-12T14%3A12%3A39Z&sr=c&sp=r&sig=xLQ8GSx2eVfweZHl4f%2BC1UFHKScOqMK91FY5vvyLnZQ%3D) - Via the LUSID API: Call the [UpdateMatchingRuleset](https://www.lusid.com/docs/api/lusid/endpoints/recs/UpdateMatchingRuleset) endpoint. When you update a matching ruleset: - All new instances that use the ruleset will use the updated version. - Reruns of an existing instance keep using the version from when the instance was created. To use the updated version, run the reconciliation definition again; this creates a new instance. See [Rerunning an instance](/v1/docs/how-do-i-run-an-advanced-reconciliation-and-monitor-its-results). ## Appendix A: Using built-in fields and properties in formulas for each rec type The built-in fields you can refer to in a rule formula depend on the rec type. Core rules and aggregate rules refer to the same fields. The following table shows some examples for each rec type: | Rec type | What built-in fields can I use? | What properties can I use? | | --- | --- | --- | | Holding | [Holding fields](https://www.lusid.com/docs/api/lusid/endpoints/transaction-portfolios/GetHoldings), for example: - `InstrumentUid` - `HoldingType` - `Units` - `SettledUnits` - `Cost.Amount` | Properties from the following [domains](/v1/docs/properties): - `Holding` - `Instrument` - `Portfolio` - `CustodianAccount` For example, `Properties[Instrument/default/Isin]`. | | Cash holding | [Holding fields](https://www.lusid.com/docs/api/lusid/endpoints/transaction-portfolios/GetHoldings), for example: - `Units` - `SettledUnits` - `UnsettledUnits` - `HoldingTypeName` - `Cost.Currency` | | Valuation | Valuation fields, for example: - `AddressKeys[Valuation/PV/Amount]` - `AddressKeys[Holding/Units]` - `AddressKeys[Instrument/default/Name]` - `AddressKeys[Valuation/PV].Units` - `AddressKeys[Valuation/EffectiveAt]` | Properties accessed via address key, for example `AddressKeys[Instrument/Properties[Instrument/<scope>/<code>]]` | | Input transaction | [Input transaction](/v1/docs/input-and-output-transactions) fields, for example: - `TransactionId` - `Type` - `TransactionDate` - `Units` - `TotalConsideration.Amount` | Properties from the `Transaction` domain, for example `Properties[Transaction/Finbourne-Examples/Strategy]` | | Output transaction | [Output transaction](/v1/docs/input-and-output-transactions) fields, for example: - `TransactionId` - `Description` - `TransactionDate` - `TransactionAmount` - `AccountingDate` | | Settlement activity | [Settlement activity](/v1/docs/loading-settlement-instructions) fields, for example: - `ActivityType` - `ActivityDate` - `Direction` - `Units` - `Transaction.TransactionId` | Properties from the following domains: - Accessed via `Transaction.Properties[...]`: - `Transaction` - Accessed via `SettlementInstruction.Properties[...]`: - `SettlementInstruction` - `Portfolio` - `Instrument` | ## Appendix B: Tolerances | Tolerance type | Applicable rule type | Data type rule formula must return | | --- | --- | --- | | [String cross](/v1/docs/how-do-i-create-an-advanced-reconciliation-matching-ruleset#tolerance-type-string-cross) | [Core rule](/v1/docs/how-do-i-create-an-advanced-reconciliation-matching-ruleset#core-rules) | Text | | [Attribute optionality](/v1/docs/how-do-i-create-an-advanced-reconciliation-matching-ruleset#tolerance-type-attribute-optionality) | [Core rule](/v1/docs/how-do-i-create-an-advanced-reconciliation-matching-ruleset#core-rules) | Any | | [Date](/v1/docs/how-do-i-create-an-advanced-reconciliation-matching-ruleset#tolerance-type-date) | [Core rule](/v1/docs/how-do-i-create-an-advanced-reconciliation-matching-ruleset#core-rules) | Dates | | [Numeric](/v1/docs/how-do-i-create-an-advanced-reconciliation-matching-ruleset#tolerance-type-numeric) | [Aggregate rule](/v1/docs/how-do-i-create-an-advanced-reconciliation-matching-ruleset#aggregate-rules) | Numbers | ### Tolerance type: String cross A string cross tolerance treats one specific value on a side as equal to a different value on the other side. Use this tolerance type for known differences in how each side records the same thing. > [!WARNING] > Important > > LUSID reports records paired using a string cross tolerance as one of the following in the [results](/v1/docs/understanding-advanced-reconciliations-results): > > - A `Cross` if the aggregate rules agree. > - A `Partial Cross` if the aggregate rules disagree. > > You can check which tolerance LUSID used for a result in its **Applied tolerance** column. | Field | Description | | --- | --- | | **Reference side** | The side where LUSID expects to find the **Reference value**: - `Left` or `Right`: Use if the two values should be treated as equal one way round and not the other. - `Either` (default): Use if you don't mind which side each value is on. | | **Reference value** | The value LUSID looks for on the reference side. | | **Cross value** | The value on the other side that LUSID treats as equal to **Reference value**. | > [!NOTE] > Note > > To treat more than one pair of values as equal, add a separate tolerance for each pair. For example, you could set a string cross tolerance when one side uses `GBP`, whilst the other uses `GBp` (and you want `GBp` to be considered `GBP` for the rec run): ![](https://cdn.document360.io/d575ad81-c0ed-4980-bbd1-d59ac5c3de82/Images/Documentation/image-EGA4IUD9.png?sv=2026-02-06&spr=https&st=2026-11-12T13%3A12%3A39Z&se=2026-11-12T14%3A12%3A39Z&sr=c&sp=r&sig=xLQ8GSx2eVfweZHl4f%2BC1UFHKScOqMK91FY5vvyLnZQ%3D) If you’re creating a ruleset via API, you might specify the following: ```json {  "toleranceType": "CoreStringCross",  "ruleName": "Currency",  "referenceValue": "GBP",  "crossValue": "GBp",  "referenceSide": "Left" } ``` ### Tolerance type: Attribute optionality An optionality tolerance allows LUSID to pair records when one side has no value for the core rule, for example because a property isn't set. | Field | Description | | --- | --- | | **Optional side** | Sets which side is allowed to have no value: - `Left` - `Right` - `Either` (default) | For example, you could set a tolerance where a right side with no strategy pairs with any strategy on the left side, whilst a left side with no strategy still has to agree with the right side exactly: ![](https://cdn.document360.io/d575ad81-c0ed-4980-bbd1-d59ac5c3de82/Images/Documentation/image-BA6JKERD.png?sv=2026-02-06&spr=https&st=2026-11-12T13%3A12%3A39Z&se=2026-11-12T14%3A12%3A39Z&sr=c&sp=r&sig=xLQ8GSx2eVfweZHl4f%2BC1UFHKScOqMK91FY5vvyLnZQ%3D) If you’re creating a ruleset via API, you might specify the following: ```json {  "toleranceType": "CoreAttributeOptionality",  "ruleName": "Strategy",  "optionalSide": "Right" } ``` ### Tolerance type: Date A date tolerance pairs records when the date on one side is within an interval of the date on the other side. | Field | Description | | --- | --- | | **Reference side** | The side whose date LUSID measures the interval *from*: `Left` or `Right`. | | **Interval** | How far the other side's date can be from the reference date, as an [ISO 8601 period](https://www.iso.org/iso-8601-date-and-time-format.html). For example: - `P1D` is one day - `PT12H` is 12 hours | | **Offset** | Which direction the other side's date *can* differ in: - `Earlier` - `Later` - `Either` (default) | > [!NOTE] > Note > > - The date range includes the dates at each end. > - A record with no date never falls within a date tolerance. For example, you could set a tolerance where a left settlement date 1 Oct 2026 pairs with a right settlement date anywhere from 30 Sep to 2 Oct 2026: ![](https://cdn.document360.io/d575ad81-c0ed-4980-bbd1-d59ac5c3de82/Images/Documentation/image-WX96806H.png?sv=2026-02-06&spr=https&st=2026-11-12T13%3A12%3A39Z&se=2026-11-12T14%3A12%3A39Z&sr=c&sp=r&sig=xLQ8GSx2eVfweZHl4f%2BC1UFHKScOqMK91FY5vvyLnZQ%3D) If you’re creating a ruleset via API, you might specify the following: ```json {  "toleranceType": "CoreDateTolerance",  "ruleName": "SettlementDate",  "referenceSide": "Left",  "interval": "P1D",  "offset": "Either" } ``` ### Tolerance type: Numeric A numeric tolerance allows the totals for an aggregate rule to differ by a specified acceptable amount. | Field | Description | | --- | --- | | **Reference side** | Whether LUSID should measure the difference from the `Left` or `Right` total. | | **Absolute threshold** | The difference allowed, in the rule’s own units. Must be greater than 0. | | **Relative threshold** | The difference allowed, as a percentage of the reference side’s total. Must be greater than 0. For example, `10` allows a 10% difference. | | **Threshold priority** | If you set both **Absolute threshold** and **Relative threshold**, this specifies how LUSID combines the two thresholds: - `GreaterOf`: Passes the difference if it’s within either threshold. - `LesserOf`: Only passes the difference if it’s within both thresholds. | | **Offset** | Which direction the non-reference side’s total can differ in: - `Above` - `Below` - `Either` (default) | > [!NOTE] > Note > > - The range *includes* the values at each end. > - A side with no total never falls within a numeric tolerance. > - If you add more than one numeric tolerance to the same rule, the totals pass if they’re within any one of them. > - For **Relative threshold**, the inputs vary depending on whether you’re using the LUSID web app or LUSID API: > - LUSID web app: Specify a percentage, for example `10` allows a 10% difference > - LUSID API: Specify a fraction, for example `0.1` allows a 10% difference For example, this is how different thresholds could apply if the left side’s total is 100 and the right side’s total is 90 (a difference of 10, or 10% of the reference total): | Threshold scenario | Absolute threshold | Relative threshold (%) | Threshold priority | Within tolerance? | | --- | --- | --- | --- | --- | | **Absolute threshold** equals the difference. | `10` | Not set | Not set | ✅ | | **Absolute threshold** is less than the difference. | `9` | Not set | Not set | ❌ | | **Relative threshold** equals the relative difference. | Not set | `10` | Not set | ✅ | | Specifying both an **Absolute** and **Relative threshold**, with a **Threshold priority** of `GreaterOf`. | `9` | `10` | `GreaterOf` | ✅ | | Specifying both an **Absolute** and **Relative threshold**, with a **Threshold priority** of `LesserOf`. | `9` | `10` | `LesserOf` | ❌ | Creating the final threshold scenario in the LUSID web app looks like this: ![](https://cdn.document360.io/d575ad81-c0ed-4980-bbd1-d59ac5c3de82/Images/Documentation/image-Q0JDAA51.png?sv=2026-02-06&spr=https&st=2026-11-12T13%3A12%3A39Z&se=2026-11-12T14%3A12%3A39Z&sr=c&sp=r&sig=xLQ8GSx2eVfweZHl4f%2BC1UFHKScOqMK91FY5vvyLnZQ%3D) If you’re creating a ruleset via API, you might specify the following: ```json {  "toleranceType": "Numeric",  "ruleName": "Units",  "referenceSide": "Left",  "absoluteThreshold": 9,  "relativeThreshold": 0.1,  "thresholdPriority": "LesserOf",  "offset": "Either" } ```