If you're building accounting integrations, you've likely encountered a common challenge: your customers want to segment their financial data by department, location, project, or business unit—but every accounting platform handles this differently.
QuickBooks calls them "Classes." Xero uses "Tracking Categories." NetSuite has departments, locations, subsidiaries, AND classes. Intuit's new Enterprise Suite supports up to 20 custom dimensions. Sage Intacct offers eight.
In this post, we'll break down how tracking dimensions work across major accounting platforms and show you how to build integrations that handle these differences gracefully.
What Are Tracking Dimensions?
Tracking dimensions are metadata tags attached to accounting transactions that enable segmented reporting. Think of them as labels that let finance teams answer questions like:
- "What's our P&L for the Marketing department?"
- "How much revenue did the New York office generate?"
- "What are the expenses for Project Alpha?"
Most accounting platforms support some combination of four dimension types:
| Dimension | Purpose | Examples |
|---|---|---|
| Department | Organizational unit or cost center | Marketing, Sales, Engineering |
| Location | Physical or logical place | NYC Office, London, Online Store |
| Subsidiary | Legal entity in multi-company setup | US Holdings Inc, UK Ltd |
| Tracking Category | Flexible custom tags | Project Alpha, Grant #12345 |
The Challenge: Every Platform Is Different
Here's where it gets interesting. Each accounting platform implements tracking dimensions differently:
Xero: Keep It Simple (Maybe Too Simple)
Xero takes a minimalist approach—you get exactly two tracking categories per organization. That's it. No departments, no locations, no subsidiaries.
{
"tracking_categories": [
{ "id": "region-uuid", "name": "North America" },
{ "id": "dept-uuid", "name": "Sales" }
]
}
Many Xero users work around this by creating one tracking category for "Department" and another for "Region." It works, but requires careful planning since you can't add a third dimension later.
QuickBooks Online: The Naming Confusion
QuickBooks supports departments, locations, AND classes—but there's a catch. In QuickBooks, "Department" often functions more like what other systems call "Location" (tracking by branch or region).
{
"department_id": "58",
"location_id": "1",
"tracking_categories": [
{ "id": "5000000000000026437", "name": "Enterprise" }
]
}
Also, these features aren't enabled by default. Your customers need to turn on "Track locations" and "Track classes" in their QuickBooks settings.
NetSuite OneWorld: All The Dimensions
NetSuite goes all-in with full support for departments, locations, subsidiaries, and classes. The tradeoff? Complexity.
{
"department_id": "3",
"location_id": "1",
"subsidiary_id": "2",
"tracking_categories": [
{ "id": "1", "name": "Class A" }
]
}
For NetSuite OneWorld (multi-subsidiary) accounts, subsidiary_id is often required. Transactions without a valid subsidiary get rejected. Your integration needs to handle this gracefully.
Intuit Enterprise Suite: The Modern Approach
Intuit Enterprise Suite (IES) is Intuit's new mid-market offering, and it takes a fundamentally different approach than QuickBooks. Instead of fixed dimension types, IES supports up to 20 custom dimensions with unlimited values and 5 levels of hierarchy.
{
"dimensions": [
{ "type": "Department", "value": { "id": "dept-001" } },
{ "type": "Location", "value": { "id": "loc-001" } },
{ "type": "Project", "value": { "id": "proj-001" } },
{ "type": "CostCenter", "value": { "id": "cc-001" } }
]
}
IES vs QuickBooks Online:
| Feature | QuickBooks Online | Intuit Enterprise Suite |
|---|---|---|
| Custom dimensions | 3 fixed | Up to 20 |
| Hierarchy depth | 2 levels | 5 levels |
| Multi-entity | No | Yes (native) |
| Shared dimensions | N/A | Across entities |
If your customers are outgrowing QuickBooks, IES is often the next step—and your integration needs to handle the richer dimension model.
Sage Intacct: Maximum Flexibility
Sage Intacct supports up to eight custom dimensions plus the standard ones. Great for complex enterprise requirements, but it means more edge cases to handle.
Building Integrations That Handle The Differences
Here's our recommended approach for building robust accounting integrations:
1. Always Discover Dimensions First
Before writing transactions with tracking data, query what dimensions are available:
// Fetch available dimensions for the connected account
const departments = await accounting.departments.list();
const locations = await accounting.locations.list();
const trackingCategories = await accounting.trackingCategories.list();
This tells you:
- Which dimensions exist in this account
- What valid IDs you can use
- How the customer has configured their tracking
2. Handle Missing Support Gracefully
Not every connector supports every dimension. Your code should check before including fields:
const invoice = {
customer_id: customerId,
line_items: lineItems
};
// Only include tracking if supported
if (connectorSupports('departments')) {
invoice.department_id = departmentId;
}
if (connectorSupports('tracking_categories')) {
invoice.tracking_categories = categories;
}
For connector-specific fields that aren't in the unified model, you can use pass_through to send them directly to the downstream API.
3. Use Line Items for Mixed Tracking
Real-world invoices often span multiple departments or projects. Most platforms support tracking at the line item level:
{
"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" }]
}
]
}
Line item values override header values, giving you granular control.
4. Validate Subsidiary Requirements
For enterprise platforms like NetSuite and Sage Intacct, check if subsidiary is required:
if (serviceId === 'netsuite') {
const subsidiaries = await accounting.subsidiaries.list();
if (subsidiaries.data.length > 1 && !transaction.subsidiary_id) {
throw new Error('subsidiary_id is required for multi-subsidiary accounts');
}
}
5. Cache Dimension Lists
Tracking dimensions change infrequently. Cache them to avoid unnecessary API calls:
const departments = await cache.getOrSet(
`departments:${consumerId}:${serviceId}`,
() => accounting.departments.list(),
{ ttl: 3600 } // 1 hour
);
Quick Reference: Connector Support Matrix
Here's what each major platform supports:
| Connector | Departments | Locations | Subsidiaries | Tracking Categories |
|---|---|---|---|---|
| NetSuite | ✅ | ✅ | ✅ | ✅ |
| Sage Intacct | ✅ | ✅ | ✅ | ✅ |
| Intuit Enterprise Suite | ✅ | ✅ | — | ✅ (up to 20) |
| QuickBooks Online | ✅ | ✅ | — | ✅ |
| Workday | ✅ | — | ✅ | — |
| Dynamics 365 BC | — | ✅ | — | ✅ |
| Xero | — | — | — | ✅ |
| MYOB | — | — | — | ✅ |
For complete coverage details, see our Accounting API page.
Common Pitfalls to Avoid
"Invalid department_id" errors: Always verify IDs exist by listing dimensions first. Departments can be archived or restricted to certain subsidiaries.
Xero's two-category limit: This is organization-wide, not per-transaction. Help customers plan their category usage upfront.
QuickBooks features not enabled: If tracking fails, check whether the customer has enabled Classes and Locations in their QuickBooks settings.
NetSuite subsidiary requirements: For OneWorld accounts, subsidiary is usually mandatory. Query subsidiaries and require selection if multiple exist.
Wrapping Up
Tracking dimensions are essential for meaningful financial reporting, but the implementation varies wildly across platforms. The key is building integrations that:
- Discover available dimensions dynamically
- Handle missing support gracefully
- Validate requirements before submitting transactions
- Cache dimension lists for performance
For the complete technical reference with curl examples and API details, see our Tracking Dimensions Developer Guide.
Building accounting integrations? Apideck's Unified Accounting API normalizes tracking dimensions across 25+ connectors, so you can write code once and support them all.
Ready to get started?
Scale your integration strategy and deliver the integrations your customers need in record time.







