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

# Tagging & Journal Entry Tracking

> Allocate payroll line items across tags for accounting codes and journal entries

## Overview

Tags categorize and allocate payroll amounts across dimensions like department, project, or location, so costs can be tracked, split, and mapped to the right accounting codes automatically.

Each line item can carry a tag assignment, a structure that defines how that line item's dollar amount is distributed across one or more tags. Those allocations are used to determine accounting codes and detailed journal entries.

The typical flow looks like this:

1. **Define tag groups and tags**: create tag groups ("Department", "Project") and tags within them ("Engineering", "Sales", "Project Alpha")
2. **Assign tags**: set tag assignments on work assignments, generators, or individual line items to describe how costs are distributed
3. **Configure accounting code rules**: define rules that map tags to expense and liability accounting codes, resolved automatically during payroll calculation
4. **Generate journal entries**: tag allocations produce journal entry rows, one per allocation per tracking dimension, ready to export to the connected accounting system

## Concepts

* **[Tag groups and tags](#setting-up-tag-groups-and-tags)**: tag groups (e.g. "Department") and their values (e.g. "Engineering", "Sales")
* **[Tag assignments and allocations](#tag-assignments-and-allocations)**: how an amount is distributed across tags, with one or more allocations
* **[Assigning tags](#assigning-tags)**: tag assignments can be set on work assignments, generators, and line items, and [resolve down](#tag-resolution-and-precedence) to line items automatically
* **[Primary tag group](#primary-tag-group)**: the tag group whose accounting code rules are applied
* **[Accounting code rules](#accounting-code-rules)**: map tags to expense and liability accounting codes, evaluated by [precedence](#rule-precedence)
* **[Derived tags on statutory withholdings](#derived-tags-on-statutory-withholdings)**: withholdings calculate their allocations from applicable remuneration
* **[Journal entry tracking dimensions](#journal-entry-tracking-dimensions)**: tag groups marked as tracking dimensions split journal entry rows with matched accounting codes

## Setting up tag groups and tags

Tag groups represent a category to track (e.g. "Department"). Tags are the values within that group (e.g. "Engineering", "Sales"). Each tag group belongs to a business entity. A tag group can also be marked as a journal entry tracking dimension; see [Journal entry tracking dimensions](#journal-entry-tracking-dimensions).

1. [Create a tag group](/api-reference/tag-groups/create-a-tag-group) for each category to track
2. [Create tags](/api-reference/tags/create-a-tag) within each group

Tag groups carry a few optional properties beyond their label:

* `color`: a hex colour (e.g. `#AABBCC`) used for display.
* `is_component_locked`: when `true`, the tags in this group cannot be edited through the embedded Component. They can still be managed through the API.
* `is_journal_entry_dimension`: see [Journal entry tracking dimensions](#journal-entry-tracking-dimensions).
* `archived_at`: archived tag groups (and tags) are excluded from index listings by default. Pass `include_archived=true` to include them.
* `is_deletable`: `false` while the group is the primary tag group, or while any of its tags are used in active allocations. The same applies to individual tags.

Tag groups and tags imported from an accounting integration carry the external identifiers (`integrations.xero`, `integrations.quickbooks`) that link them back to the source system, so they round-trip on re-sync. See [Syncing tracking dimensions](/guides/advanced/accounting#syncing-tracking-dimensions).

See the [Tag Groups](/api-reference/tag-groups/list-tag-groups) and [Tags](/api-reference/tags/list-tags) API reference for full details.

## Tag assignments and allocations

A **tag assignment** describes how a line item's amount is distributed across tags. Tag assignments can be set on work assignments, generators, and line items. Each contains a `unit` that determines how allocations are measured, and one or more **tag allocations**, each with a set of tags and an allocation value.

#### Example tag assignment structure

```jsonc theme={null}
{
  "tag_assignment": {
    "unit": "percentage",
    "tag_allocations": [
      {
        "tags": [
          "<engineering_tag_id>", // From department tag group
          "<project_alpha_tag_id>", // From project tag group
        ],
        "value": 60,
      },
      {
        "tags": [
          "<sales_tag_id>", // From department tag group
        ],
        "value": 40,
      },
    ],
  },
}
```

In this example, 60% of the amount is attributed to the Engineering department on Project Alpha, and 40% to the Sales department. Each allocation can combine tags from different tag groups, but only one tag per group is allowed in each allocation.

Allocations must sum to less than or equal to 100%. Any unallocated remainder falls back to the line item's own accounting codes. If no accounting code can be determined, the payroll is blocked until the allocation is resolved.

### Supported units by resource

The `unit` on a tag assignment controls how allocation values are interpreted. Available units depend on the resource type. Work assignments and generators only support `percentage` because they define default allocations before line item amounts are known. Only line items have concrete dollar values and hours that can be allocated directly.

| Resource                                                                                                  | Supported units                 |
| --------------------------------------------------------------------------------------------------------- | ------------------------------- |
| Work assignment                                                                                           | `percentage`                    |
| Pay rate, overtime rate, employee benefit, employer benefit, reimbursement, earning, allowance, deduction | `percentage`                    |
| Earning line item                                                                                         | `percentage`, `amount`, `hours` |
| Deduction, allowance, reimbursement, employee benefit, employer benefit line item                         | `percentage`, `amount`          |

## Assigning tags

### Work assignments

A `tag_assignment` on a work assignment defines the default allocation for all of that employee's line items. Work assignment tag assignments use the `percentage` unit. Each allocation defines what percentage of the total amount is attributed to those tags. Allocations must sum to less than or equal to 100%.

Pass `tag_assignment` when [creating](/api-reference/work-assignments/create-a-work-assignment) or [updating](/api-reference/work-assignments/update-a-work-assignment) a work assignment. Pass `null` to remove it. Use `?expand=tag_assignment` to include it in responses.

### Generators

In this guide, **generators** refers to the resources that produce line items: pay rates, overtime rates, earnings, deductions, allowances, reimbursements, and benefits.

Generators support a `tag_assignment` that overrides the work assignment's `tag_assignment` for line items created by that generator. Generator tag assignments use `percentage` allocations only.

Pass `tag_assignment` when creating or updating any generator resource.

### Line items

Line items support a `custom_tag_assignment` that overrides the managed tag assignment inherited from the work assignment or generator.

#### Allocation units

| Unit         | Description                         | Validation                                                             | Use case                                                  |
| ------------ | ----------------------------------- | ---------------------------------------------------------------------- | --------------------------------------------------------- |
| `percentage` | Split by percentage of total amount | Allocations must sum to less than or equal to 100%                     | Default, same as work assignments and generators          |
| `hours`      | Split by hours worked               | Allocations must sum to less than or equal to the line item's `hours`  | Earning line items only, attribute costs by hours per tag |
| `amount`     | Split by fixed dollar amounts       | Allocations must sum to less than or equal to the line item's `amount` | Attribute exact dollar amounts per tag                    |

Earning line items accept all three units. Non-earning line items (deductions, allowances, reimbursements, benefits) accept `percentage` and `amount` only.

#### Expanding tag assignments

Line items expose several expandable tag properties. Each returns the full tag assignment structure:

| Expand                   | Description                                                                                              |
| ------------------------ | -------------------------------------------------------------------------------------------------------- |
| `tag_assignment`         | The effective tag assignment: the `custom_tag_assignment` if set, otherwise the `managed_tag_assignment` |
| `custom_tag_assignment`  | Optional override: if set, these allocations are used instead of the managed value                       |
| `managed_tag_assignment` | The auto-inherited allocation from the work assignment or generator                                      |
| `tag_assignment_amounts` | The resolved dollar distribution for the line item                                                       |

### Patching a tag assignment

To adjust an existing tag assignment without resending the whole structure, pass `tag_assignment_patch` instead of `tag_assignment` on a work assignment or generator. The two keys are mutually exclusive.

| Field                   | Description                                                                                                                                |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `add_tags`              | Tag IDs to add to every existing allocation. Adding a tag replaces any other tag from the same tag group on that allocation.               |
| `remove_tags`           | Tag IDs to remove from every existing allocation. An allocation left with no tags is deleted; the distribution strategy decides its value. |
| `distribution_strategy` | Governs the value of allocations deleted by `remove_tags`. Defaults to `reallocate`.                                                       |

The `distribution_strategy` accepts:

* `reallocate`: spread the deleted value proportionally across the remaining allocations.
* `unallocated`: drop the value; the assignment's allocations will sum to less than before.
* `error`: refuse the patch if any allocation would be deleted.

## Tag resolution and precedence

Line items on draft payrolls automatically receive a `managed_tag_assignment`, resolved from the generator's `tag_assignment` if set, otherwise from the work assignment's `tag_assignment`.

If a `custom_tag_assignment` is set on the line item, it takes precedence over the managed value. The effective `tag_assignment` (used for accounting code matching and journal entries) is the `custom_tag_assignment` if one exists, otherwise the `managed_tag_assignment`.

Tag resolution only applies to draft payrolls. Once a payroll is approved, its tag assignments are locked. Subsequent changes to work assignment or generator tag assignments will not affect it.

When a work assignment's `tag_assignment` is updated, draft line items whose generator has no `tag_assignment`, have their `managed_tag_assignment` updated to match. When a generator's `tag_assignment` is updated, all of its draft line items have their `managed_tag_assignment` updated to match.

## Primary tag group

A business entity can designate one tag group as its **primary tag group**.

* Tags from this group can be linked to [accounting code rules](#accounting-code-rules), enabling automatic accounting code matching
* When set, every tag allocation must include a tag from the primary tag group

Set the primary tag group through the business entity's [`accounting_settings.primary_tag_group`](/api-reference/business-entities/update-a-business-entity). The top-level `primary_tag_group_id` is a deprecated alias. Setting a primary tag group also flags it as a journal entry dimension. If the primary tag group is changed, draft payrolls with allocations missing a tag from the new primary group will be blocked until corrected.

## Accounting code rules

Accounting code rules map tags to expense and liability accounting codes. Rules are defined on a business entity and scoped to tags in the [primary tag group](#primary-tag-group). Nmbr automatically matches rules against each line item's tag assignment, assigning the matched expense and liability accounting codes to each allocation.

### Rule precedence

Rules are tried in the order shown. If no rule matches the line item at one precedence, the next precedence is tried.

| Precedence  | Fields set on the rule                | Example line item the rule matches                                                  |
| ----------- | ------------------------------------- | ----------------------------------------------------------------------------------- |
| 1 (highest) | `tag_id` + `business_preset_id`       | "Engineering" tag on a line item with the "Senior Developer Salary" business preset |
| 2           | `tag_id` + `type` + `subtype`         | "Engineering" tag on a line item of type earning, subtype salary                    |
| 3           | `tag_id` + `type`                     | "Engineering" tag on any earning line item                                          |
| 4           | `tag_id`                              | "Engineering" tag on any line item                                                  |
| 5           | `tag_group_id` + `business_preset_id` | Any tag from "Department" on a line item with the "Senior Developer Salary" preset  |
| 6           | `tag_group_id` + `type` + `subtype`   | Any tag from "Department" on a line item of type earning, subtype salary            |
| 7           | `tag_group_id` + `type`               | Any tag from "Department" on any earning line item                                  |
| 8           | `tag_group_id`                        | Any tag from "Department" on any line item                                          |
| 9           | `business_preset_id`                  | A line item with the "Senior Developer Salary" business preset                      |
| 10          | `type` + `subtype`                    | A line item of type earning, subtype salary                                         |
| 11 (lowest) | `type`                                | Any earning line item                                                               |

A rule can be scoped to line items with a specific tag (`tag_id`) or any tag from a group (`tag_group_id`). When neither is set, the rule must specify a `type` (optionally with `subtype`) or a `business_preset_id`.

The expense and liability codes are resolved **independently**. A line item can take its expense code from one rule and its liability code from another, so a rule that supplies only one of the two is valid. Each side walks the precedence order on its own and uses the first rule that supplies that code.

See the [Accounting Code Rules](/api-reference/accounting-code-rules/list-accounting-code-rules) API reference for creating and managing rules.

### How rules are resolved

When a payroll is calculated, the system processes each line item's tag allocations:

1. For each allocation, find the tag that belongs to the primary tag group
2. Look up rules for that tag in precedence order:
   * If the line item has a business preset, try a preset-level rule first
   * Then try type + subtype
   * Then type only
   * Finally try a tag-only rule
3. Apply the matched rule's expense and liability accounting codes to the allocation

Rules are re-evaluated whenever payroll is recalculated, so changes to rules take effect on the next calculation of any draft payroll.

## Derived tags on statutory withholdings

Statutory withholding line items (CPP, EI, income tax, etc.) are system-calculated and cannot be directly tagged. Instead, they derive their tag assignments from the applicable remuneration on the pay stub.

This behaviour is controlled by the business entity's `accounting_settings.resolve_journal_entry_stat_withholdings` setting. When `true`, each withholding is split across the same tags as its source earnings. When `false`, each withholding stays a single unsplit row in journal entries.

When enabled, after payroll calculation the system examines which remuneration line items contribute to each withholding and builds a weighted percentage distribution based on their resolved dollar amounts, regardless of whether the original tag assignment used `percentage`, `hours`, or `amount` units. This ensures withholding amounts follow the same distribution as the remuneration that generated them.

### Example: statutory withholdings derived from pensionable earnings

**Earnings on the pay stub:**

| Line item      | Amount  | Engineering | Sales |
| -------------- | ------- | ----------- | ----- |
| Salary earning | \$3,000 | 100%        |       |
| Bonus earning  | \$2,000 |             | 100%  |

**Derived statutory withholdings:**

| Line item       | Amount | Engineering | Sales       |
| --------------- | ------ | ----------- | ----------- |
| CPP withholding | \$300  | 60% (\$180) | 40% (\$120) |
| EI withholding  | \$150  | 60% (\$90)  | 40% (\$60)  |

The withholdings inherit a 60/40 distribution derived from the weighted remuneration (\$3,000 Engineering / \$2,000 Sales).

## Journal entry tracking dimensions

Tag groups can be marked as **tracking dimensions** on journal entries. When enabled, each line item allocation produces a separate journal entry row, with matched accounting codes and the tag from each tracking dimension.

The [primary tag group](#primary-tag-group) is always a tracking dimension. Up to two additional tag groups can be marked as tracking dimensions by setting `is_journal_entry_dimension` to `true`.

Tagged payroll data produces structured journal entries with tracking detail per tag group.

### Example: journal entry rows with tracking dimensions

Given a salary earning of \$5,000 tagged 60% Engineering / 40% Sales:

**Without tracking dimensions:**

| Account           | Debit   | Credit  |
| ----------------- | ------- | ------- |
| Salary Expense    | \$5,000 |         |
| Payroll Liability |         | \$5,000 |

**With Department and Project as tracking dimensions:**

| Account           | Debit   | Credit  | Department  | Project       |
| ----------------- | ------- | ------- | ----------- | ------------- |
| Salary Expense    | \$3,000 |         | Engineering | Project Alpha |
| Salary Expense    | \$2,000 |         | Sales       |               |
| Payroll Liability |         | \$3,000 | Engineering | Project Alpha |
| Payroll Liability |         | \$2,000 | Sales       |               |

Each allocation produces its own debit and credit row, with the matched accounting codes and tracking dimension tags.

### Retrieving dimension tags

Each journal entry row carries up to three dimension tags, one per tracking dimension: `dimension_1_tag`, `dimension_2_tag`, and `dimension_3_tag`. A tag group keeps the same slot across every row in a payroll. Slots are not pinned to a fixed group, so read each tag's `tag_group` to tell which dimension it represents rather than relying on position.

The tags sit two levels down: rows are nested under the journal entry, and each tag is nested under a row. Both are returned as stubs by default, so resolving the tags inline takes a nested expand. Expand `journal_entry_rows.dimension_1_tag`, `journal_entry_rows.dimension_2_tag`, and `journal_entry_rows.dimension_3_tag` for the dimensions to resolve.

Each expanded row then includes the full tag for every dimension it carries:

```json theme={null}
{
  "id": "accjr_01...",
  "object": "journal_entry_row",
  "data": {
    "type": "debit",
    "amount": 2500,
    "code": "5110",
    "dimension_1_tag": {
      "id": "tag_01AURORA...",
      "object": "tag",
      "data": {
        "label": "Aurora",
        "tag_group": { "id": "tgrp_01...", "object": "tag_group" }
      }
    },
    "dimension_2_tag": {
      "id": "tag_01MTL...",
      "object": "tag",
      "data": {
        "label": "Montreal",
        "tag_group": { "id": "tgrp_01...", "object": "tag_group" }
      }
    },
    "dimension_3_tag": null
  }
}
```

A dimension with no tag on a row returns `null`. Without the expand, each dimension tag is returned as a stub with `id` and `links` only.

## End-to-end example

This example walks through the full tagging flow, from setup to journal entries.

* Quebec-based business entity with two employees: Marie (Montreal) and Luc (Quebec City)
* Both earn \$60,000 salary, paid semi-monthly (\$2,500 per period)
* Both work on Project Aurora
* Luc also receives an \$833.33 bonus for work on Project Beacon

### 1. Set up tags

1. Create a "Project" tag group with two tags: **Aurora** and **Beacon**
2. Create a "Location" tag group with two tags: **Montreal** and **Quebec City**
3. Set "Project" as the business entity's primary tag group (`accounting_settings.primary_tag_group`)
4. Mark both tag groups as journal entry tracking dimensions (`is_journal_entry_dimension: true`)

<details>
  <summary>API structure</summary>

  ```jsonc theme={null}
  // Project tag group
  {
    "id": "tgrp_01J...",
    "object": "tag_group",
    "data": {
      "label": "Project",
      "label_translations": { "en": "Project", "fr": "Projet" },
      "description": null,
      "is_component_locked": false,
      "is_journal_entry_dimension": true,
      "business_entity": { "id": "be_01...", "object": "business_entity" }
    }
  }

  // Aurora project tag
  {
    "id": "tag_01AURORA...",
    "object": "tag",
    "data": {
      "label": "Aurora",
      "label_translations": { "en": "Aurora", "fr": "Aurora" },
      "description": null,
      "tag_group": { "id": "tgrp_01J...", "object": "tag_group" },
      "business_entity": { "id": "be_01...", "object": "business_entity" }
    }
  }

  // Beacon project tag
  {
    "id": "tag_01BEACON...",
    "object": "tag",
    "data": {
      "label": "Beacon",
      "label_translations": { "en": "Beacon", "fr": "Beacon" },
      "description": null,
      "tag_group": { "id": "tgrp_01J...", "object": "tag_group" },
      "business_entity": { "id": "be_01...", "object": "business_entity" }
    }
  }

  // The "Location" tag group (tgrp_01L...) with tags "Montreal" and "Quebec City"
  // follows the same structure as above.
  ```
</details>

### 2. Create accounting code rules

Define rules for the primary tag group to map each project to accounting codes:

| Tag    | Type                  | Expense code                    | Liability code                  |
| ------ | --------------------- | ------------------------------- | ------------------------------- |
| Aurora | Earning               | 5110 (Aurora Salaries)          | 2100 (Aurora Payroll Liability) |
| Aurora | Statutory withholding | 5120 (Aurora Stat Withholdings) | 2110 (Aurora Stat Payable)      |
| Beacon | Earning               | 5210 (Beacon Salaries)          | 2200 (Beacon Payroll Liability) |
| Beacon | Statutory withholding | 5220 (Beacon Stat Withholdings) | 2210 (Beacon Stat Payable)      |

Each project has an earning rule and a stat withholding rule. No tag-only fallbacks are needed since all line items in this example are covered by type-level rules.

<details>
  <summary>API structure</summary>

  ```jsonc theme={null}
  // Aurora: earning rule
  {
    "id": "acr_01A...",
    "object": "accounting_code_rule",
    "data": {
      "business_entity": { "id": "be_01...", "object": "business_entity" },
      "tag": { "id": "tag_01AURORA...", "object": "tag" }, // Aurora project tag
      "type": "earning",
      "subtype": null,
      "business_preset": null,
      "expense_accounting_code": { "id": "ac_5110...", "object": "accounting_code" }, // Aurora Salaries
      "liability_accounting_code": { "id": "ac_2100...", "object": "accounting_code" } // Aurora Payroll Liability
    }
  }

  // Aurora: stat withholding rule
  {
    "id": "acr_01B...",
    "object": "accounting_code_rule",
    "data": {
      "business_entity": { "id": "be_01...", "object": "business_entity" },
      "tag": { "id": "tag_01AURORA...", "object": "tag" }, // Aurora project tag
      "type": "statutory_withholding",
      "subtype": null,
      "business_preset": null,
      "expense_accounting_code": { "id": "ac_5120...", "object": "accounting_code" }, // Aurora Stat Withholdings
      "liability_accounting_code": { "id": "ac_2110...", "object": "accounting_code" } // Aurora Stat Payable
    }
  }

  // Beacon: earning rule
  {
    "id": "acr_01C...",
    "object": "accounting_code_rule",
    "data": {
      "business_entity": { "id": "be_01...", "object": "business_entity" },
      "tag": { "id": "tag_01BEACON...", "object": "tag" }, // Beacon project tag
      "type": "earning",
      "subtype": null,
      "business_preset": null,
      "expense_accounting_code": { "id": "ac_5210...", "object": "accounting_code" }, // Beacon Salaries
      "liability_accounting_code": { "id": "ac_2200...", "object": "accounting_code" } // Beacon Payroll Liability
    }
  }

  // Beacon: stat withholding rule
  {
    "id": "acr_01D...",
    "object": "accounting_code_rule",
    "data": {
      "business_entity": { "id": "be_01...", "object": "business_entity" },
      "tag": { "id": "tag_01BEACON...", "object": "tag" }, // Beacon project tag
      "type": "statutory_withholding",
      "subtype": null,
      "business_preset": null,
      "expense_accounting_code": { "id": "ac_5220...", "object": "accounting_code" }, // Beacon Stat Withholdings
      "liability_accounting_code": { "id": "ac_2210...", "object": "accounting_code" } // Beacon Stat Payable
    }
  }
  ```
</details>

### 3. Assign tags to work assignments

Both employees' work assignments are tagged 100% Aurora, with their respective location:

**Marie:**

* 100% to `Project: Aurora`, `Location: Montreal`

**Luc:**

* 100% to `Project: Aurora`, `Location: Quebec City`

<details>
  <summary>API structure: work assignment with tag\_assignment</summary>

  ```jsonc theme={null}
  // Marie's work assignment with ?expand=tag_assignment
  {
    "id": "wa_01M...",
    "object": "work_assignment",
    "data": {
      // ...
      "tag_assignment": {
        "unit": "percentage",
        "tag_allocations": [
          {
            "tags": [
              {
                "id": "tag_01AURORA...",
                "object": "tag",
                "data": {
                  "label": "Aurora",
                  "tag_group": { "id": "tgrp_01J...", "object": "tag_group" },
                  "business_entity": {
                    "id": "be_01...",
                    "object": "business_entity",
                  },
                },
              },
              {
                "id": "tag_01MTL...",
                "object": "tag",
                "data": {
                  "label": "Montreal",
                  "tag_group": { "id": "tgrp_01L...", "object": "tag_group" },
                  "business_entity": {
                    "id": "be_01...",
                    "object": "business_entity",
                  },
                },
              },
            ],
            "value": 100,
            "expense_accounting_code": null,
            "liability_accounting_code": null,
          },
        ],
      },
    },
  }
  ```
</details>

### 4. Line items on the pay stub

In this example, the salary line items resolve their `managed_tag_assignment` from the work assignment. Since Luc's bonus is for work on Project Beacon, a `custom_tag_assignment` on that line item overrides the managed Aurora tag with Beacon. When expanded, the effective `tag_assignment` returns the `custom_tag_assignment` if one is set, otherwise the `managed_tag_assignment`.

**Marie's salary earning (\$2,500.00):**

* Managed tag assignment: 100% to `Project: Aurora`, `Location: Montreal`
* Custom tag assignment: none
* **Effective tag assignment:** 100% to `Project: Aurora`, `Location: Montreal`

**Luc's salary earning (\$2,500.00):**

* Managed tag assignment: 100% to `Project: Aurora`, `Location: Quebec City`
* Custom tag assignment: none
* **Effective tag assignment:** 100% to `Project: Aurora`, `Location: Quebec City`

**Luc's bonus earning (\$833.33):**

* Managed tag assignment: 100% to `Project: Aurora`, `Location: Quebec City`
* Custom tag assignment: 100% to `Project: Beacon`, `Location: Quebec City`
* **Effective tag assignment:** 100% to `Project: Beacon`, `Location: Quebec City`

### 5. Matched accounting codes

Each line item's tag allocations are matched against accounting code rules:

| Employee | Line item       | Primary tag | Matched rule          | Expense code                    | Liability code                  |
| -------- | --------------- | ----------- | --------------------- | ------------------------------- | ------------------------------- |
| Marie    | Salary earning  | Aurora      | Earning               | 5110 (Aurora Salaries)          | 2100 (Aurora Payroll Liability) |
| Luc      | Salary earning  | Aurora      | Earning               | 5110 (Aurora Salaries)          | 2100 (Aurora Payroll Liability) |
| Luc      | Bonus earning   | Beacon      | Earning               | 5210 (Beacon Salaries)          | 2200 (Beacon Payroll Liability) |
| Marie    | QPP withholding | Aurora      | Statutory withholding | 5120 (Aurora Stat Withholdings) | 2110 (Aurora Stat Payable)      |
| Luc      | QPP withholding | Aurora      | Statutory withholding | 5120 (Aurora Stat Withholdings) | 2110 (Aurora Stat Payable)      |
| Luc      | QPP withholding | Beacon      | Statutory withholding | 5220 (Beacon Stat Withholdings) | 2210 (Beacon Stat Payable)      |

Luc's QPP withholding has two allocations (75% Aurora / 25% Beacon), each resolved independently against its matching rule.

<details>
  <summary>API structure: Luc's bonus earning with resolved tag allocations and accounting codes</summary>

  ```jsonc theme={null}
  // Luc's bonus earning with ?expand=tag_assignment,managed_tag_assignment,custom_tag_assignment
  {
    "id": "eli_01B...",
    "object": "earning_line_item",
    "data": {
      "amount": 833.33,
      // ...
      "managed_tag_assignment": {
        // Inherited from work assignment (Aurora, Quebec City)
        "unit": "percentage",
        "tag_allocations": [
          {
            "tags": [
              {
                "id": "tag_01AURORA...",
                "object": "tag",
                "data": { "label": "Aurora" },
              },
              {
                "id": "tag_01QC...",
                "object": "tag",
                "data": { "label": "Quebec City" },
              },
            ],
            "value": 100,
            "expense_accounting_code": {
              "id": "ac_5110...",
              "object": "accounting_code",
            }, // Aurora Salaries
            "liability_accounting_code": {
              "id": "ac_2100...",
              "object": "accounting_code",
            }, // Aurora Payroll Liability
          },
        ],
      },
      "custom_tag_assignment": {
        // User override: Beacon, Quebec City
        "unit": "percentage",
        "tag_allocations": [
          {
            "tags": [
              {
                "id": "tag_01BEACON...",
                "object": "tag",
                "data": { "label": "Beacon" },
              },
              {
                "id": "tag_01QC...",
                "object": "tag",
                "data": { "label": "Quebec City" },
              },
            ],
            "value": 100,
            "expense_accounting_code": {
              "id": "ac_5210...",
              "object": "accounting_code",
            }, // Beacon Salaries
            "liability_accounting_code": {
              "id": "ac_2200...",
              "object": "accounting_code",
            }, // Beacon Payroll Liability
          },
        ],
      },
      "tag_assignment": {
        // Effective: custom takes precedence over managed
        "unit": "percentage",
        "tag_allocations": [
          {
            "tags": [
              {
                "id": "tag_01BEACON...",
                "object": "tag",
                "data": { "label": "Beacon" },
              },
              {
                "id": "tag_01QC...",
                "object": "tag",
                "data": { "label": "Quebec City" },
              },
            ],
            "value": 100,
            "expense_accounting_code": {
              "id": "ac_5210...",
              "object": "accounting_code",
            }, // Beacon Salaries
            "liability_accounting_code": {
              "id": "ac_2200...",
              "object": "accounting_code",
            }, // Beacon Payroll Liability
          },
        ],
      },
    },
  }
  ```
</details>

### 6. Derived statutory withholdings

Statutory withholdings derive their tag allocations from the remuneration that generates them.

**Marie**: all remuneration is tagged `Project: Aurora`, `Location: Montreal`, so each withholding carries the same single allocation:

* Federal tax (\$184.47)
  * 100% (\$184.47) to `Project: Aurora`, `Location: Montreal`
* Provincial tax (\$235.29)
  * 100% (\$235.29) to `Project: Aurora`, `Location: Montreal`
* QPP (\$148.31)
  * 100% (\$148.31) to `Project: Aurora`, `Location: Montreal`
* QPIP (\$10.75)
  * 100% (\$10.75) to `Project: Aurora`, `Location: Montreal`
* EI (\$32.50)
  * 100% (\$32.50) to `Project: Aurora`, `Location: Montreal`

**Luc**: remuneration is \$2,500 (`Project: Aurora`) + \$833.33 (`Project: Beacon`), a 75/25 split. Both carry `Location: Quebec City`. Each withholding derives two allocations at that ratio:

* Federal tax (\$325.75)
  * 75% (\$244.31) to `Project: Aurora`, `Location: Quebec City`
  * 25% (\$81.44) to `Project: Beacon`, `Location: Quebec City`
* Provincial tax (\$350.77)
  * 75% (\$263.08) to `Project: Aurora`, `Location: Quebec City`
  * 25% (\$87.69) to `Project: Beacon`, `Location: Quebec City`
* QPP (\$200.81)
  * 75% (\$150.61) to `Project: Aurora`, `Location: Quebec City`
  * 25% (\$50.20) to `Project: Beacon`, `Location: Quebec City`
* QPIP (\$14.33)
  * 75% (\$10.75) to `Project: Aurora`, `Location: Quebec City`
  * 25% (\$3.58) to `Project: Beacon`, `Location: Quebec City`
* EI (\$43.33)
  * 75% (\$32.50) to `Project: Aurora`, `Location: Quebec City`
  * 25% (\$10.83) to `Project: Beacon`, `Location: Quebec City`

### 7. Journal entry rows

With Project and Location as journal entry tracking dimensions, each allocation produces its own row. Employer contribution rows are omitted for brevity.

| Account             | Location    | Project | Debit      | Credit     |
| ------------------- | ----------- | ------- | ---------- | ---------- |
| Aurora Salaries     | Montreal    | Aurora  | \$2,500.00 |            |
| Aurora Salaries     | Quebec City | Aurora  | \$2,500.00 |            |
| Beacon Salaries     | Quebec City | Beacon  | \$833.33   |            |
| Aurora Stat Payable | Montreal    | Aurora  |            | \$611.32   |
| Aurora Stat Payable | Quebec City | Aurora  |            | \$701.25   |
| Beacon Stat Payable | Quebec City | Beacon  |            | \$233.74   |
| Net Payroll Payable |             |         |            | \$4,287.02 |
