# Exact Online: Balance Sheet & Profit and Loss configuration

This guide explains the **Balance-Sheet** and **Profit-and-Loss** configuration on an Exact
Online connection: why it exists, how it behaves out of the box, when you need to touch it, and
**how to look up the exact values to enter** from your own Exact administration.

> **⚠️ Check which region your administration is on.** The built-in defaults are the Dutch **RGS**
> headings. They fit a Dutch (`nl`) administration out of the box; on any other region this connector
> supports (`be`, `de`, `es`, `co.uk`, `com`) the first-level headings differ, so you will normally
> need to configure them. Use §4 to read your own headings, then enter them as described in §3.

---

## 1. The problem it solves

A Balance Sheet and a Profit & Loss are just your General Ledger accounts **grouped into sections**
(Assets / Liabilities / Equity, and Income / Cost of Goods Sold / Expenses / Other income / Other
expenses).

In Exact Online, that grouping comes from a **classification scheme**: every GL account is mapped to a
classification, and each classification rolls up to a first-level heading such as *Vaste activa*,
*Eigen vermogen*, *Kortlopende schulden*, *Financiële baten en lasten*, *Belastingen*, etc. **The
first-level heading is what decides the section.**

The catch: schemes differ per administration. Most Dutch administrations use the standard RGS scheme,
whose first-level headings are well known, so the integration ships with **built-in defaults** that
need **no configuration** for them. Administrations on another region, or on a custom scheme, use
**different first-level names**. When that happens the integration can't recognise the headings, and
accounts can land in the wrong section (or fall back to a less reliable heuristic).

**This configuration is the escape hatch:** you tell the integration the first-level classification
titles *your* administration uses, and it classifies accordingly.

> You only need this if your administration is **not** using the Dutch RGS headings the defaults assume,
> which includes most administrations outside the Netherlands. If your reports already look correct,
> leave every field empty.

---

## 2. How it works with the defaults

For each account, the integration finds its **top-level classification heading** and matches that
heading (case- and accent-insensitive; a heading matches if it *contains* one of the titles) against
these built-in **Dutch RGS** defaults (they apply on every region this connector serves, because the
classification logic is shared with the Exact Online NL connector):

**Balance Sheet**
| Section | Default first-level titles |
|---|---|
| Assets | activa *(broad catch)*, vaste activa, vlottende activa, voorraden, liquide middelen, effecten, onderhanden projecten, vorderingen, debiteuren |
| Liabilities | schulden *(broad catch)*, langlopende schulden, kortlopende schulden, verplichtingen, voorzieningen, achtergestelde leningen, leningen, crediteuren |
| Equity | eigen vermogen, ondernemingsvermogen, groepsvermogen, gestort kapitaal, kapitaal, reserves |

**Profit & Loss**
| Section | Default first-level titles |
|---|---|
| Income | netto-omzet, omzet, opbrengsten, overige opbrengsten |
| Cost of Goods Sold | kostprijs van de omzet, inkoopwaarde van de omzet, kostprijs |
| Expenses | som der bedrijfskosten, bedrijfskosten, personeelskosten, afschrijvingen, lonen en salarissen, overige bedrijfskosten |
| Other income | financiële baten, buitengewone baten, aandeel in het resultaat, *(combined "…baten en lasten" groups)* |
| Other expenses | belastingen, vennootschapsbelasting, financiële lasten, buitengewone lasten, *(combined "…baten en lasten" groups)* |

> The tables above show the **main** defaults, not the full list: the built-in sets are longer, and
> matching is *substring*-based, so a heading like *Overige bedrijfskosten* already matches
> `bedrijfskosten` and *Rentebaten en soortgelijke opbrengsten* already matches `rentebaten`. Check
> your report output before concluding a heading is unrecognised.

Two important behaviours:

- **Combined result groups (financial / exceptional / participations).** A single first-level heading can
  hold both income (interest received) and costs (interest paid). When that heading appears in **both**
  the Other income and Other expenses lists, each account under it is routed by the **sign of its
  balance**: a credit balance → Other income, a debit balance → Other expenses, so interest income and
  interest expense split correctly instead of both landing in operating expenses. The built-in defaults
  already do this for the Dutch combined headings (*financiele baten en lasten*, *buitengewone baten en
  lasten*); for your own scheme you get the same behaviour by entering the heading under both fields.
  See the tip in §3.
- **Operating detail (Income vs COGS vs Expenses).** Some schemes don't separate revenue, cost of
  sales and operating costs at the first level (they sit under a single *EBITDA*-style subtotal). When
  a heading isn't recognised as non-operating, the split falls back to the Dutch RGS **account-number
  range** (8xxxx = revenue, 7xxxx = cost of sales, 4xxxx/6xxxx = operating costs), a Dutch numbering
  convention, so it will not help an administration that numbers its accounts differently. After that
  the account's Exact **Type** is used, which is scheme- and region-independent. Nothing is ever
  dropped; anything still unresolved goes to *Uncategorized*.

---

## 3. When and how to configure

Configure if the defaults don't match your scheme (sections look wrong or empty), which is the normal
case outside the Netherlands. Enter the
first-level titles **as they appear in your Exact administration**, separated by commas. Each field is
independent: override only the ones that differ; the rest keep using the defaults.

**Balance-Sheet configuration**
- *Assets Titles*: your first-level Asset headings.
- *Liabilities Titles*: your first-level Liability headings.
- *Equity Titles*: your first-level Equity headings.

**Profit-and-Loss configuration**
- *Income Titles*: operating revenue headings.
- *Cost of Goods Sold Titles*: cost-of-sales headings.
- *Expenses Titles*: operating-expense headings.
- *Other Income Titles*: non-operating income (financial / exceptional / participations).
- *Other Expenses Titles*: non-operating expense headings, including taxes.

