# Intercompany reconciliation with the Accounting API

Group consolidation starts with pairing each entity's intercompany receivable against the counterparty's payable, before any elimination entries land in a consolidation worksheet. This guide drives that workflow from the [Accounting API](/apis/accounting/reference): discover the entities, read the intercompany ledger accounts and period-end balances, pull the journal entries that hit those accounts, and propose balancing adjustments for a controller to review.

## One integration for every ledger

Some ledgers model a corporate group natively (NetSuite OneWorld, Sage Intacct entities). Most do not, and the group is represented as one company per legal entity. A consolidation or close-management product has to handle both shapes without rewriting its reconciliation engine per vendor.

- Entity discovery uses [`GET /accounting/subsidiaries`](/apis/accounting/reference#operation/subsidiariesAll) where the connector exposes subsidiaries, [`GET /accounting/companies`](/apis/accounting/reference#operation/companiesAll) where one connection reaches several companies, and [`GET /accounting/company-info`](/apis/accounting/reference#operation/companyInfoOne) on each connection otherwise.
- `LedgerAccount` and `JournalEntry` have the same shape on every supported ledger.
- Period-end balances come from the ledger's own reports, so cut-off matches what the close team signs off on.
- Proposed journals use the same payload whether the counterparty is a NetSuite subsidiary, a Sage Intacct entity, or a tracking category.

## Resource mapping

Every connector exposes the same unified resources. The downstream objects they map to differ depending on whether the ledger has a native multi-entity model.

| Resource | Native multi-entity (NetSuite, Sage Intacct) | One ledger per company (QuickBooks, Xero, Exact, others) |
| --- | --- | --- |
| `Subsidiary` | OneWorld `Subsidiary`, Intacct `Entity` | Not exposed. Use `CompanyInfo` from each connection instead. |
| `LedgerAccount` | GL account, filterable by `subsidiary_id` on connectors that support it | GL account on the single-entity ledger |
| `JournalEntry` | Journal with a header `subsidiary` reference; counterparty on the line's tracking categories, department, or location | Journal with the counterparty in a tracking category, class, or memo convention |
| `BalanceSheet`, `TrialBalance` | Report for the connection. The report filters have no subsidiary parameter. | Report for the full ledger |

## Walkthrough

The example below reconciles the US parent against the UK subsidiary at month end. Replace the IDs with values returned by your discovery call.

### 1. Discover the entities in the group

If the connection is NetSuite or Sage Intacct, list subsidiaries directly with [`GET /accounting/subsidiaries`](/apis/accounting/reference#operation/subsidiariesAll).

```http
GET /accounting/subsidiaries HTTP/1.1
Host: unify.apideck.com
Authorization: Bearer {API_KEY}
x-apideck-app-id: {APP_ID}
x-apideck-consumer-id: consumer_group_acme
x-apideck-service-id: netsuite
```

Response (abbreviated):

```json
{
  "data": [
    {
      "id": "sub_01H8X9Y2A3B4C5D6E7F8G9H0J1",
      "parent_id": null,
      "name": "Acme Holdings Inc.",
      "status": "active"
    },
    {
      "id": "sub_01H8X9Y2A3B4C5D6E7F8G9H0J2",
      "parent_id": "sub_01H8X9Y2A3B4C5D6E7F8G9H0J1",
      "name": "Acme UK Ltd.",
      "status": "active"
    }
  ]
}
```

Subsidiary records have no base-currency field. Some connectors return a `currencies` list, and the controller can confirm each entity's functional currency during setup.

Native subsidiary coverage is narrow: only NetSuite and Sage Intacct REST support write, and a short list of other connectors expose read access (see the connector table below). Some connectors reach several companies from one connection. On those, [`GET /accounting/companies`](/apis/accounting/reference#operation/companiesAll) lists them, and every later call is scoped by sending the returned `id` in the `x-apideck-company-id` header. For single-company connectors, hold one Vault consumer per legal entity and call [`GET /accounting/company-info`](/apis/accounting/reference#operation/companyInfoOne) on each to read the entity's name, base `currency`, and `fiscal_year_start_month`.

### 2. Identify the intercompany ledger accounts per entity

Reconciliation needs the GL account IDs for intercompany receivable and intercompany payable on each entity. Fetch the chart of accounts with [`GET /accounting/ledger-accounts`](/apis/accounting/reference#operation/ledgerAccountsAll). The filters cover `name`, `classification`, `status`, `updated_since`, and `subsidiary_id`. There is no intercompany flag, so match the accounts the controller has nominated on your side.

```json
{
  "data": [
    {
      "id": "acct_01H8X9Y2A3B4C5D6E7F8G9H0K1",
      "nominal_code": "1310",
      "name": "Intercompany Receivable - UK",
      "classification": "asset",
      "type": "accounts_receivable",
      "currency": "USD"
    },
    {
      "id": "acct_01H8X9Y2A3B4C5D6E7F8G9H0K2",
      "nominal_code": "2310",
      "name": "Intercompany Payable - US",
      "classification": "liability",
      "type": "accounts_payable",
      "currency": "GBP"
    }
  ]
}
```

Store the mapping once per close cycle.

### 3. Fix the fiscal period window

Pulling balances at an arbitrary date risks catching mid-period adjustments. Use the ledger's own period boundaries. Where the connector supports it, [`GET /accounting/fiscal-periods`](/apis/accounting/reference#operation/fiscalPeriodsAll) returns each period's `start_date`, `end_date`, and `status` (`open`, `locked`, `closed`, `future`). Otherwise derive the window from `fiscal_year_start_month` in company info, or let the controller nominate the close period.

```
Period: Jul 2025
Start:  2025-07-01
End:    2025-07-31
```

### 4. Pull period-end balances per entity

For per-account balances, [`GET /accounting/trial-balance`](/apis/accounting/reference#operation/trialBalanceOne) returns one line per ledger account with `account_id`, `debit`, `credit`, and a signed `balance`, as of `filter[end_date]`. [`GET /accounting/balance-sheet`](/apis/accounting/reference#operation/balanceSheetOne) returns the same position grouped into assets, liabilities, and equity. Pull either at the close date and read the ending balances of the accounts from step 2.

```http
GET /accounting/trial-balance?filter[end_date]=2025-07-31 HTTP/1.1
Host: unify.apideck.com
Authorization: Bearer {API_KEY}
x-apideck-app-id: {APP_ID}
x-apideck-consumer-id: consumer_acme_uk
x-apideck-service-id: xero
```

Scope each call to one entity. On multi-company connectors, send `x-apideck-company-id`. On single-company ledgers, run the call against each entity's Vault consumer and label the result with its company info. The trial balance and balance sheet filters have no subsidiary parameter, so on NetSuite OneWorld the report covers the whole connection. For a per-subsidiary figure there, sum the journal lines from step 5 for the intercompany accounts, filtered with `filter[subsidiary_id]`.

Balance sheet coverage spans the main targets (NetSuite, QuickBooks, Xero, Sage Intacct, Exact Online, FreshBooks, Zoho Books, Wave, Rillet, Campfire, Digits, FreeAgent, Sage Business Cloud Accounting). Where a connector exposes neither report, rebuild ending balances by replaying journal lines for the period against the intercompany accounts.

### 5. Pull journal entries that hit the intercompany accounts

Report totals give the net position. To explain a mismatch, pull the underlying journal entries for the period with [`GET /accounting/journal-entries`](/apis/accounting/reference#operation/journalEntriesAll). `filter[start_date]` and `filter[end_date]` limit by posting date (connectors without date-range support reject them with `UnsupportedFiltersError`, so fall back to `filter[updated_since]`), and `filter[subsidiary_id]` scopes to one entity on NetSuite OneWorld. There is no ledger account filter, so keep only entries whose `line_items[].ledger_account.id` matches an intercompany account.

```http
GET /accounting/journal-entries?filter[start_date]=2025-07-01&filter[end_date]=2025-07-31&filter[subsidiary_id]=sub_01H8X9Y2A3B4C5D6E7F8G9H0J1 HTTP/1.1
Host: unify.apideck.com
Authorization: Bearer {API_KEY}
x-apideck-app-id: {APP_ID}
x-apideck-consumer-id: consumer_group_acme
x-apideck-service-id: netsuite
```

```json
{
  "data": [
    {
      "id": "je_01H8X9Y2A3B4C5D6E7F8G9H0M1",
      "number": "JE-2025-0712",
      "journal_symbol": "GJ",
      "posted_at": "2025-07-28T00:00:00Z",
      "currency": "USD",
      "memo": "Shared services recharge July 2025",
      "subsidiary": {
        "id": "sub_01H8X9Y2A3B4C5D6E7F8G9H0J1",
        "name": "Acme Holdings Inc."
      },
      "line_items": [
        {
          "ledger_account": { "id": "acct_01H8X9Y2A3B4C5D6E7F8G9H0K1" },
          "description": "Intercompany receivable from Acme UK",
          "type": "debit",
          "total_amount": 42000.0,
          "tracking_categories": [
            { "id": "tc_counterparty_uk", "name": "Counterparty: Acme UK Ltd." }
          ]
        },
        {
          "ledger_account": { "id": "acct_01H8X9Y2A3B4C5D6E7F8G9H0K9" },
          "description": "Shared services revenue",
          "type": "credit",
          "total_amount": 42000.0
        }
      ]
    }
  ]
}
```

The entry's own entity is the header `subsidiary`. Journal lines have no subsidiary field. The counterparty sits on the line's `tracking_categories`, `department_id`, or `location_id`, or in the memo convention your customer uses. See [Tracking categories list](/apis/accounting/reference#operation/trackingCategoriesAll) for the available dimensions.

### 6. Pair receivable with payable and convert

For each ordered pair of entities, compare the receiver's intercompany receivable against the payer's intercompany payable. Convert both to the group reporting currency at one rate per close cycle. A worked example:

| Pair | Source balance | FX rate | Group currency (USD) |
| --- | --- | --- | --- |
| US receivable from UK | 184,250.00 USD | 1.0000 | 184,250.00 |
| UK payable to US | 141,650.00 GBP | 1.3000 | 184,145.00 |
| Difference | | | 105.00 |

Then classify the difference. A timing difference means a journal exists on one side but the other side has not posted it in the period yet; the counterparty journal from step 5 will be dated after the period end on the lagging side. An FX difference means both sides agree in transaction currency but diverge after translation, usually because one entity has not revalued a foreign-currency balance at the period-end rate. A missing posting means the amount appears on one side only, with no matching entry on the other after the lookback window.

### 7. Propose balancing journal entries for review

A reconciliation agent should propose adjustments and leave posting to a person. Build the draft payload, store it for a controller to approve, and send it to [`POST /accounting/journal-entries`](/apis/accounting/reference#operation/journalEntriesAdd) once approved. Send it with an [`Idempotency-Key` header](/guides/idempotency-keys) derived from the proposal ID, so a retry after a timeout posts the adjustment at most once. In this example the UK entity revalues its USD-denominated payable, which raises the payable by 80.77 GBP (105.00 USD at 1.30).

```json
{
  "memo": "IC reconciliation adjustment - Jul 2025 FX revaluation",
  "posted_at": "2025-07-31T00:00:00Z",
  "currency": "GBP",
  "journal_symbol": "ICR",
  "subsidiary": { "id": "sub_01H8X9Y2A3B4C5D6E7F8G9H0J2" },
  "line_items": [
    {
      "ledger_account": { "id": "acct_01H8X9Y2A3B4C5D6E7F8G9H0K7" },
      "description": "Unrealized FX loss on intercompany payable",
      "type": "debit",
      "total_amount": 80.77
    },
    {
      "ledger_account": { "id": "acct_01H8X9Y2A3B4C5D6E7F8G9H0K2" },
      "description": "FX revaluation of intercompany payable to US",
      "type": "credit",
      "total_amount": 80.77,
      "tracking_categories": [
        { "id": "tc_counterparty_us", "name": "Counterparty: Acme Holdings Inc." }
      ]
    }
  ]
}
```

Post it against the UK entity. On NetSuite, `subsidiary.id` selects the UK subsidiary within the shared connection. On a multi-company connector, drop `subsidiary` and send the UK company's ID in `x-apideck-company-id`. On a single-company ledger, send it with the UK entity's own `x-apideck-consumer-id`.

>
> Stage every proposed journal in your own database with a status field. Posting
> directly from the agent without a human in the loop is the fastest way to
> break trust with a close team.

## Connector-specific behavior

The notes column reflects whether the connector exposes a native subsidiary resource, whether journal entries can be posted (required for proposing adjustments), and whether the connector fits a reconciliation flow at all. Verify any specific operation against the live coverage matrix before building against it.

| Connector | Notes |
| --- | --- |
| `access-financials` | Journal entries are not in coverage. Use for discovery (customers, suppliers, invoices) only; export adjustments for manual posting. |
| `acumatica` | One ledger per company. Journal entries writable. Subsidiaries resource not in coverage. Balance sheet not in coverage; derive period balances from journal entries. |
| `api-explorer` | Sandbox connector. Use for shape testing only. |
| `banqup` | Journal entries, ledger accounts, and bills are not in coverage. Not suited for intercompany reconciliation. |
| `campfire` | Subsidiaries readable (read-only). Journal entries writable. Balance sheet readable. Fits as a reconciliation target. |
| `clearbooks-uk` | Single-entity ledger. Journal entries are not in coverage; export adjustments for manual posting. |
| `coverage` | Internal connector. Not used in production reconciliation flows. |
| `digits` | Journal entries are read-only and balance sheet is readable. Use for discovery and analysis, not for posting adjustments. |
| `dualentry` | Subsidiaries readable (read-only). Journal entries writable. Confirm the header `subsidiary` is populated on journals before posting. |
| `exact-online` | One ledger per administration. Journal entries writable. Hold one Vault consumer per administration. Subsidiaries resource not in coverage. |
| `exact-online-nl` | Same shape as `exact-online`. One ledger per administration. Journal entries writable. Subsidiaries resource not in coverage. |
| `exact-online-uk` | One ledger per administration. Journal entries writable. Bills are read-only here, so AP-side adjustments route through journals rather than bill writes. |
| `freeagent` | One ledger per company. Journal entries writable. Subsidiaries resource not in coverage. Ledger accounts are read-only; source the intercompany account mapping from the customer. |
| `freshbooks` | Journal entries are read-only. This connector cannot accept proposed adjustments. |
| `intuit-enterprise-suite` | Journal entries, departments, locations, and tracking categories are all writable, which gives the most dimensions for counterparty tagging in the QuickBooks family. Subsidiaries resource is not in coverage; treat each legal entity as its own company. |
| `kashflow` | Single-entity ledger. Journal entries are read-only. |
| `microsoft-dynamics-365-business-central` | Companies are first-class in Business Central. Scope each call to one company. Journal entries writable. Subsidiaries resource not in coverage. Balance sheet not in coverage; derive period balances from journal entries. |
| `moneybird` | Subsidiaries readable (read-only). Journal entries writable. Tracking categories writable for counterparty tagging. |
| `mrisoftware` | Subsidiaries readable (read-only). Journal entries writable. Customers and ledger accounts are read-only here, so counterparty master data has to be sourced from the ledger. |
| `myob` | One ledger per company. Journal entries writable. Subsidiaries resource not in coverage. |
| `myob-acumatica` | One ledger per company. Journal entries writable. Subsidiaries resource not in coverage. Balance sheet not in coverage. |
| `netsuite` | OneWorld subsidiaries are first-class and writable, including nested parent/child relationships. Journal entries carry a header `subsidiary` reference, and list filters accept `subsidiary_id`. |
| `odoo` | Subsidiaries readable (read-only). Journal entries writable. Verify the header `subsidiary` or `company_id` on each journal matches the source company. |
| `overview` | Internal connector. Not used in production reconciliation flows. |
| `pennylane` | One ledger per company. Journal entries writable. Attachment coverage is limited, so link supporting evidence in your own store. Subsidiaries resource not in coverage. |
| `procountor-fi` | One ledger per company. Journal entries writable. Subsidiaries resource and balance sheet not in coverage. |
| `quickbooks` | Single-entity QuickBooks Online. Model the group as one Vault consumer per company and tag counterparties with classes or a tracking category. For multi-entity QuickBooks, use `intuit-enterprise-suite`. Subsidiaries resource not in coverage. |
| `rillet` | Subsidiaries readable (read-only). Journal entries writable. Balance sheet and profit-and-loss are available for close-cycle snapshots. |
| `sage-business-cloud-accounting` | Subsidiaries readable (read-only). Journal entries are read-only. Use for discovery and balance pulls; export adjustments for manual posting. |
| `sage-intacct` | Entities exposed as subsidiaries (read-only). Journal entries writable. Dimension model (department, location, tracking category) available for counterparty tagging. |
| `sage-intacct-rest` | REST-based Intacct connector with subsidiary read and write. Journal entries, departments, locations, and tracking categories all writable. Preferred Intacct target for a reconciliation agent. |
| `stripe` | Not a general ledger. Journal entries not in coverage. Treat as a feeder system only. |
| `visma-netvisor` | One ledger per company. Journal entries are read-only, so adjustments must be exported. |
| `wave` | Journal entries are not in coverage. Use for discovery only. |
| `workday` | Subsidiaries readable (read-only). Journal entries writable. Fits groups where Workday Financials is the group ledger. Balance sheet not in coverage. |
| `xero` | Single-entity ledger. Hold one Vault consumer per organisation. Journal entries writable. Tracking categories are read-only, so reference existing counterparty tracking options rather than creating new ones from the agent. |
| `yuki` | One ledger per administration. Journal entries writable. Tracking categories writable, which can carry the counterparty tag. Bills are read-only here. |
| `zoho-books` | One ledger per organization. Journal entries writable. For multi-org groups, scope each call to one Books organization. Subsidiaries and tracking categories resources not in coverage. |

### NetSuite OneWorld

NetSuite is the clearest native multi-entity target. The header `subsidiary.id` on a journal entry scopes the entry to that entity, and `filter[subsidiary_id]` on journal entries, ledger accounts, invoices, bills, and payments scopes reads the same way. The unified journal line has no subsidiary field, so NetSuite's advanced intercompany journals (one journal whose lines belong to different subsidiaries) are out of reach through the unified model. Post one single-subsidiary journal per side instead, each with its own `subsidiary.id`, and tag the counterparty on the lines.

### Sage Intacct (SOAP and REST)

Both Intacct connectors expose entities as subsidiaries for read. The REST connector also supports writing to the subsidiary resource and has writable department, location, and tracking category dimensions, which is where Intacct carries per-line counterparty tagging. Prefer `sage-intacct-rest` when the customer has it enabled.

### Single-ledger ecosystems (QuickBooks, Xero, Exact)

For QuickBooks Online, Xero, Exact Online, and similar single-entity ledgers, treat each legal entity as its own company: either its own Vault consumer, or an `x-apideck-company-id` value where one connection reaches several companies. Pull company info once per entity to resolve its name and base currency, then run the walkthrough against each entity in turn. Counterparty tagging lives in a tracking category (Xero, read-only resource), a class (QuickBooks), or a cost center (Exact), depending on the customer's convention.

## Next steps

- [Managing Locations, Departments, and Subsidiaries](/guides/locations-subsidiaries-departments) for a deeper look at dimensional tagging.
- [Mark Invoices and Bills as Paid](/guides/mark-invoices-as-paid) for the payment-allocation pattern used in intercompany settlement flows.
- [Accounting API reference](/apis/accounting/reference) for the full operation list.
