> This page is for version V1 (default).
> For other versions, use one of these documentation indexes:
> - V1 (default): https://docs.ottimate.com/v-1/llms.txt

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.ottimate.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.ottimate.com/_mcp/server.

# Dimensions

## Overview

In accounting systems, financial transactions must be properly categorized for reporting and compliance. Transactions are assigned to GL accounts, departments, locations, projects, and other organizational segments based on a company's accounting rules.

Ottimate represents these transaction classifications through **dimensions**. Each dimension links an Ottimate record to a corresponding entity in the client's ERP/accounting system.

**Key Characteristics:**

* Dimensions are **company-specific** - each company maintains its own set of dimensions
* Dimensions must be synced from your ERP before invoices can be properly coded
* The `erp_dimension_id` links Ottimate dimensions to your accounting system
* Dimensions can be applied at both invoice header and line item level

---

## Dimension Fields

### Response Fields

| Field                 | Type    | Description                                                                                                                                                |
| --------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                  | string  | Ottimate-assigned ID (e.g., `dim_VOH21Up3M7HV`)                                                                                                            |
| `name`                | string  | Dimension name (e.g., "Food Cost", "Marketing Department")                                                                                                 |
| `erp_type`            | string  | The dimension category label as displayed in your ERP (e.g., "Account", "Department"). Client-specific — varies by accounting software.                    |
| `ottimate_type`       | string  | Ottimate's standardized dimension type code (e.g., "account", "department")                                                                                |
| `code`                | string  | The GL code or short identifier for this dimension in your ERP                                                                                             |
| `reference_id`        | string  | A unique identifier that tracks this dimension across client systems (ERP, inventory management, Ottimate). Used for upsert matching on the bulk endpoint. |
| `erp_dimension_id`    | string  | Identifier from your ERP/accounting system                                                                                                                 |
| `ottimate_company_id` | integer | Company this dimension belongs to                                                                                                                          |

### Creation Fields

All fields below are required on both `POST /v1/dimensions` and `POST /v1/dimensions/bulk`.

| Field                 | Required | Description                                                      |
| --------------------- | -------- | ---------------------------------------------------------------- |
| `ottimate_company_id` | Yes      | Company ID this dimension belongs to                             |
| `erp_type`            | Yes      | Display type name as shown in your ERP (e.g., "Department")      |
| `ottimate_type`       | Yes      | Ottimate's standardized dimension type code (e.g., "department") |
| `name`                | Yes      | Dimension name                                                   |
| `code`                | Yes      | Short code (e.g., "MKT", "5000")                                 |
| `reference_id`        | Yes      | Unique identifier for upsert matching                            |
| `erp_dimension_id`    | Yes      | Identifier from your ERP/accounting system                       |

---

## Supported Dimension Types

The following dimension types are supported. The table shows equivalent entities across common ERP systems.

### GL Account (`account`)

Classifies how a transaction will be recorded in the general ledger.

| Dimension Type | NetSuite | Intacct    | Business Central | Acumatica | QuickBooks Online |
| -------------- | -------- | ---------- | ---------------- | --------- | ----------------- |
| `account`      | Account  | GL Account | GLAccount        | Account   | Account           |

**Example:**

```json
{
  "id": "dim_VOH21Up3M7HV",
  "name": "Food Cost",
  "erp_type": "Account",
  "code": "5000",
  "reference_id": "acct-food-cost-001",
  "ottimate_type": "account",
  "erp_dimension_id": "5000",
  "ottimate_company_id": 123
}
```

### Location (`location`)

Represents geographic locations like offices, facilities, or warehouses.

| Dimension Type | NetSuite | Intacct  | Business Central | Acumatica       | QuickBooks Online |
| -------------- | -------- | -------- | ---------------- | --------------- | ----------------- |
| `location`     | Location | Location | Location         | Branch/Location | —                 |

### Department (`department`)

Represents internal business units or functions.

| Dimension Type | NetSuite   | Intacct    | Business Central | Acumatica  | QuickBooks Online |
| -------------- | ---------- | ---------- | ---------------- | ---------- | ----------------- |
| `department`   | Department | Department | Department       | Subaccount | Department        |

### Project (`project`)

Tracks transactions related to a specific initiative, job, or capital effort.

| Dimension Type | NetSuite | Intacct | Business Central | Acumatica | QuickBooks Online |
| -------------- | -------- | ------- | ---------------- | --------- | ----------------- |
| `project`      | Job      | Project | Job              | Project   | —                 |

### Task (`task`)

Represents specific tasks within a project.

| Dimension Type | NetSuite | Intacct | Business Central | Acumatica | QuickBooks Online |
| -------------- | -------- | ------- | ---------------- | --------- | ----------------- |
| `task`         | —        | Task    | —                | Cost Code | —                 |

### Cost Type (`cost_type`)

Categorizes different types of costs within projects.

| Dimension Type | NetSuite | Intacct   | Business Central | Acumatica | QuickBooks Online |
| -------------- | -------- | --------- | ---------------- | --------- | ----------------- |
| `cost_type`    | —        | Cost Type | —                | Cost Code | —                 |

### Item (`item`)

