# Tracking Dimensions: Departments, Locations, Subsidiaries & Tracking Categories

Apideck's Accounting API exposes four tracking dimensions for segmenting transactions: Department (cost center), Location (physical or logical place), Subsidiary (legal entity in a multi-company setup), and Tracking Category (flexible custom tags). Not every connector supports every dimension: NetSuite and Sage Intacct support all four, while Xero and MYOB only support tracking categories.

## Overview

Apideck's Accounting API supports four tracking dimensions:

| Dimension | Purpose | Example Use Cases |
|-----------|---------|-------------------|
| **Department** | Organizational unit or cost center | Marketing, Sales, Engineering, Operations |
| **Location** | Physical or logical place | London Office, New York, Online Store |
| **Subsidiary** | Legal entity in multi-company setup | US Holdings Inc, UK Ltd, APAC Pty |
| **Tracking Category** | Flexible custom tags | Project Alpha, Grant #12345, Product Line A |

These dimensions enable powerful reporting capabilities:
- P&L by department (cost center analysis)
- Balance sheet by subsidiary (consolidated reporting)
- Revenue by location (regional performance)
- Expenses by tracking category (project costing)

## Supported Resources

### Transactional Resources

Tracking dimensions can be applied to these transactional resources:

| Resource | department_id | location_id | subsidiary_id | tracking_categories |
|----------|:-------------:|:-----------:|:-------------:|:-------------------:|
| [Invoices](/apis/accounting/reference#tag/Invoices) | ✅ | ✅ | ✅ | ✅ |
| [Bills](/apis/accounting/reference#tag/Bills) | ✅ | ✅ | ✅ | ✅ |
| [Credit Notes](/apis/accounting/reference#tag/CreditNotes) | ✅ | ✅ | ✅ | ✅ |
| [Expenses](/apis/accounting/reference#tag/Expenses) | ✅ | ✅ | ❌ | ✅ |
| [Purchase Orders](/apis/accounting/reference#tag/PurchaseOrders) | ✅ | ✅ | ✅ | ✅ |
| [Journal Entries](/apis/accounting/reference#tag/JournalEntries) | ✅ | ✅ | ❌ | ✅ |
| [Payments](/apis/accounting/reference#tag/Payments) | ❌ | ❌ | ❌ | ✅ |
| [Bill Payments](/apis/accounting/reference#tag/BillPayments) | ❌ | ❌ | ❌ | ✅ |

### Line Item Support

Most transactional resources support tracking at both the header and line item level:

```json
{
  "department_id": "dept-001",
  "location_id": "loc-001",
  "line_items": [
    {
      "description": "Consulting services",
      "department_id": "dept-002",
      "location_id": "loc-002",
      "tracking_categories": [
        { "id": "cat-001", "name": "Project Alpha" }
      ]
    }
  ]
}
```

**Priority**: Line item values override header values for that specific line.

### Dimension Resources

You can manage tracking dimensions through dedicated endpoints:

| Endpoint | Description |
|----------|-------------|
| [GET /accounting/departments](/apis/accounting/reference#tag/Departments) | List all departments |
| [GET /accounting/locations](/apis/accounting/reference#tag/Locations) | List all locations |
| [GET /accounting/subsidiaries](/apis/accounting/reference#tag/Subsidiaries) | List all subsidiaries |
| [GET /accounting/tracking-categories](/apis/accounting/reference#tag/TrackingCategories) | List all tracking categories |

Each endpoint supports standard CRUD operations (list, get, create, update, delete) where the connector supports them.

## Using Tracking Dimensions

### Creating a Transaction with Tracking

```bash
curl -X POST "https://unify.apideck.com/accounting/invoices" \
  -H "Authorization: Bearer {api_key}" \
  -H "x-apideck-consumer-id: {consumer_id}" \
  -H "x-apideck-app-id: {app_id}" \
  -H "x-apideck-service-id: quickbooks" \
  -H "Content-Type: application/json" \
  -d '{
    "customer_id": "cust-123",
    "department_id": "dept-sales",
    "location_id": "loc-nyc",
    "tracking_categories": [
      { "id": "class-enterprise" }
    ],
    "line_items": [
      {
        "description": "Professional Services",
        "quantity": 10,
        "unit_price": 150,
        "department_id": "dept-consulting",
        "tracking_categories": [
          { "id": "project-alpha" }
        ]
      }
    ]
  }'
```

### Filtering Reports by Location

[Balance Sheet](/apis/accounting/reference#tag/BalanceSheet) and [Profit & Loss](/apis/accounting/reference#tag/ProfitAndLoss) reports can be filtered by location:

```bash
# Balance Sheet filtered by location
curl "https://unify.apideck.com/accounting/balance-sheet?filter[location_id]=loc-nyc" \
  -H "Authorization: Bearer {api_key}" \
  -H "x-apideck-consumer-id: {consumer_id}" \
  -H "x-apideck-app-id: {app_id}" \
  -H "x-apideck-service-id: netsuite"

# Profit & Loss filtered by location
curl "https://unify.apideck.com/accounting/profit-and-loss?filter[location_id]=loc-nyc" \
  -H "Authorization: Bearer {api_key}" \
  -H "x-apideck-consumer-id: {consumer_id}" \
  -H "x-apideck-app-id: {app_id}" \
  -H "x-apideck-service-id: netsuite"
```

### Listing Available Tracking Categories

```bash
curl "https://unify.apideck.com/accounting/tracking-categories" \
  -H "Authorization: Bearer {api_key}" \
  -H "x-apideck-consumer-id: {consumer_id}" \
  -H "x-apideck-app-id: {app_id}" \
  -H "x-apideck-service-id: xero"
```

Response:
```json
{
  "data": [
    {
      "id": "tc-001",
      "name": "Region",
      "status": "active",
      "items": [
        { "id": "tc-001-a", "name": "North America" },
        { "id": "tc-001-b", "name": "Europe" },
        { "id": "tc-001-c", "name": "Asia Pacific" }
      ]
    },
    {
      "id": "tc-002",
      "name": "Department",
      "status": "active",
      "items": [
        { "id": "tc-002-a", "name": "Sales" },
        { "id": "tc-002-b", "name": "Marketing" }
      ]
    }
  ]
}
```

## Connector Support

Not all connectors support all tracking dimensions. Here's what's available:

### Full Support (All 4 Dimensions)

| Connector | Departments | Locations | Subsidiaries | Tracking Categories |
|-----------|:-----------:|:---------:|:------------:|:-------------------:|
| [NetSuite](/apis/accounting/netsuite) | ✅ | ✅ | ✅ | ✅ |
| [Sage Intacct](/apis/accounting/sage-intacct) | ✅ | ✅ | ✅ | ✅ |

### Partial Support (2-3 Dimensions)

| Connector | Departments | Locations | Subsidiaries | Tracking Categories |
|-----------|:-----------:|:---------:|:------------:|:-------------------:|
| [QuickBooks Online](/apis/accounting/quickbooks) | ✅ | ✅ | ❌ | ✅ |
| [Workday](/apis/accounting/workday) | ✅ | ❌ | ✅ | ❌ |
| [Dynamics 365 BC](/apis/accounting/microsoft-dynamics-365-business-central) | ❌ | ✅ | ❌ | ✅ |

### Limited Support (1 Dimension)

| Connector | Departments | Locations | Subsidiaries | Tracking Categories |
|-----------|:-----------:|:---------:|:------------:|:-------------------:|
| [Xero](/apis/accounting/xero) | ❌ | ❌ | ❌ | ✅ |
| [MYOB](/apis/accounting/myob) | ❌ | ❌ | ❌ | ✅ |
| [Zoho Books](/apis/accounting/zoho-books) | ❌ | ❌ | ❌ | ❌ |
| [FreeAgent](/apis/accounting/freeagent) | ❌ | ❌ | ❌ | ❌ |
| [Freshbooks](/apis/accounting/freshbooks) | ❌ | ❌ | ❌ | ❌ |

## Connector-Specific Behavior

### QuickBooks Online

QuickBooks uses "Classes" for tracking categories and has both Departments and Locations:

```json
{
  "department_id": "58",
  "location_id": "1",
  "tracking_categories": [
    { "id": "5000000000000026437", "name": "Enterprise" }
  ]
}
```

**Note**: QuickBooks' "Department" is often used for location-like tracking (regions, branches). Check your customer's QuickBooks configuration to understand how they use these fields.

**Requirement**: The QuickBooks account must have "Track locations" and "Track classes" enabled in Settings > Account and Settings > Advanced.

**Scope required**: `com.intuit.quickbooks.accounting`

For detailed setup instructions, see the [QuickBooks Departments and Classes guide](https://developers.apideck.com/connectors/quickbooks/docs/application_owner+departments_and_classes).

### Xero

Xero uses [Tracking Categories](/apis/accounting/reference#tag/TrackingCategories) for all dimensional tracking. Each organization can have **up to 2 tracking categories**, each with multiple options:

```json
{
  "tracking_categories": [
    { "id": "region-uuid", "name": "North America" },
    { "id": "dept-uuid", "name": "Sales" }
  ]
}
```

**Limitation**: Only 2 tracking categories per Xero organization. Plan category usage carefully.

**Common Pattern**: Many Xero users create one tracking category for "Department" and one for "Region" to simulate the department/location split.

**Line Items**: Tracking can be assigned at both the transaction header and line item level for granular reporting.

### NetSuite

[NetSuite](/apis/accounting/netsuite) (OneWorld edition) has full support for all dimensions:

```json
{
  "department_id": "3",
  "location_id": "1",
  "subsidiary_id": "2",
  "tracking_categories": [
    { "id": "1", "name": "Class A" }
  ]
}
```

**Requirement**: For NetSuite OneWorld, `subsidiary_id` is often **required** on transactions. Transactions without a valid subsidiary will be rejected.

**Hierarchy**: Departments, locations, and subsidiaries can be hierarchical (parent/child). Use the `parent_id` field when creating nested structures.

**Custom Segments**: NetSuite supports additional custom segments beyond the standard four dimensions. These can be set with [pass_through](/guides/pass-through), but NetSuite is a SOAP connector, so the path has to start at the SOAP envelope: a root-level `extend_object` is refused with `UnsupportedPassThroughError`. See [XML and SOAP connectors](/guides/pass-through#xml-and-soap-connectors) for the shape, and note that a NetSuite reference field is written as an `internalId` attribute rather than a text value:

```json
{
  "pass_through": [
    {
      "service_id": "netsuite",
      "extend_paths": [
        {
          "path": "$['soap:Envelope']['soap:Body']['platformMsgs:add']['platformMsgs:record']['tranPurch:customForm']['_attributes']['internalId']",
          "value": "229"
        }
      ]
    }
  ]
}
```

**Department scope**: NetSuite departments are scoped to a subsidiary. A `department_id` that is valid for one subsidiary is rejected with `Invalid department reference key … for subsidiary …` on another, so pair `department_id` with the matching `subsidiary_id`.

### Sage Intacct

[Sage Intacct](/apis/accounting/sage-intacct) uses "Entities" for subsidiaries and supports up to 8 custom dimensions:

```json
{
  "department_id": "SALES",
  "location_id": "US-WEST",
  "subsidiary_id": "ENTITY-001",
  "tracking_categories": [
    { "id": "PROJECT-123", "name": "Project Alpha" }
  ]
}
```

**Requirement**: `location_id` (maps to `LOCATIONID`) is often required for multi-entity setups.

**Custom Dimensions**: Sage supports user-defined dimensions beyond the standard four. These are accessible via [pass_through](/guides/pass-through) if needed.

**Note**: Sage Intacct was historically mapped to use subsidiaries for location data. Dedicated locations support was added later.

## Best Practices

### 1. Discover Available Dimensions First

Before creating transactions with tracking, list available dimensions:

```bash
# Get departments
curl "https://unify.apideck.com/accounting/departments" \
  -H "Authorization: Bearer {api_key}" \
  -H "x-apideck-consumer-id: {consumer_id}" \
  -H "x-apideck-app-id: {app_id}" \
  -H "x-apideck-service-id: {service_id}"

# Get tracking categories
curl "https://unify.apideck.com/accounting/tracking-categories" \
  -H "Authorization: Bearer {api_key}" \
  -H "x-apideck-consumer-id: {consumer_id}" \
  -H "x-apideck-app-id: {app_id}" \
  -H "x-apideck-service-id: {service_id}"
```

This ensures you use valid IDs and understand the customer's tracking structure.

### 2. Handle Missing Dimension Support Gracefully

Not all connectors support all dimensions. Your integration should check support before including fields:

```javascript
const invoice = {
  customer_id: customerId,
  line_items: lineItems
};

// Only include tracking if the connector supports it
if (connectorSupports('departments')) {
  invoice.department_id = departmentId;
}

if (connectorSupports('tracking_categories')) {
  invoice.tracking_categories = categories;
}
```

### 3. Use Line Items for Mixed Tracking

When a single invoice spans multiple departments or projects:

```json
{
  "customer_id": "cust-123",
  "line_items": [
    {
      "description": "Sales consulting",
      "department_id": "sales",
      "tracking_categories": [{ "id": "project-a" }]
    },
    {
      "description": "Technical implementation",
      "department_id": "engineering",
      "tracking_categories": [{ "id": "project-a" }]
    }
  ]
}
```

### 4. Validate Subsidiary Requirements

For enterprise connectors (NetSuite, Sage), check if subsidiary is required:

```javascript
// NetSuite OneWorld requires subsidiary
if (serviceId === 'netsuite') {
  const subsidiaries = await apideck.accounting.subsidiaries.list();
  if (subsidiaries.data.length > 1 && !transaction.subsidiary_id) {
    throw new Error('subsidiary_id is required for NetSuite OneWorld');
  }
}
```

### 5. Cache Dimension Lists

Tracking dimensions change infrequently. Cache the results of list calls:

```javascript
// Cache departments for 1 hour
const departments = await cache.getOrSet(
  `departments:${consumerId}:${serviceId}`,
  () => apideck.accounting.departments.list(),
  { ttl: 3600 }
);
```

## Troubleshooting

### "Invalid department_id" Error

**Cause**: The department ID doesn't exist or isn't active in the connected accounting system.

**Solution**:
1. List departments to get valid IDs: `GET /accounting/departments`
2. Verify the department is active (not archived)
3. Check if department is valid for the specified subsidiary/location

### "tracking_categories not supported" Error

**Cause**: The connector doesn't support tracking categories.

**Solution**: Check the [connector support matrix](#connector-support) above. Consider using [pass_through](/guides/pass-through) for connector-specific fields.

### Subsidiary Required Error (NetSuite/Sage)

**Cause**: Multi-entity systems often require subsidiary context.

**Solution**:
1. List subsidiaries: `GET /accounting/subsidiaries`
2. Include `subsidiary_id` on the transaction
3. Ensure the subsidiary is valid for the current user's permissions

### Xero "Maximum tracking categories exceeded"

**Cause**: Xero only allows 2 tracking categories per organization.

**Solution**: Xero's limit is organization-wide, not per-transaction. Work with your customer to:
1. Consolidate tracking needs into 2 categories
2. Use tracking category "options" for additional granularity within each category

## Enhancement Options

If you need tracking support that isn't currently available in the unified API:

1. **Pass-through**: Use [pass_through](/guides/pass-through) to send connector-specific fields directly to the downstream API
2. **Proxy API**: For complex platform-specific functionality, use the [Proxy API](https://help.apideck.com/en/articles/4230978) to make direct calls
3. **Request Enhancement**: [Contact us](https://www.apideck.com/contact) to request unified API extensions for your use case

## Related Resources

- [Departments API Reference](/apis/accounting/reference#tag/Departments)
- [Locations API Reference](/apis/accounting/reference#tag/Locations)
- [Subsidiaries API Reference](/apis/accounting/reference#tag/Subsidiaries)
- [Tracking Categories API Reference](/apis/accounting/reference#tag/TrackingCategories)
- [Using Pass-through](/guides/pass-through)
- [Balance Sheet API Reference](/apis/accounting/reference#tag/BalanceSheet)
- [Profit and Loss API Reference](/apis/accounting/reference#tag/ProfitAndLoss)
