> 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.

# Account Structure

## Overview

Ottimate uses a tiered organizational hierarchy to manage accounts payable operations. Understanding this structure is essential for integration because every API request must be properly scoped to the correct company and location.

The hierarchy consists of:

1. **Account** - The top-level organization (an Ottimate client)
2. **Company** - A distinct accounting entity with its own chart of accounts and vendors
3. **Location** - A subdivision within a company for invoice processing and approvals

Additionally, locations can be organized into **Groups** - optional labels that can span multiple companies in multi-company accounts.

**Single-Company Account:**

```
Structural Hierarchy:
Ottimate Account (racc_xxx)
│
└── Company 1 (id: 123)
    ├── Chart of Accounts (Dimensions)
    ├── Vendor List
    └── Locations
        ├── Location A (id: 456)
        ├── Location B (id: 457)
        ├── Location C (id: 458)
        └── Location D (id: 459)

Optional Groups (labels only):
├── North Region → Locations A, B
└── South Region → Locations C, D
```

**Multi-Company Account:**

```
Structural Hierarchy:
Ottimate Account (racc_xxx)
│
├── Company 1 (id: 123)
│   ├── Chart of Accounts (Dimensions)
│   ├── Vendor List
│   └── Locations
│       ├── Location A (id: 456)
│       └── Location B (id: 457)
│
└── Company 2 (id: 789)
    ├── Chart of Accounts (Dimensions)
    ├── Vendor List
    └── Locations
        ├── Location D (id: 460)
        └── Location E (id: 461)

Groups (can span across companies):
├── West Region → Location A (Company 1), Location D (Company 2)
└── East Region → Location B (Company 1), Location E (Company 2)
```

> **Note**
>
> **Key Principle:** Data does not cross company boundaries. Each company has completely separate dimensions, vendors, and invoice workflows.

### Identifier naming across endpoints

The Accounts API returns companies and locations with a plain `id`. That same value is what you pass on the other endpoints, where it is named for the entity it points at:

| Value       | Returned as                                            | Sent as                                                                                       |
| ----------- | ------------------------------------------------------ | --------------------------------------------------------------------------------------------- |
| Account ID  | `id` on `GET /v1/accounts` (a `racc_`-prefixed string) | `{account_id}` in the Accounts API paths                                                      |
| Company ID  | `id` on `GET /v1/accounts/{account_id}/companies`      | `ottimate_company_id` on vendors, invoices, dimensions, and catalog; `company_id` on payments |
| Location ID | `id` on `GET /v1/accounts/{account_id}/locations`      | `ottimate_location_id` on invoices, purchase orders, and receipts; `location_id` on payments  |

There is no `ottimate_company_id` or `ottimate_location_id` field in an Accounts API response — those names only ever appear in requests to other APIs.

---

## Hierarchy Components

### Client Accounts (racc\_)

A **client account** represents an organization using Ottimate to automate accounts payable processes. This is the top-level entity that Ottimate provisions during onboarding.

**Key Characteristics:**

* Unique identifier format: `racc_` prefix (e.g., `racc_90832490382904802384`)
* Contains one or more companies
* API credentials (Client ID, Client Secret) are scoped to an account
* Billing and subscription are managed at the account level

**For API Integrators**

Accounts are provisioned by Ottimate during the **Onboarding** process before you begin integration development. By the time you receive your API credentials, your account structure (account, companies, and locations) is already configured.

> **Note**
>
> **Structure is read-only, with one exception.** You cannot create, rename, or delete accounts, companies, locations, or groups through the API, and you cannot move a location between companies or groups. For any of those, contact your Ottimate Partner Manager.
>
> The one setting you can change is a location's Basic Approval flag, via [`PATCH /v1/accounts/{id}/locations/{location_id}`](/api-reference/api-reference/accounts/patch-accounts-id-locations-location-id). Everything else on the structure is `GET`-only.

**Listing the accounts you can reach:**

Your credentials may be scoped to more than one account. `GET /v1/accounts` returns every account your API user can access, which is where an integration usually starts.

| Task                             | Endpoint                                                                      |
| -------------------------------- | ----------------------------------------------------------------------------- |
| List all accounts you can access | [`GET /v1/accounts`](/api-reference/api-reference/accounts/get-accounts-root) |
| Filter accounts by name          | `GET /v1/accounts?search={account_name}`                                      |

```json
{
  "version": "1.0.0",
  "count": 1,
  "page": 1,
  "limit": 50,
  "results": [
    {
      "id": "racc_a1b2c3d4e5f6",
      "name": "Acme Restaurant Group",
      "created_date": "2024-01-15T10:30:00Z",
      "created_date_utc": "2024-01-15T10:30:00.000000Z",
      "company_count": 5,
      "location_count": 23
    }
  ]
}
```

**Response Fields:**

| Field              | Type    | Description                                                                                     |
| ------------------ | ------- | ----------------------------------------------------------------------------------------------- |
| `id`               | string  | The account ID, `racc_` prefixed. Use it as `{account_id}` in the paths below.                  |
| `name`             | string  | Account name                                                                                    |
| `created_date`     | string  | Account creation timestamp                                                                      |
| `created_date_utc` | string  | UTC equivalent of `created_date`, ISO 8601 with a `Z` suffix                                    |
| `company_count`    | integer | Number of companies in this account. Companies have no active flag, so this counts all of them. |
| `location_count`   | integer | Number of **active** locations across all companies in this account                             |