Represents goods and services purchased from vendors.

| Dimension Type | NetSuite | Intacct | Business Central | Acumatica | QuickBooks Online |
| -------------- | -------- | ------- | ---------------- | --------- | ----------------- |
| `item`         | Item     | Item    | Item             | StockItem | Item              |

---

## Sync Patterns

### Ottimate-Hosted ERP Integrations

When you use an [Ottimate-hosted integration](https://ottimate.com/integrations/) (e.g., QuickBooks, NetSuite, Sage), Ottimate automatically syncs dimensions from your ERP:

* **Initial sync** happens during onboarding
* **Ongoing sync** keeps dimensions updated automatically
* **No API calls needed** - dimension management is handled by the integration

In this scenario, you primarily use the Dimensions API to **read** dimension data for reference or validation.

### Custom ERP Integrations

If you're building a custom integration with an unsupported ERP, you're responsible for syncing dimensions:

1. **Extract** dimension data from your ERP (chart of accounts, departments, etc.)
2. **Transform** to Ottimate's dimension format
3. **Load** via the Dimensions API (`POST /v1/dimensions` or `POST /v1/dimensions/bulk`)
4. **Maintain** ongoing sync as dimensions are added/updated in your ERP

> **Note**
>
> Keep dimension data consistent between Ottimate and your ERP. Mismatched dimension IDs can cause issues during invoice export.

---

## API Operations

### List Dimensions

Retrieve all dimensions for a company, optionally filtered by ERP type:

```bash
GET /v1/dimensions?ottimate_company_id=123&erp_type=Account
```

**Response:**

```json
{
  "version": "1.0.0",
  "count": 100,
  "page": 1,
  "limit": 20,
  "dimensions": [
    {
      "id": "dim_VOH21Up3M7HV",
      "name": "Food Cost",
      "erp_type": "Account",
      "code": "5000",
      "reference_id": "acct-food-cost-001",
      "ottimate_type": "account",
      "erp_dimension_id": "5000",
      "ottimate_company_id": 123
    },
    {
      "id": "dim_XKL45Qp8N2RT",
      "name": "Beverage Cost",
      "erp_type": "Account",
      "code": "5100",
      "reference_id": "acct-beverage-cost-001",
      "ottimate_type": "account",
      "erp_dimension_id": "5100",
      "ottimate_company_id": 123
    }
  ]
}
```

> **Note**
>
> Add `detail=basic` to shorten each item in the `dimensions` array to just `id`, `name`, `erp_dimension_id`, and `erp_type` — `code`, `reference_id`, `ottimate_type`, and `ottimate_company_id` are omitted. Use this when you only need enough information to display or match a dimension, not to re-submit it.

### Get Single Dimension

Retrieve a specific dimension by ID:

```bash
GET /v1/dimensions/dim_VOH21Up3M7HV
```

**Response:**

```json
{
  "version": "1.0.0",
  "success": true,
  "dimension": {
    "id": "dim_VOH21Up3M7HV",
    "name": "Food Cost",
    "erp_type": "Account",
    "code": "5000",
    "reference_id": "acct-food-cost-001",
    "ottimate_type": "account",
    "erp_dimension_id": "5000",
    "ottimate_company_id": 123
  }
}
```

### Create Single Dimension

Create a new dimension. All fields shown below are required — including `code`.

```bash
POST /v1/dimensions
Content-Type: application/json

{
  "ottimate_company_id": 123,
  "erp_type": "Department",
  "ottimate_type": "department",
  "name": "Marketing Department",
  "code": "MKT",
  "reference_id": "dept-mkt-001",
  "erp_dimension_id": "DEPT-MKT-001"
}
```

**Response (200 OK):**

```json
{
  "version": "1.0.0",
  "dimension": {
    "id": "dim_ABC12345XYZ",
    "name": "Marketing Department",
    "erp_type": "Department",
    "code": "MKT",
    "reference_id": "dept-mkt-001",
    "ottimate_type": "department",
    "erp_dimension_id": "DEPT-MKT-001",
    "ottimate_company_id": 123
  }
}
```

The created object is returned nested under a `dimension` key, matching the shape of the get-single-dimension response. There is no `created_date` or other timestamp field in the response.

> **Note**
>
> **Single-create does not upsert.** Unlike the bulk endpoint, `POST /v1/dimensions` rejects a request whose `reference_id` + `erp_type` combination already exists for the company, with a 400 response:
>
> ```json
> {
>   "message": {
>     "detail": ["A dimension with reference_id 'dept-mkt-001' and erp_type 'Department' already exists."]
>   }
> }
> ```
>
> To update an existing dimension, use the bulk endpoint below (which upserts by `reference_id` + `erp_type`).

### Bulk Create/Update Dimensions

Create or update multiple dimensions in a single request. This endpoint uses **upsert behavior** - existing dimensions (matched by `reference_id` + `erp_type`) are updated, new dimensions are created. This is different from single-create (`POST /v1/dimensions`), which rejects duplicates instead of updating them — see the note above.

```bash
POST /v1/dimensions/bulk
Content-Type: application/json

{
  "ottimate_company_id": 123,
  "dimensions": [
    {
      "erp_type": "Department",
      "ottimate_type": "department",
      "name": "Operations Department",
      "code": "OPS",
      "reference_id": "dept-ops-001",
      "erp_dimension_id": "DEPT-OPS-001"
    },
    {
      "erp_type": "Department",
      "ottimate_type": "department",
      "name": "Finance Department",
      "code": "FIN",
      "reference_id": "dept-fin-001",
      "erp_dimension_id": "DEPT-FIN-001"
    }
  ]
}
```

**Response (200 OK):**

```json
{
  "version": "1.0.0",
  "success_count": 3,
  "created_count": 2,
  "updated_count": 1,
  "fail_count": 0,
  "created": [
    {
      "id": "dim_DEF67890ABC",
      "ottimate_company_id": 123,
      "ottimate_type": "department",
      "name": "Operations Department",
      "code": "OPS",
      "erp_type": "Department",
      "reference_id": "dept-ops-001",
      "erp_dimension_id": "DEPT-OPS-001"
    },
    {
      "id": "dim_GHI11223DEF",
      "ottimate_company_id": 123,
      "ottimate_type": "department",
      "name": "Finance Department",
      "code": "FIN",
      "erp_type": "Department",
      "reference_id": "dept-fin-001",
      "erp_dimension_id": "DEPT-FIN-001"
    }
  ],
  "updated": [
    {
      "id": "dim_VOH21Up3M7HV",
      "ottimate_company_id": 123,
      "ottimate_type": "account",
      "name": "Food Cost (Updated)",
      "code": "5000",
      "erp_type": "Account",
      "reference_id": "acct-food-cost-001",
      "erp_dimension_id": "5000"
    }
  ],
  "failures": []
}
```

`created` and `updated` each carry the full dimension object (the same fields as the single-create response), not just `id` and `name`.

> **Note**
>
> **Validation is all-or-nothing.** If any item in the `dimensions` array is missing a required field or has a type that can't be resolved, the entire request is rejected with `400 Bad Request` and **no dimensions are created or updated** — not even the valid ones. For example, omitting `code` on one item out of ten fails the whole batch:
>
> ```json
> {
>   "message": {
>     "dimensions": [{}, {}, {"code": ["This field is required."]}]
>   }
> }
> ```
>
> `fail_count` / `failures` in a `200` response are reserved for the rare case where an individual item passes validation but fails during the write itself (for example, a record-level conflict). Because validation is checked up front for every item, this happens far less often than the all-or-nothing case above — plan for the 400 case first.

---

## Using Dimensions in Invoices

Dimensions are applied to invoices and line items to categorize transactions for proper GL coding.

**Invoice Header Dimensions:**
Used for header-level coding that applies to the entire invoice (e.g., department, location).

**Line Item Dimensions:**
Used for item-level coding when different line items should be coded to different accounts.

```json
{
  "ottimate_company_id": 123,
  "ottimate_location_id": 456,
  "ottimate_vendor_id": 789,
  "invoice_number": "INV-001",
  "dimensions": {
    "Department": "DEPT-MKT-001",
    "Location": "LOC-001"
  },
  "line_items": [
    {
      "description": "Office Supplies",
      "amount": 150.00,
      "dimensions": {
        "Account": "5000"
      }
    }
  ]
}
```

> **Note**
>
> The dimension keys in the `dimensions` object are the display type names (e.g., "Account", "Department"), and the values are the `erp_dimension_id` values.

---

## Best Practices

* **Sync dimensions before invoices** - Ensure all required dimensions exist before creating invoices that reference them.
* **Use the bulk endpoint for efficiency** - When syncing your chart of accounts, use `POST /v1/dimensions/bulk` to reduce API calls and to update existing dimensions in place.
* **Match ERP identifiers exactly** - The `erp_dimension_id` must match your ERP's identifier for successful invoice export.
* **Use consistent reference_ids** - The `reference_id` field determines upsert matching on the bulk endpoint; use a consistent naming convention.
* **Cache dimension mappings** - Fetch and cache the dimension list at integration startup to avoid repeated API calls.
* **Handle validation errors** - A single invalid item in a bulk request fails the entire batch (`400`, nothing created). Validate your payload client-side before sending, and check the `failures` array for any items reported in a `200` response.

---

## Troubleshooting

### "Dimension not found" when coding invoice

* Verify the dimension exists: `GET /v1/dimensions?ottimate_company_id=123&erp_type=Account`
* Check you're using the correct `erp_dimension_id` value
* Ensure the dimension belongs to the same company as the invoice

### Invoice export fails with dimension error

* Verify the `erp_dimension_id` matches exactly what's in your ERP
* Check that all required dimension types are populated on the invoice

### Bulk create returns 400 for the whole batch

* One invalid item rejects every item in the request, including otherwise-valid ones. The response body identifies which array index failed and why.
* Common issues: missing `code`, missing `reference_id`, missing `erp_type` or `ottimate_type`
* Fix the flagged item(s) and resend the full batch

### Dimensions not syncing from ERP

* For Ottimate-hosted integrations, check sync status in the dashboard
* For custom integrations, verify your API calls are succeeding