> **Tip for combined groups:** if a first-level heading holds both income and costs (e.g. *Financiële
> baten en lasten*, *Buitengewone baten en lasten*), enter it under **both** *Other Income* and *Other
> Expenses*: the integration then splits each account by its balance sign. If a heading is one
> direction only (e.g. *Belastingen*), enter it under a single field.

---

## 4. How to look up your first-level titles (via the Apideck Proxy)

Your first-level classification headings live in Exact's `GLClassifications` table. You can read them
through the **Apideck Proxy** (any HTTP client works). You need three request headers besides auth:
`x-apideck-consumer-id`, `x-apideck-app-id`, and `x-apideck-service-id: exact-online`, plus an
`x-apideck-downstream-url` pointing at the Exact endpoint. Always send `Accept: application/json`
(otherwise Exact replies in XML).

The Exact URL contains your **division** id, the same value stored on the connection as
`systemDivisionId` (visible in the connection settings / `GET /vault/connections/accounting/exact-online`).
It also contains `{tld}`: replace it with your administration's region top-level domain (for example
`nl`, `be`, `de`, `es`, `co.uk`, `com`), matching the domain of your Exact Online login.

### Step 1: find your main GL scheme id

```bash
curl 'https://unify.apideck.com/proxy' \
  -H 'Authorization: Bearer <APIDECK_API_KEY>' \
  -H 'x-apideck-app-id: <APIDECK_APP_ID>' \
  -H 'x-apideck-consumer-id: <CONSUMER_ID>' \
  -H 'x-apideck-service-id: exact-online' \
  -H 'Accept: application/json' \
  -H "x-apideck-downstream-url: https://start.exactonline.{tld}/api/v1/<DIVISION>/financial/GLSchemes"
```

In the response, take the scheme where `"Main": 1` and copy its `ID` (a GUID).

### Step 2: list the classifications for that scheme

```bash
curl 'https://unify.apideck.com/proxy' \
  -H 'Authorization: Bearer <APIDECK_API_KEY>' \
  -H 'x-apideck-app-id: <APIDECK_APP_ID>' \
  -H 'x-apideck-consumer-id: <CONSUMER_ID>' \
  -H 'x-apideck-service-id: exact-online' \
  -H 'Accept: application/json' \
  -H "x-apideck-downstream-url: https://start.exactonline.{tld}/api/v1/<DIVISION>/financial/GLClassifications?\$filter=TaxonomyNamespace eq guid'<SCHEME_ID>'&\$select=ID,Code,Description,Parent,PeriodType"
```

### Step 3: pick the first-level headings

In the response (`d.results`):

1. Find the two **root** rows: the ones with `Parent: null`. One has `PeriodType: "instant"` and a
   description like *Balance Sheet*; the other has `PeriodType: "duration"` and a description like
   *Profit and loss*. Note their `ID`s.
2. The **first-level headings** are the rows whose `Parent` equals a root `ID`. Their `Description`
   values are exactly what you paste into the configuration:
   - Rows under the **instant** (Balance Sheet) root → sort into *Assets / Liabilities / Equity Titles*
     by meaning (activa → Assets, eigen vermogen → Equity, schulden / leningen / voorzieningen →
     Liabilities).
   - Rows under the **duration** (Profit & Loss) root → sort into the P&L fields (financial / exceptional
     / participations → Other Income **and** Other Expenses; belastingen → Other Expenses; operating
     headings → Income / COGS / Expenses if your scheme separates them).

> Only the **first-level** (direct children of the root) headings matter: the integration walks every
> account up to its first-level heading automatically. You don't need the deeper sub-levels.

### Worked example (a Dutch RGS scheme)

For a scheme whose Balance Sheet root has children *VASTE ACTIVA*, *VLOTTENDE ACTIVA*, *EIGEN
VERMOGEN*, *LANGLOPENDE SCHULDEN*, *KORTLOPENDE SCHULDEN*, *VOORZIENINGEN*, and P&L root children
*EBITDA*, *FINANCIELE OPBRENGSTEN EN KOSTEN*, *BELASTINGEN*, *BUITENGEWONE OPBRENGSTEN EN KOSTEN*:

- Assets Titles: `Vaste activa, Vlottende activa`
- Liabilities Titles: `Langlopende schulden, Kortlopende schulden, Voorzieningen`
- Equity Titles: `Eigen vermogen`
- Other Income Titles: `Financiele opbrengsten en kosten, Buitengewone opbrengsten en kosten`
- Other Expenses Titles: `Belastingen, Financiele opbrengsten en kosten, Buitengewone opbrengsten en kosten`
- (Income / COGS / Expenses left empty: this scheme groups operating under *EBITDA*, so the
  account-number range handles the split automatically.)

These headings match the built-in defaults precisely because they *are* the Dutch RGS defaults. The
example exists only to show how first-level headings map onto the fields. An administration on another
region will have different headings, so read yours with the three steps above rather than copying this
list.

---

## 5. What if I misconfigure or leave it blank?

- **Blank:** the Dutch RGS defaults are used. Correct for standard Dutch administrations; on another
  region they generally will not match, so classification falls through to the account-number range and
  then to Exact's account Type, giving coarser sections than configuring the titles would.
- **A heading not recognised (blank or wrong):** those accounts fall back to the RGS **classification
  code** on the Balance Sheet (`01.01`–`01.06` → Assets, `01.07`–`01.09` → Equity, `01.10`–`01.12` →
  Liabilities) or to the RGS **account-number range** in the P&L, and finally to Exact's account
  **Type**, which is scheme- and region-independent (with a balance-sign tiebreak on the Balance
  Sheet). The report still balances and nothing is dropped, but a section total may be off, which is
  exactly what configuring the titles fixes.