---

### Companies

A **company** is a distinct accounting entity within a client account. Each company maintains its own:

* **Chart of accounts** - Separate set of dimensions (GL accounts, departments, etc.)
* **Vendor list** - Vendors are company-specific
* **Accounting rules** - Separate coding and approval policies
* **ERP connection** - Each company connects to one ERP instance

#### When to Use Multiple Companies

Use separate companies when:

* **Different ERP systems** - Subsidiary A uses NetSuite, Subsidiary B uses QuickBooks
* **Separate legal entities** - Each entity files taxes separately and maintains separate books
* **Distinct chart of accounts** - Each entity has a completely different GL structure
* **Separate vendor relationships** - Vendors are managed independently between entities

Use a single company with multiple locations when:

* **Same ERP system** - All locations use the same accounting software
* **Shared chart of accounts** - Same GL structure across all locations
* **Shared vendor list** - Vendors serve multiple locations
* **Centralized accounting** - Single accounting team manages all locations

#### Examples

**Multi-Company Scenario:**

```
Restaurant Group (Account)
├── Brand A LLC (Company 1) - Uses NetSuite
│   └── 50 Brand A locations
└── Brand B Corp (Company 2) - Uses QuickBooks
    └── 30 Brand B locations
```

**Single-Company Scenario:**

```
Regional Restaurant Chain (Account)
└── ABC Restaurants Inc (Company 1) - Uses NetSuite
    ├── Boston Location
    ├── New York Location
    └── Philadelphia Location
```

#### Accessing Company Information

Companies are pre-configured during account onboarding. Use the companies endpoint to retrieve company IDs for your API calls:

| Task                              | Endpoint                                                                                                     |
| --------------------------------- | ------------------------------------------------------------------------------------------------------------ |
| List all companies for an account | [`GET /v1/accounts/{account_id}/companies`](/api-reference/api-reference/accounts/get-accounts-id-companies) |
| Filter companies by name          | `GET /v1/accounts/{account_id}/companies?search={company_name}`                                              |
| Get company details               | Included in the list response above                                                                          |

**List Companies Example:**

```bash
GET https://api.ottimate.com/v1/accounts/racc_90832490382904802384/companies
```

**Response:**

```json
{
  "version": "1.0.0",
  "count": 2,
  "page": 1,
  "limit": 10,
  "results": [
    {
      "id": 123,
      "name": "Acme Inc.",
      "location_count": 5,
      "plan": null
    },
    {
      "id": 124,
      "name": "Acme Inc. - Downtown",
      "location_count": 1,
      "plan": null
    }
  ]
}
```

**Response Fields:**

