Eviworx
Docs

Cost Centers API

The Cost Centers API manages cost centers as central master data — with code/name, lifecycle (DRAFT/ACTIVE/CLOSED), validity period, budget, hierarchy (cost-center groups/rollups), owner and ERP sync. Cost centers are assigned to assets, contracts and licenses via costCenterId and form the basis of the cost reports.

🏷️
Features
✓ Lifecycle DRAFT → ACTIVE → CLOSED
✓ Validity period (validFrom/validUntil)
✓ Hierarchy (parentId, rollups)
✓ Budget & Owner
✓ ERP sync (idempotent import)
✓ Merge (consolidate cost centers)
✓ Soft delete (CLOSED)
✓ Assignment to asset/contract/license

Authentication & Permissions

Each action has its own permission under costCenters.*, which applies equally to all cost centers. There are no restrictions to individual cost centers or their owners. See User Management & RBAC.

Action Permission
View / list / searchcostCenters.view
See and set budget amountscostCenters.viewBudget
CreatecostCenters.create
UpdatecostCenters.update
Delete (soft delete)costCenters.delete
MergecostCenters.merge (default: admin)
Import / ERP synccostCenters.import

Users and API keys: All reads (list, search, detail, activity trail) are open to logged-in users and API keys holding costCenters.view alike — so the ERP key that may import can also read the list for reconciliation. Create, update, delete and merge require a logged-in user; an X-API-Key is rejected there with 403 ("This operation requires a logged-in user account, not an API key"). POST /api/cost-centers/import accepts both. This keeps manual maintenance attributable to a person (audit), while reads and ERP sync run via key.

Budget amounts are protected separately: Without costCenters.viewBudget every response contains budget: null — list, detail and also the create/update response. The field is always present, only the value is withheld. Rejected with 403 COST_CENTER_BUDGET_FORBIDDEN: sorting by budget (?sort=budget:…, the order would reveal the amounts) and budget in the create/update body. In the import the affected row is reported as an error and the import continues. Whoever may not see the budget may not set it either. In the cost report, reports.viewBudget controls visibility of the same amounts.

Endpoints Overview

Method Endpoint Description Permission
GET/api/cost-centersPaginated, filter-spec-driven listcostCenters.view
GET/api/cost-centers/search?q=&per=Search for select fields: only assignable (ACTIVE and valid) cost centers. Only q and per are accepted; other parameters (e.g. limit) → 400costCenters.view
GET/api/cost-centers/:idSingle cost centercostCenters.view
GET/api/cost-centers/:id/activitiesActivity trail of the cost centercostCenters.view
POST/api/cost-centersCreate cost center (source=MANUAL)costCenters.create
PATCH/api/cost-centers/:idUpdate cost centercostCenters.update
DELETE/api/cost-centers/:idSoft delete (status → CLOSED) → 204; with active child cost centers: 400 COST_CENTER_HAS_CHILDREN (+ details.children)costCenters.delete
POST/api/cost-centers/:id/mergeReassign references (assets/contracts/licenses) AND children to targetId, close the source — all in one step. The target must be assignable and must not be a descendant of the source. With two concurrent, opposing merges one loses with 409 COST_CENTER_MERGE_CONFLICTcostCenters.merge
POST/api/cost-centers/importIdempotent upsert (ERP sync, source=SYNCED) — user OR API keycostCenters.import

Fields

Field Type Description
codeString (1–50)Cost-center number/code — unique, required
nameString (1–200)Display name — required
descriptionString? (≤2000)Description
colorString (#rrggbb)Badge color (default #3b82f6)
sortOrderIntSort order (settable via import only, not a create/update input)
statusenumDRAFT, ACTIVE, CLOSED
validFrom / validUntilDateTime?Validity period (controls assignability); validFrom ≤ validUntil enforced (400 COST_CENTER_VALIDITY_INVALID)
ownerIdString?Responsible user — must exist and not be archived (400 COST_CENTER_OWNER_NOT_FOUND)
budgetDecimal? (14,2)Budget (in system currency — see below)
parentIdString?Parent cost center (hierarchy/rollups). Deleting does NOT tidy up the hierarchy — only the merge reattaches children, so restructuring stays a deliberate decision
externalIdString? (≤100)Stable ERP key (SAP/DATEV) — unique
sourceenumMANUAL, SYNCED (read-only, set by the system)

Currency: Cost centers have no own currency field — budget is interpreted in the global system currency (general settings: systemCurrency). No conversion takes place. See Settings & Global Search API.

Create Cost Center

POST /api/cost-centers
{
  "code": "CC-1000",
  "name": "IT Operations",
  "description": "Betrieb & Infrastruktur",
  "color": "#3b82f6",
  "status": "ACTIVE",
  "validFrom": "2026-01-01T00:00:00Z",
  "ownerId": "clx-user-id",
  "budget": 250000.00,
  "parentId": "clx-parent-cost-center-id"
}

Response (201 Created)

{
  "id": "clx...",
  "code": "CC-1000",
  "name": "IT Operations",
  "status": "ACTIVE",
  "color": "#3b82f6",
  "source": "MANUAL",
  "createdAt": "2026-06-18T08:00:00.000Z"
}

Via this route source is always MANUAL. SYNCED records are created exclusively through the import/ERP-sync endpoint.

ERP Import (Sync)

Idempotent upsert keyed by externalId (if present), else code. Imported records are flagged source=SYNCED. A previously soft-deleted code is restored on re-import. Up to 1000 items per request. This endpoint accepts both user and API-key actors.

POST /api/cost-centers/import
X-API-Key: <your-api-key>
{
  "items": [
    {
      "code": "CC-1000",
      "name": "IT Operations",
      "externalId": "SAP-1000",
      "status": "ACTIVE",
      "budget": 250000.00
    },
    {
      "code": "CC-2000",
      "name": "Facility Management",
      "externalId": "SAP-2000",
      "status": "ACTIVE"
    }
  ]
}

Note: parentId (cuid) is optional; linking the hierarchy by parent code is intentionally NOT supported here (use update/UI for that).

Lifecycle & Assignability

Status Description
DRAFTDraft — not yet assignable
ACTIVEActive — assignable within the validity period
CLOSEDClosed (soft delete) — no longer assignable

Cost centers are assigned to assets, contracts and licenses via the costCenterId field. Assignability is validated server-side: only ACTIVE cost centers within their validity period are assignable — a CLOSED or expired cost center returns 400 Bad Request. The assignment is a plain field on the respective record, not a link via the linking API.

Assets API →
costCenterId on assets
Contracts & Licenses API →
costCenterId on contracts/licenses
Reports API →
Cost reports per cost center