> ## Documentation Index
> Fetch the complete documentation index at: https://docs.filexpress.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Cost centers

> Query, create, edit and delete cost centers from an external system

| Permission | Endpoints |
| - | - |
| `accounting.cost_centers` | `GET /accounting/cost-centers`, `GET /accounting/cost-centers/{id}`, `GET /accounting/cost-center-distributions`, `GET /accounting/cost-center-distributions/{id}` |
| `accounting.cost_centers.write` | `POST /accounting/cost-centers`, `PUT /accounting/cost-centers/{id}`, `DELETE /accounting/cost-centers/{id}`, `POST /accounting/cost-center-distributions`, `PUT /accounting/cost-center-distributions/{id}`, `DELETE /accounting/cost-center-distributions/{id}` |

> [!NOTE]
>
> These are **explicit** permissions: they must be checked one by one in **External integrations**; existing integrations do not receive them automatically.

Cost centers are hierarchical (a center can have sub-centers). Their `id` values are sent as `cost_center_id` in [journal entries](https://docs.filexpress.app/en/docs/api/contabilidad/partidas.md), [sales](https://docs.filexpress.app/en/docs/api/facturacion/ventas.md), [purchases](https://docs.filexpress.app/en/docs/api/facturacion/compras.md), [collections](https://docs.filexpress.app/en/docs/api/cuentas-por-cobrar.md#register-payment), [payments](https://docs.filexpress.app/en/docs/api/cuentas-por-pagar.md#register-payment), [transfers](https://docs.filexpress.app/en/docs/api/inventario.md#complete-a-transfer) and [payrolls](https://docs.filexpress.app/en/docs/api/rrhh/planillas.md#assign-cost-center), and they serve as a filter in the [financial statements](https://docs.filexpress.app/en/docs/api/contabilidad/reportes.md).

> [!TIP]
>
> Only **leaf** (`is_leaf: true`) and active centers are accepted in documents and journal entries. Parent centers group their sub-centers in reports.

For functional usage (policy per account, how centers reach the journal entry, reassignment), see [Cost centers](https://docs.filexpress.app/en/docs/contabilidad/centros-de-costo.md) in the manual.

## Cost center object

| Field | Description |
| - | - |
| `id` | Identifier. |
| `code` | Code, unique within the company. |
| `name` | Name. |
| `description` | Description (can be `null`). |
| `parent_id` | Parent center (`null` if it is a root center). |
| `level` | Level in the tree (1 = root). |
| `full_code` | Full code including the hierarchy. |
| `is_active` | `false` if deactivated. |
| `is_leaf` | `true` if it has no active sub-centers: it is the only type of center that can be assigned. |

## List cost centers

```http theme={null}
GET /accounting/cost-centers?include_inactive=false
```

**Required permission:** `accounting.cost_centers`

| Parameter | Required | Description |
| - | - | - |
| `include_inactive` | No | `true` to include deactivated centers. By default only active ones. |

Returns the flat list (sorted by code) and the same set as a tree.

```json theme={null}
{
  "status": true,
  "data": {
    "list": [
      { "id": "c3a0...", "code": "01", "name": "Ventas", "description": null, "parent_id": null, "level": 1, "full_code": "01", "is_active": true, "is_leaf": false },
      { "id": "c3a1...", "code": "0101", "name": "Ventas San Salvador", "description": null, "parent_id": "c3a0...", "level": 2, "full_code": "01.0101", "is_active": true, "is_leaf": true }
    ],
    "tree": [
      {
        "id": "c3a0...", "code": "01", "name": "Ventas", "description": null, "parent_id": null, "level": 1, "full_code": "01", "is_active": true, "is_leaf": false,
        "children": [
          { "id": "c3a1...", "code": "0101", "name": "Ventas San Salvador", "description": null, "parent_id": "c3a0...", "level": 2, "full_code": "01.0101", "is_active": true, "is_leaf": true, "children": [] }
        ]
      }
    ]
  }
}
```

## Get cost center

```http theme={null}
GET /accounting/cost-centers/{id}
```

**Required permission:** `accounting.cost_centers`

Responds `200` with the [cost center object](#cost-center-object) inside `data`, or `404` `Cost center not found`.

## Create cost center

```http theme={null}
POST /accounting/cost-centers
```

**Required permission:** `accounting.cost_centers.write`

```json theme={null}
{
  "code": "0102",
  "name": "Ventas Santa Ana",
  "description": "Equipo comercial de occidente",
  "parent_id": "c3a0...",
  "is_active": true
}
```

| Field | Required | Description |
| - | - | - |
| `code` | Yes | Code (max. 50), unique within the company. |
| `name` | Yes | Name (max. 255). |
| `description` | No | Description (max. 1000). |
| `parent_id` | No | Parent center from the same company. Without it, the center is a root. |
| `is_active` | No | Defaults to `true`. |

**Behavior**

* The level (`level`) is calculated from the parent.
* When you create a sub-center, its parent stops being a leaf: it can no longer be assigned to new documents, but it still groups its children in reports.

**Response `201`**: the created [cost center object](#cost-center-object).

## Edit cost center

```http theme={null}
PUT /accounting/cost-centers/{id}
```

**Required permission:** `accounting.cost_centers.write`

Accepts the same fields as creation, all optional: only the ones you send change.

```json theme={null}
{ "name": "Ventas Occidente", "is_active": false }
```

**Behavior**

* If you change `parent_id`, the level of the center and of all its sub-centers is recalculated.
* A center cannot depend on itself or on one of its sub-centers.
* Deactivating a center (`is_active: false`) prevents assigning it to new documents and journal entries; what is already recorded is preserved.

**Response `200`**: the updated [cost center object](#cost-center-object).

## Delete cost center

```http theme={null}
DELETE /accounting/cost-centers/{id}
```

**Required permission:** `accounting.cost_centers.write`

**Behavior**

* If the center has **not** been used in journal entries or documents, it is deleted: `{"deleted": true, "deactivated": false}`.
* If it already has movements or documents, it is not deleted: it is **deactivated** to preserve the history: `{"deleted": false, "deactivated": true}`.
* If it has sub-centers, it responds `422`: delete them or move them to another parent first.

```json theme={null}
{
  "status": true,
  "data": { "deleted": false, "deactivated": true }
}
```

## Distributions

A distribution splits an amount among several leaf centers by percentage (for example, rent 60% Sales and 40% Administration). Its `id` is sent as `cost_center_distribution_id` instead of `cost_center_id` in [journal entries](https://docs.filexpress.app/en/docs/api/contabilidad/partidas.md), [sales](https://docs.filexpress.app/en/docs/api/facturacion/ventas.md), [purchases](https://docs.filexpress.app/en/docs/api/facturacion/compras.md) and [payrolls](https://docs.filexpress.app/en/docs/api/rrhh/planillas.md#assign-cost-center). See [Distributions](https://docs.filexpress.app/en/docs/contabilidad/centros-de-costo.md#distributions) in the manual.

**Usage rules**

* `cost_center_id` and `cost_center_distribution_id` are **mutually exclusive** at the same level (document or line): sending both responds `422` `Indique centro de costo o distribución, no ambos.`.
* The distribution must be **active** and belong to the company.
* When posting, each line of an account that accepts a center is split into one line per center according to the percentage; rounding is adjusted on the part with the highest percentage (60/40 over 100.01 gives 60.01 and 40.00). On accounts with the None policy, a single line without a center is used.
* Changing the percentages does not modify journal entries that were already generated.
* Collections, payments and transfers do not accept distributions.

### Distribution object

```json theme={null}
{
  "id": "d7e2...",
  "business_id": "uuid",
  "code": "ALQ",
  "name": "Alquiler oficina central",
  "is_active": true,
  "lines": [
    { "id": "uuid", "cost_center_id": "c3a1...", "percentage": 60.0, "cost_center": { "id": "c3a1...", "code": "0101", "name": "Ventas San Salvador" } },
    { "id": "uuid", "cost_center_id": "c3b7...", "percentage": 40.0, "cost_center": { "id": "c3b7...", "code": "0201", "name": "Administración" } }
  ],
  "created_at": "2026-10-08T15:00:00.000000Z",
  "updated_at": "2026-10-08T15:00:00.000000Z"
}
```

`percentage` has up to 4 decimals and the sum of the lines is exactly 100. Lines are returned from highest to lowest percentage.

### List distributions

```http theme={null}
GET /accounting/cost-center-distributions?include_inactive=false
```

**Required permission:** `accounting.cost_centers`

| Parameter | Required | Description |
| - | - | - |
| `include_inactive` | No | `true` to include deactivated ones. By default only active ones. |

Returns in `data` the list of [distributions](#distribution-object) sorted by code.

### Get distribution

```http theme={null}
GET /accounting/cost-center-distributions/{id}
```

**Required permission:** `accounting.cost_centers`

Responds `200` with the [distribution](#distribution-object) or `404` `Distribution not found`.

### Create distribution

```http theme={null}
POST /accounting/cost-center-distributions
```

**Required permission:** `accounting.cost_centers.write`

```json theme={null}
{
  "code": "ALQ",
  "name": "Alquiler oficina central",
  "is_active": true,
  "lines": [
    { "cost_center_id": "c3a1...", "percentage": 60 },
    { "cost_center_id": "c3b7...", "percentage": 40 }
  ]
}
```

| Field | Required | Description |
| - | - | - |
| `code` | Yes | Code, unique within the company. |
| `name` | Yes | Name. |
| `is_active` | No | Defaults to `true`. |
| `lines` | Yes | At least 2 `{cost_center_id, percentage}` lines. Leaf centers, active and belonging to the company, with no repeats; percentages greater than 0 that add up to exactly 100. |

**Response `201`**: the created [distribution](#distribution-object).

### Edit distribution

```http theme={null}
PUT /accounting/cost-center-distributions/{id}
```

**Required permission:** `accounting.cost_centers.write`

Accepts the same fields, all optional. If you send `lines`, they **replace** all previous lines (with the same rules). Responds `200` with the updated distribution.

### Delete distribution

```http theme={null}
DELETE /accounting/cost-center-distributions/{id}
```

**Required permission:** `accounting.cost_centers.write`

* Not used in documents or journal entries: it is deleted, `{"deleted": true}`.
* Already used: it is **deactivated** to preserve the history, `{"deleted": false, "deactivated": true}`.

### Distribution errors

`422` `Validation errors` when the code is repeated, there are fewer than 2 lines, the sum is not 100 (`errors.lines`), a center is repeated, belongs to another company, is inactive or has sub-centers (`errors["lines.N.cost_center_id"]`), or a percentage is 0 or negative.

```json theme={null}
{
  "status": false,
  "message": "Validation errors",
  "errors": { "lines": ["Los porcentajes deben sumar exactamente 100 (suman 90)."] }
}
```

## Errors

| Code | Cause |
| - | - |
| `401` | Missing or invalid API key. |
| `403` | The integration does not have the required permission. |
| `404` | `Cost center not found` (nonexistent or belongs to another company). |
| `422` | `Validation errors`; for example `code`: `Ya existe un centro de costos con este código.`, `parent_id`: `El centro de costos padre no existe o no pertenece a esta empresa.` or `Un centro de costos no puede depender de sí mismo ni de un subcentro suyo.` |
| `422` | `El centro de costo tiene subcentros; elimínelos o muévalos primero.` (when deleting). |
| `404` | `Distribution not found` (nonexistent distribution or one that belongs to another company). |

```json theme={null}
{
  "status": false,
  "message": "Validation errors",
  "errors": { "code": "Ya existe un centro de costos con este código." }
}
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.