| Field            | Type    | Description                                                                                                              |
| ---------------- | ------- | ------------------------------------------------------------------------------------------------------------------------ |
| `version`        | string  | The API version that rendered the response. See [API versioning](#api-versioning) below.                                 |
| `count`          | integer | Total number of companies matching the request                                                                           |
| `page`           | integer | The page returned                                                                                                        |
| `limit`          | integer | Page size                                                                                                                |
| `id`             | integer | The Ottimate-assigned company ID (send it as `ottimate_company_id` in other API calls)                                   |
| `name`           | string  | Company name                                                                                                             |
| `location_count` | integer | Number of **active** locations in this company — the same active-only basis as `GET /v1/accounts/{account_id}/locations` |
| `plan`           | string  | Reserved. Always `null` in the current release — subscription plan is held at the account level, not per company.        |

> **Note**
>
> **Pagination fields:** List responses carry `count`, `page`, and `limit` only. There are no `next` or `previous` link fields — page through results by incrementing `page` until you have seen `count` records.

---

### Locations

A **location** is a subdivision within a company used to organize invoice processing, permissions, and approval workflows. Locations share the same chart of accounts and vendor list as their parent company.

**Key Characteristics:**

* Belongs to exactly one company
* Shares the company's dimensions and vendors
* Can have location-specific approval policies
* Can have location-specific user permissions
* Can have location-specific email to receive invoices
* Used to route invoices to appropriate approvers

#### Accessing Location Information

Locations are pre-configured during account onboarding. Use the locations endpoint to retrieve location IDs for invoice creation and filtering:

| Task                                 | Endpoint                                                                                                                                   |
| ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------ |
| List all locations for an account    | [`GET /v1/accounts/{account_id}/locations`](/api-reference/api-reference/accounts/get-accounts-id-locations)                               |
| Filter locations by name             | `GET /v1/accounts/{account_id}/locations?search={location_name}`                                                                           |
| Toggle Basic Approval for a location | [`PATCH /v1/accounts/{account_id}/locations/{location_id}`](/api-reference/api-reference/accounts/patch-accounts-id-locations-location-id) |

**List Locations Example:**

```bash
GET https://api.ottimate.com/v1/accounts/racc_90832490382904802384/locations
```

**Response:**

```json
{
  "version": "1.0.0",
  "count": 3,
  "page": 1,
  "limit": 10,
  "results": [
    {
      "id": 456,
      "name": "Main Kitchen",
      "company": "Acme Inc.",
      "company_id": 123,
      "group": "West Region",
      "uses_approval": true
    },
    {
      "id": 457,
      "name": "Downtown Branch",
      "company": "Acme Inc.",
      "company_id": 123,
      "group": "West Region",
      "uses_approval": false
    },
    {
      "id": 458,
      "name": "East Side Location",
      "company": "Acme Inc.",
      "company_id": 123,
      "group": "East Region",
      "uses_approval": false
    }
  ]
}
```

**Response Fields:**

| Field            | Type    | Description                                                                                                                           |
| ---------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `version`        | string  | The API version that rendered the response. See [API versioning](#api-versioning) below.                                              |
| `id`             | integer | The Ottimate-assigned location ID (send it as `ottimate_location_id` in invoice creation)                                             |
| `name`           | string  | Location name                                                                                                                         |
| `company`        | string  | Parent company name                                                                                                                   |
| `company_id`     | integer | Parent company ID — the `id` from the companies list                                                                                  |
| `location_group` | string  | The group this location belongs to, or `null`. Returned when you send `X-API-Version: 1.1.0`.                                         |
| `group`          | string  | **Deprecated.** The same value as `location_group`. Returned for `X-API-Version: 1.0.0` and for requests that send no version header. |
| `uses_approval`  | boolean | Whether Basic Approval is enabled for this location                                                                                   |

> **Warning**
>
> **Only active locations are returned.** The active-only filter is applied server-side and cannot be turned off — there is no parameter for listing de-activated locations. If you cache this list (recommended, see [Best Practices](/account-structure/patterns)), treat a location that has disappeared from a later fetch as de-activated rather than deleted, and stop routing new invoices to it.
>
> `is_active` for a single location is available on the response to [`PATCH /v1/accounts/{id}/locations/{location_id}`](/api-reference/api-reference/accounts/patch-accounts-id-locations-location-id).

> **Note**
>
> **Filtering by company:** The locations endpoint returns all locations for an account across all companies, and has no server-side company filter. Use the `company_id` field in the response to select the locations for a specific company client-side. Prefer `company_id` over the `company` name for this — names are not guaranteed unique or stable.

---

## API versioning

The Accounts API supports two versions, selected with the `X-API-Version` request header:

| `X-API-Version` | Behavior                                             |
| --------------- | ---------------------------------------------------- |
| absent          | Same as `1.0.0`                                      |
| `1.0.0`         | Locations return the deprecated `group` field        |
| `1.1.0`         | Locations return `location_group` instead of `group` |

Every response echoes the version that rendered it in its top-level `version` field, so you can confirm which one you got.

> **Warning**
>
> **An unrecognized version falls back to `1.0.0` silently** — it is not an error. If you send `X-API-Version: 2.0.0`, you get the `1.0.0` response shape and `"version": "1.0.0"` in the body, with no warning. Check the `version` field rather than assuming your header was honored.

**Migrating from `group` to `location_group`:**

```bash
curl https://api.ottimate.com/v1/accounts/racc_90832490382904802384/locations \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "X-API-Version: 1.1.0"
```

The two fields carry the same value, so migration is a rename: send the `1.1.0` header and read `location_group`. `group` is still returned for `1.0.0` and unversioned requests.

---

### Groups

A **group** is an optional label applied to a location, used for reporting, permissions, and workflow management within Ottimate. Groups are not a scoping tier for the API: no request is scoped to a group, and nothing is inherited through one.

**Key Characteristics:**

* A group contains 1 or more locations
* A location can only belong to 1 group (or none)
* Groups are optional - locations can exist without being assigned to a group
* Groups are configured through the Ottimate Onboarding Tool, not the API

> **Note**
>
> **There is no Groups API.** Groups are surfaced only as a name on the Location object — `location_group` (or the deprecated `group`) on `GET /v1/accounts/{id}/locations`. There is no endpoint that lists groups, and no `group` parameter on any endpoint to filter or scope by.
>
> To work with groups, read the locations list and group the results client-side by `location_group`. To scope requests to a set of locations, pass the individual location IDs.
>
> The `PATCH` locations endpoint additionally returns `group_id` and `group_name` for the single location it responds with — this is the only place a group ID is exposed.

**Groups in Single-Company Accounts:**

In a single-company account, groups organize locations within the same company:

* **Regional organization** - "North Region" and "South Region" groups organize stores by geography
* **Department-based grouping** - "Corporate Offices" vs "Warehouse Locations"
* **Reporting** - Track AP metrics by location grouping

**Groups in Multi-Company Accounts (Cross-Company Groups):**

In multi-company accounts, groups can span across companies, creating "cross-company groups":

* **Regional organization** - "West Coast" group contains locations from multiple subsidiary companies
* **Cross-company reporting** - Track AP metrics across subsidiaries by region/division
* **Shared management** - A regional manager oversees locations from multiple legal entities