Menu
Cookbooks
API reference
- Overview
- Companies
- Portfolio
- Customers
- Invoices
- Articles
- Suppliers
- Supplier invoices
- Supplier payment files
- Expense claims
- Transactions
- Reconciliation
- Bank accounts
- Journal entries
- Voucher gap explanations
- Fiscal periods
- Accounts
- Fixed assets
- Documents
- Inbox items
- Dimensions
- Employees
- Salary runs
- Reports
- Imports
- Compliance check
- Skatteverket
- Peppol
- Webhooks
- Operations
- Health
- API key
Dimensions
Cost-centre / project dimensions and their values for tagging journal lines.
Endpoints
GET/api/v1/companies/:companyId/dimensions: List dimensions (kostnadsställe/projekt) with their values.GET/api/v1/companies/:companyId/dimensions/retag-log: Read the history of dimension tag changes on posted lines, newest first.GET/api/v1/companies/:companyId/dimensions/rules: List the account dimension rules (required, default or fixed dimension per account).POST/api/v1/companies/:companyId/dimensions: Create a custom dimension (e.g. Avdelning, Kund, Fordon).POST/api/v1/companies/:companyId/dimensions/:id/values: Create a dimension value (kostnadsställe/projekt code).POST/api/v1/companies/:companyId/dimensions/retag: Change the dimension tags (kostnadsställe, projekt) on posted journal lines.POST/api/v1/companies/:companyId/dimensions/rules: Require, pre-fill or pin a dimension value on an account.PATCH/api/v1/companies/:companyId/dimensions/:id: Rename, archive or reorder a dimension.PATCH/api/v1/companies/:companyId/dimensions/:id/values/:valueId: Update a dimension value (rename, archive, set start/end date).PATCH/api/v1/companies/:companyId/dimensions/rules/:id: Change, pause or resume an account dimension rule.DELETE/api/v1/companies/:companyId/dimensions/:id: Delete a custom dimension nobody has booked on.DELETE/api/v1/companies/:companyId/dimensions/:id/values/:valueId: Delete an unreferenced dimension value.DELETE/api/v1/companies/:companyId/dimensions/rules/:id: Delete an account dimension rule.
GET /api/v1/companies/:companyId/dimensions
dimensions.list · scope reports:read
List dimensions (kostnadsställe/projekt) with their values.
Returns the company's dimension registry: SIE #DIM entries keyed by sie_dim_no (1 = Kostnadsställe, 6 = Projekt; both always exist): with the registered values (#OBJEKT) nested under each dimension. Dimensions are ordered by sort_order, values by code. Line-level tags on journal entries reference these values as {"<sie_dim_no>":"<code>"} in the dimensions map.
Use when: You need the valid dimension value codes before tagging journal-entry lines with a cost centre or project, or you are rendering a dimension picker.
Don't use for: Filtering reports (pass the dimension filter to the report endpoints once available) or reading which lines carry a tag (read the journal entries themselves).
Pitfalls
- Dimension value codes are STRINGS and case-sensitive: "P001", not 1.
- sie_dim_no is the key used in journal_entry_lines.dimensions, NOT the dimension row id.
- is_active=false values are historical (archived): do not tag new lines with them.
- resets_annually=true (dim 1) means balances reset each fiscal year; dim 6 (projekt) accumulates across years.
Risk: low · Idempotent: yes · Reversible: no · Dry-run supported: no
Response fields
| Name | Type |
|---|---|
dimensions[].id | string |
dimensions[].sie_dim_no | number |
dimensions[].name | string |
dimensions[].parent_sie_dim_no | number | null |
dimensions[].resets_annually | boolean |
dimensions[].is_system | boolean |
dimensions[].is_active | boolean |
dimensions[].sort_order | number |
dimensions[].values | object[] |
Example response
{
"data": {
"dimensions": [
{
"id": "0e9c…",
"sie_dim_no": 1,
"name": "Kostnadsställe",
"parent_sie_dim_no": null,
"resets_annually": true,
"is_system": true,
"is_active": true,
"sort_order": 10,
"values": [
{
"id": "a8f1…",
"code": "BUTIK",
"name": "Butiken",
"is_active": true,
"start_date": null,
"end_date": null
}
]
}
]
},
"meta": {
"request_id": "req_…",
"api_version": "2026-05-12"
}
}
GET /api/v1/companies/:companyId/dimensions/retag-log
dimensions.retag-log · scope reports:read
Read the history of dimension tag changes on posted lines, newest first.
The immutable trail behind every retag of a posted line: the tags before and after, who made the change, when and why. Filter by journal_entry_id and/or line_id, or read the whole company's history. The log outlives its lines (an undone SIE import deletes the verifikat, not the history). Paged with limit and offset: total_count, has_more and next_offset say what is left.
Use when: Explaining why a line carries its project or cost centre, or auditing who changed dimension tags on booked history.
Don't use for: Rättelser of amounts, accounts or texts (GET /journal-entries/{id}/rattelse-log) or the company-wide change history (GET /audit-trail).
Pitfalls
- An id of another company matches nothing: the answer is an empty page, not a 404.
- Tags set when the line was booked are not here: only later changes are.
Risk: low · Idempotent: yes · Reversible: no · Dry-run supported: no
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
journal_entry_id | string | no | Only changes to lines of this verifikat. |
line_id | string | no | Only changes to this line. |
limit | number | no | Page size, 1-200 (default 50). |
offset | number | no | Rows to skip (next_offset of the previous page). |
Response fields
| Name | Type |
|---|---|
entries | object[] |
count | number |
total_count | number |
has_more | boolean |
next_offset | number (optional) |
Example response
{
"data": {
"entries": [
{
"retag_log_id": "1c2d…",
"journal_entry_id": "7b3a…",
"line_id": "9f1c…",
"old_dimensions": {
"1": "KS01"
},
"new_dimensions": {
"1": "KS01",
"6": "P001"
},
"actor": "9d2b…",
"reason": "Projektet saknades på fakturan",
"created_at": "2026-09-28T09:14:00Z"
}
],
"count": 1,
"total_count": 1,
"has_more": false
},
"meta": {
"request_id": "req_…",
"api_version": "2026-05-12"
}
}
GET /api/v1/companies/:companyId/dimensions/rules
dimension-rules.list · scope reports:read
List the account dimension rules (required, default or fixed dimension per account).
Returns the company's per-account dimension policy, by account number: required (the account cannot be posted without a value for the dimension; the booking answers MANDATORY_DIMENSION_MISSING), default (the value is filled in on a line that has none) and fixed (the value always applies, over what the line says). Paused rules (is_active false) are listed too; they are not enforced.
Use when: A booking was refused with MANDATORY_DIMENSION_MISSING, or before posting to an account, to know which dimensions its lines need.
Don't use for: The dimensions and their values themselves (GET /dimensions).
Pitfalls
- account_number filters on one exact account: a STRING of 4 digits, "4010".
- A company without rules gets an empty list: dimensions are then never required.
Risk: low · Idempotent: yes · Reversible: no · Dry-run supported: no
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
account_number | string | no | Only the rules of this account. |
Response fields
| Name | Type | Description |
|---|---|---|
rules[].account_dimension_rule_id | string | |
rules[].account_number | string | |
rules[].dimension_id | string | |
rules[].sie_dim_no | number | |
rules[].dimension_name | string | |
rules[].rule_type | "required" | "default" | "fixed" | |
rules[].value_id | string | null | The dimension value a default or fixed rule applies; null for required. |
rules[].value_code | string | null | |
rules[].value_name | string | null | |
rules[].is_active | boolean |
Example response
{
"data": {
"rules": [
{
"account_dimension_rule_id": "5b7e…",
"account_number": "4010",
"dimension_id": "0e9c…",
"sie_dim_no": 6,
"dimension_name": "Projekt",
"rule_type": "required",
"value_id": null,
"value_code": null,
"value_name": null,
"is_active": true
}
]
},
"meta": {
"request_id": "req_…",
"api_version": "2026-05-12"
}
}
POST /api/v1/companies/:companyId/dimensions
dimensions.create · scope bookkeeping:write
Create a custom dimension (e.g. Avdelning, Kund, Fordon).
Adds a dimension to the registry (SIE #DIM). Omit sie_dim_no and the next free number from 20 is used: SIE reserves 1-19 for standardized meanings (1 Kostnadsställe, 6 Projekt, 7 Anställd, ...). parent_sie_dim_no declares an #UNDERDIM hierarchy and must name an existing dimension. Add values afterwards with POST /dimensions/{id}/values. Idempotent. Dry-runnable.
Use when: The company wants to follow up on something beyond kostnadsställe and projekt, and no existing dimension fits.
Don't use for: Adding a cost centre or project code: those are values of the system dimensions 1 and 6 (POST /dimensions/{id}/values).
Pitfalls
- An explicit sie_dim_no that is taken returns 409 DIMENSION_NUMBER_TAKEN; omit it to get the next free number.
- Numbers 1-19 have standardized SIE meanings: only use one when the dimension really is that (e.g. 7 Anställd).
- resets_annually defaults to true (balances reset each fiscal year, like kostnadsställe); set false for things that accumulate, like projekt.
Risk: low · Idempotent: yes · Reversible: yes · Dry-run supported: yes
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
dry_run | string | no | true (any case) previews the write without committing it, like the X-Dry-Run: true header. Any other value commits. |
Request body
| Name | Type | Required | Description |
|---|---|---|---|
name | string | yes | Display name, e.g. "Avdelning". |
sie_dim_no | number | no | SIE dimension number. Omit for the next free number from 20. |
resets_annually | boolean | no | Whether balances reset each fiscal year. Default true. |
parent_sie_dim_no | number | null | no | Parent dimension number (#UNDERDIM). |
Response fields
| Name | Type |
|---|---|
dimension | object |
Example request
{
"name": "Avdelning"
}
Example response
{
"data": {
"dimension": {
"id": "3c1d…",
"sie_dim_no": 20,
"name": "Avdelning",
"parent_sie_dim_no": null,
"resets_annually": true,
"is_system": false,
"is_active": true,
"sort_order": 100
}
},
"meta": {
"request_id": "req_…",
"api_version": "2026-05-12"
}
}
POST /api/v1/companies/:companyId/dimensions/:id/values
dimensions.values.create · scope bookkeeping:write
Create a dimension value (kostnadsställe/projekt code).
Registers a new value (SIE #OBJEKT) under a dimension: e.g. a new project code under dimension 6. Requires Idempotency-Key (UUID). Supports ?dry_run=true to validate the code format without committing. The :id path segment is the dimension row id (from GET …/dimensions), not the sie_dim_no. Send is_active=false to create the value archived. start_date/end_date are only allowed on accumulating dimensions (resets_annually=false, e.g. dim 6 Projekt). Duplicate codes within the dimension return 409 DIMENSION_VALUE_DUPLICATE_CODE.
Use when: A voucher or invoice references a cost centre / project code that does not exist yet and the user has confirmed it should be created.
Don't use for: Renaming or archiving an existing value (PATCH …/dimensions/{id}/values/{valueId}). Tagging lines: pass the dimensions map on the journal-entry line instead.
Pitfalls
- Idempotency-Key is mandatory: calls without it return 400 VALIDATION_ERROR.
- The :id segment is the dimension UUID, not the SIE dimension number.
- Codes are limited to the strict Fortnox charset (A-Ö, digits, _, +, -; max 20 chars) even though historical imported codes may be looser.
- code is immutable after creation: there is no rename in v1; create the correct code and archive the wrong one.
- start_date/end_date return 400 DIMENSION_VALUE_DATES_NOT_ALLOWED on resets_annually dimensions (dim 1 Kostnadsställe).
Risk: low · Idempotent: yes · Reversible: yes · Dry-run supported: yes
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
dry_run | string | no | true (any case) previews the write without committing it, like the X-Dry-Run: true header. Any other value commits. |
Request body
| Name | Type | Required |
|---|---|---|
code | string | yes |
name | string | yes |
is_active | boolean | no |
start_date | string | null | no |
end_date | string | null | no |
Response fields
| Name | Type |
|---|---|
id | string | null |
dimension_id | string |
code | string |
name | string |
is_active | boolean |
start_date | string | null |
end_date | string | null |
created_at | string | null |
Example request
{
"code": "P001",
"name": "Villa Almgren tak"
}
Example response
{
"data": {
"id": "0e9c…",
"dimension_id": "a8f1…",
"code": "P001",
"name": "Villa Almgren tak",
"is_active": true,
"start_date": null,
"end_date": null,
"created_at": "2026-07-02T12:00:00Z"
},
"meta": {
"request_id": "req_…",
"api_version": "2026-05-12"
}
}
POST /api/v1/companies/:companyId/dimensions/retag
dimensions.retag-lines · scope bookkeeping:write
Change the dimension tags (kostnadsställe, projekt) on posted journal lines.
Sets dimension tags on lines of posted verifikat, the one thing about a posted line that may change: amounts, accounts and texts never do. mode merge (default) sets the pairs in dimensions and keeps every other dimension the line carries; replace makes the line's tags exactly dimensions. Each line is its own transaction through the audited retag path, which writes an immutable before/after row with the reason (GET /dimensions/retag-log) and refuses a line in a closed or locked period, on or before the bookkeeping lock date, on a draft, or with a code that is not an active registry value. Partial success is success: refused lines are listed in failed. Idempotent. Dry-runnable: the dry run shows each line's tags before and after.
Use when: Posted lines lack a project or cost centre, or carry the wrong one, and you know the line ids (lines[].id of GET /journal-entries/{id}).
Don't use for: Changing amounts, accounts or dates (a rättelse: POST /journal-entries/{id}/correct), tagging lines of a draft (edit the draft), or clearing every tag of a line (the dashboard only).
Pitfalls
- Codes are STRINGS keyed by sie_dim_no: {"6": "P001"}, and each must be an active value in the registry (GET /dimensions).
- merge keeps the line's other tags, and those must still be active registry values too: a line carrying an archived code is refused until the code is reactivated or replace is used.
- At most 500 line ids per call; the reason (3-500 characters) is stored per line.
- When every line is refused the call answers 400 DIMENSION_RETAG_FAILED with details.failed; otherwise it answers 200 with the refused lines in failed.
Risk: medium · Idempotent: yes · Reversible: yes · Dry-run supported: yes
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
dry_run | string | no | true (any case) previews the write without committing it, like the X-Dry-Run: true header. Any other value commits. |
Request body
| Name | Type | Required | Description |
|---|---|---|---|
line_ids | string[] | yes | |
dimensions | object | yes | |
reason | string | yes | |
mode | "merge" | "replace" | no | merge (default) keeps each line's other dimensions; replace makes its tags exactly dimensions. |
Response fields
| Name | Type | Description |
|---|---|---|
retagged | number | Lines whose tags changed. |
unchanged | number | Lines that already carried the resulting tags. |
failed_count | number | |
failed | object[] | |
mode | "merge" | "replace" |
Example request
{
"line_ids": [
"9f1c…",
"9f1d…"
],
"dimensions": {
"6": "P001"
},
"reason": "Projektet saknades på fakturan"
}
Example response
{
"data": {
"retagged": 2,
"unchanged": 0,
"failed_count": 0,
"failed": [],
"mode": "merge"
},
"meta": {
"request_id": "req_…",
"api_version": "2026-05-12"
}
}
POST /api/v1/companies/:companyId/dimensions/rules
dimension-rules.create · scope bookkeeping:write
Require, pre-fill or pin a dimension value on an account.
Adds a rule for one account and one dimension. required: posting the account without a value for the dimension is refused (MANDATORY_DIMENSION_MISSING), drafts may still be incomplete. default: value_id is filled in on a line of the account that carries no value for the dimension. fixed: value_id always applies, over what the line says. Takes effect for bookings from now on; posted verifikat are never changed. Idempotent. Dry-runnable.
Use when: Every booking on an account should carry a project or cost centre, or an account always belongs to the same one.
Don't use for: Tagging lines that are already posted: a rule only acts on bookings made after it.
Pitfalls
- required takes no value_id; default and fixed must name one (400 VALIDATION_ERROR otherwise).
- value_id must be an active value of that same dimension: 404 DIMENSION_VALUE_NOT_FOUND or 400 DIMENSION_VALUE_ARCHIVED.
- The account must be an active account in the chart: 404 ACCOUNT_NOT_FOUND.
- One rule per account and dimension: a second answers 409 DIMENSION_RULE_EXISTS; change the existing rule with PATCH instead.
Risk: low · Idempotent: yes · Reversible: yes · Dry-run supported: yes
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
dry_run | string | no | true (any case) previews the write without committing it, like the X-Dry-Run: true header. Any other value commits. |
Request body
| Name | Type | Required | Description |
|---|---|---|---|
account_number | string | yes | |
dimension_id | string | yes | The dimension row id (dimension_id from the dimension list), not its sie_dim_no. |
rule_type | "required" | "default" | "fixed" | yes | required: no posting on the account without a value; default: pre-filled when a line has none; fixed: always applied. |
value_id | string | no | default/fixed: the dimension value to apply (dimension_value_id). Omit for required. |
is_active | boolean | no | false saves the rule paused. Default true. |
Response fields
| Name | Type |
|---|---|
rule | object |
Example request
{
"account_number": "4010",
"dimension_id": "0e9c…",
"rule_type": "required"
}
Example response
{
"data": {
"rule": {
"account_dimension_rule_id": "5b7e…",
"account_number": "4010",
"dimension_id": "0e9c…",
"sie_dim_no": 6,
"dimension_name": "Projekt",
"rule_type": "required",
"value_id": null,
"value_code": null,
"value_name": null,
"is_active": true
}
},
"meta": {
"request_id": "req_…",
"api_version": "2026-05-12"
}
}
PATCH /api/v1/companies/:companyId/dimensions/:id
dimensions.update · scope bookkeeping:write
Rename, archive or reorder a dimension.
Sparse update of a dimension: name, is_active (false archives it, hiding it from pickers while history keeps its tags) and sort_order. The system dimensions 1 (Kostnadsställe) and 6 (Projekt) can be archived and reordered but not renamed. sie_dim_no is immutable. Idempotent. Dry-runnable.
Use when: A dimension needs a clearer name, should stop being offered for new tags, or should move in the pickers.
Don't use for: Changing a value (use PATCH /dimensions/{id}/values/{valueId}) or removing a dimension (DELETE).
Pitfalls
- Renaming a system dimension returns 400 DIMENSION_SYSTEM_RENAME.
- At least one of name, is_active, sort_order must be sent.
Risk: low · Idempotent: yes · Reversible: yes · Dry-run supported: yes
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
dry_run | string | no | true (any case) previews the write without committing it, like the X-Dry-Run: true header. Any other value commits. |
Request body
| Name | Type | Required | Description |
|---|---|---|---|
name | string | no | |
is_active | boolean | no | false archives the dimension. |
sort_order | number | no |
Response fields
| Name | Type |
|---|---|
id | string |
sie_dim_no | number |
name | string |
parent_sie_dim_no | number | null |
resets_annually | boolean |
is_system | boolean |
is_active | boolean |
sort_order | number |
Example request
{
"is_active": false
}
Example response
{
"data": {
"id": "3c1d…",
"sie_dim_no": 20,
"name": "Avdelning",
"parent_sie_dim_no": null,
"resets_annually": true,
"is_system": false,
"is_active": false,
"sort_order": 100
},
"meta": {
"request_id": "req_…",
"api_version": "2026-05-12"
}
}
PATCH /api/v1/companies/:companyId/dimensions/:id/values/:valueId
dimensions.values.update · scope bookkeeping:write
Update a dimension value (rename, archive, set start/end date).
Sparse update of a dimension value (SIE #OBJEKT): name, is_active (false = archive), start_date, end_date. code is immutable: renaming a code would orphan every journal line tagged with it; create a new value and archive the old one instead. Dates are only allowed on accumulating dimensions (resets_annually=false, e.g. dim 6 Projekt): use end_date to close a finished project. Idempotent (mandatory Idempotency-Key) and dry-runnable.
Use when: You need to rename a project/cost-centre, mark a finished project with an end date, or archive (is_active=false) a value that should no longer be used on new lines.
Don't use for: Changing the code (immutable: create + archive instead). Removing an unused value entirely (use DELETE). Tagging lines (pass dimensions on the journal-entry line or invoice).
Pitfalls
- Idempotency-Key is mandatory.
- The :id segment is the dimension UUID and :valueId the value UUID (both from GET …/dimensions), not SIE numbers or codes.
- start_date/end_date return 400 DIMENSION_VALUE_DATES_NOT_ALLOWED on resets_annually dimensions (dim 1 Kostnadsställe).
- Archived values (is_active=false) still appear in GET …/dimensions and remain valid on historical lines; they are only blocked for NEW tags.
Risk: low · Idempotent: yes · Reversible: yes · Dry-run supported: yes
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
dry_run | string | no | true (any case) previews the write without committing it, like the X-Dry-Run: true header. Any other value commits. |
Request body
| Name | Type | Required |
|---|---|---|
name | string | no |
is_active | boolean | no |
start_date | string | null | no |
end_date | string | null | no |
Response fields
| Name | Type |
|---|---|
id | string |
dimension_id | string |
code | string |
name | string |
is_active | boolean |
start_date | string | null |
end_date | string | null |
Example request
{
"end_date": "2026-08-31",
"is_active": false
}
Example response
{
"data": {
"id": "0e9c…",
"dimension_id": "a8f1…",
"code": "P001",
"name": "Villa Almgren tak",
"is_active": false,
"start_date": null,
"end_date": "2026-08-31"
},
"meta": {
"request_id": "req_…",
"api_version": "2026-05-12"
}
}
PATCH /api/v1/companies/:companyId/dimensions/rules/:id
dimension-rules.update · scope bookkeeping:write
Change, pause or resume an account dimension rule.
Sparse update of a rule: rule_type, value_id and is_active (false pauses the rule without losing it). The value rule holds for the rule as it will be: switching to required needs value_id null in the same call, switching to default or fixed needs a value. The account and the dimension of a rule are fixed: delete it and create another to move it. Idempotent. Dry-runnable.
Use when: A rule should apply another value, change type, or stop being enforced for a while.
Don't use for: Removing a rule for good (DELETE /dimensions/rules/{id}).
Pitfalls
- At least one of rule_type, value_id, is_active must be sent.
- A rule of another company answers 404 DIMENSION_RULE_NOT_FOUND.
Risk: low · Idempotent: yes · Reversible: yes · Dry-run supported: yes
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
dry_run | string | no | true (any case) previews the write without committing it, like the X-Dry-Run: true header. Any other value commits. |
Request body
| Name | Type | Required | Description |
|---|---|---|---|
rule_type | "required" | "default" | "fixed" | no | required: no posting on the account without a value; default: pre-filled when a line has none; fixed: always applied. |
value_id | string | null | no | The value to apply; null clears it (required rules carry none). |
is_active | boolean | no | false pauses the rule without losing it. |
Response fields
| Name | Type |
|---|---|
rule | object |
Example request
{
"is_active": false
}
Example response
{
"data": {
"rule": {
"account_dimension_rule_id": "5b7e…",
"account_number": "4010",
"dimension_id": "0e9c…",
"sie_dim_no": 6,
"dimension_name": "Projekt",
"rule_type": "required",
"value_id": null,
"value_code": null,
"value_name": null,
"is_active": false
}
},
"meta": {
"request_id": "req_…",
"api_version": "2026-05-12"
}
}
DELETE /api/v1/companies/:companyId/dimensions/:id
dimensions.delete · scope bookkeeping:write
Delete a custom dimension nobody has booked on.
Removes a custom dimension and its values. Refused for the system dimensions and for any dimension whose number is tagged on a posted or reversed verifikat line (BFL 7 kap: booked history is never pulled out from under a verifikat). Archive it with PATCH is_active=false instead. Idempotent. Dry-runnable.
Use when: A dimension was created by mistake and nothing has been booked on it.
Don't use for: Retiring a dimension that has been used: archive it (PATCH is_active=false).
Pitfalls
- A dimension used on any posted line returns 409 DIMENSION_REFERENCED naming it.
- System dimensions return 400 DIMENSION_SYSTEM_DELETE.
Risk: medium · Idempotent: yes · Reversible: no · Dry-run supported: yes
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
dry_run | string | no | true (any case) previews the write without committing it, like the X-Dry-Run: true header. Any other value commits. |
Response fields
| Name | Type |
|---|---|
deleted | true |
dimension_id | string |
Example response
{
"data": {
"deleted": true,
"dimension_id": "3c1d…"
},
"meta": {
"request_id": "req_…",
"api_version": "2026-05-12"
}
}
DELETE /api/v1/companies/:companyId/dimensions/:id/values/:valueId
dimensions.values.delete · scope bookkeeping:write
Delete an unreferenced dimension value.
Hard-deletes a dimension value (SIE #OBJEKT) that no journal line references. Values used on posted or reversed verifikat are retained for the BFL 7-year archive and cannot be deleted: the DB trigger blocks it and this endpoint returns 409 DIMENSION_VALUE_REFERENCED. Archive those instead (PATCH is_active=false). Requires Idempotency-Key.
Use when: A project/cost-centre code was created by mistake (typo, duplicate) and has never been used on any booking.
Don't use for: Retiring a project that has bookings: PATCH is_active=false (and optionally end_date) instead. Deleting a whole dimension (not supported).
Pitfalls
- Idempotency-Key is mandatory.
- 409 DIMENSION_VALUE_REFERENCED means the value is used on booked verifikat: it can never be deleted, only archived.
- Deletion is permanent: the code can be re-created afterwards, but the old row id is gone.
Risk: medium · Idempotent: yes · Reversible: no · Dry-run supported: no
Response fields
| Name | Type |
|---|---|
deleted | true |
id | string |
Example response
{
"data": {
"deleted": true,
"id": "0e9c…"
},
"meta": {
"request_id": "req_…",
"api_version": "2026-05-12"
}
}
DELETE /api/v1/companies/:companyId/dimensions/rules/:id
dimension-rules.delete · scope bookkeeping:write
Delete an account dimension rule.
Removes the rule: from then on the account neither requires nor fills in that dimension. Nothing booked changes. Pausing it instead keeps the configuration (PATCH is_active=false). Idempotent. Dry-runnable.
Use when: A rule no longer applies.
Don't use for: A short pause (PATCH is_active=false).
Pitfalls
- A rule of another company, or one already deleted, answers 404 DIMENSION_RULE_NOT_FOUND.
Risk: low · Idempotent: yes · Reversible: yes · Dry-run supported: yes
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
dry_run | string | no | true (any case) previews the write without committing it, like the X-Dry-Run: true header. Any other value commits. |
Response fields
| Name | Type |
|---|---|
deleted | true |
account_dimension_rule_id | string |
Example response
{
"data": {
"deleted": true,
"account_dimension_rule_id": "5b7e…"
},
"meta": {
"request_id": "req_…",
"api_version": "2026-05-12"
}
}