> For the complete documentation index, see [llms.txt](https://developer.vario-software.de/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developer.vario-software.de/documentation/rest-api/concepts/documents.md).

# Documents

How documents move through their state machine — transitions, states and references.

Documents (*Belege*) — offers, orders, delivery notes, invoices, credit notes — are the ERP's central business objects. They are also the object most integrations get wrong, because a document is not a record you edit freely. It is a **state machine**.

{% hint style="info" %}
This page explains the model. For endpoint details use the [API Reference](https://developer.vario-software.de/api-reference); for field paths use [runtime discovery](https://developer.vario-software.de/documentation/fundamentals/vql#dont-guess-field-paths-ask-the-api).
{% endhint %}

## Everything happens through a transition

There is **no** `DELETE /erp/documents/{id}`, and no endpoint that sets a state directly. Creating, editing, transferring, cancelling and deleting a document are all **transitions**:

```
POST /erp/documents            create — with a transitionId 
PUT  /erp/documents/{id}/states   change state — with a transitionId 
POST /erp/documents/{id}/types    transfer into a follow-up document
```

A transition is more than "from state A to state B". It is identified by the document **category**, an optional **source category** (for takeovers), the **transition**, the **source state** (absent = creation) and the **target state**.

{% hint style="warning" %}
`transitionId` is derived from enum ordinals and is not a stable contract.

Never hard-code it, never store it long-term. Fetch it at runtime, per request. This is the single most common cause of an integration that works today and breaks after an update.
{% endhint %}

## Finding the transition you need

Three questions, three endpoints:

| Question                                        | Call                                                       |
| ----------------------------------------------- | ---------------------------------------------------------- |
| Which documents may I create?                   | `GET /erp/documents/types?qualifiers=SALE&languageCode=de` |
| What can I do with *this* document right now?   | `GET /erp/documents/{id}/states`                           |
| Which follow-up documents can I create from it? | `GET /erp/documents/{id}/types`                            |

Each entry carries the `transitionId` you then send, plus `typeId`, `typeLabel`, `targetState` and `transitionKey`. Of those, `transitionKey` (the transition's name) is the only human-stable identifier — match on that if you need to recognise a specific transition, and take the `transitionId` from the same response.

The **qualifier** narrows the document world; there are only eight, so they are worth knowing: `SALE`, `PURCHASE`, `COMMISSION`, `POINT_OF_SALE`, `SALES_CONTRACT`, `PURCHASE_CONTRACT`, `FABRICATION`, `RMA`.

Categories and transitions, by contrast, number in the dozens and hundreds. Their names follow a grammar — categories are prefixed by counterparty (`CUSTOMER_ORDER`, `SUPPLIER_ORDER`, `POS_*`, `FABRICATION_*`), transitions are either lifecycle verbs (`CREATE`, `BEGIN_EDITING`, `DELETE`, `PUBLISH`, `COPY`) or takeovers in `X_TO_Y` form (`OFFER_TO_ORDER`, `ORDER_TO_DELIVERY`, `DELIVERY_TO_INVOICE`) — but do not work from a list. The valid combinations depend on your installation's configuration; enumerate them.

{% hint style="info" %}
`/{id}/states` and `/{id}/types` are **disjoint** lists. A takeover into another document type never appears under `/states`. If a transition you expect is "missing", you are probably asking the wrong endpoint.
{% endhint %}

{% hint style="warning" %}
`/states` contains a **collective** `ACCEPT` entry with `transitionId: 0`. Don't fire it — a document becomes *accepted* as a side effect of being taken over into a follow-up document, not by a direct call.
{% endhint %}

## Creating a document

Only `transitionId` is required:

```json
POST /erp/documents 
{ 
  "transitionId": "<from GET /erp/documents/types>", 
  "targetTypeId": "<typeId from the same entry>", 
  "document": { "accountId": "12345" } 
}
```

Whether an account is required depends on the category. Ids travel as **strings** throughout.

Two things to get right in the order you do them:

* Header fields such as the document date, default storage or report group are set with `PATCH /erp/documents/{id}` — **before** you add lines.
* POS documents need `posRegisterId` and `cashDrawerId` **flat in** `document` at creation time, not nested.

Leave the document **number** out. It is generated automatically (see below).

## What you may change, and when

Every state carries a classification, and that classification — not the state name — decides what is allowed:

| Classification                          | Meaning                                                |
| --------------------------------------- | ------------------------------------------------------ |
| `EDITABLE`                              | Full edit allowed                                      |
| `INTERNAL_EDITABLE`                     | Only the backend may change it (a workflow is running) |
| `PARTIALLY_TRANSFERRED` / `TRANSFERRED` | Partly or fully taken over into a follow-up            |
| `DELETED`                               | Being deleted                                          |

A full-body update requires `EDITABLE`. On a non-editable document, `PATCH` still works — but only for a small whitelist of fields; anything else is rejected. A document in a workflow state, or deleted, is refused outright.

{% hint style="warning" %}
`HISTORICAL_DATA` is a dead end by design. Imported legacy documents get this state and have *no transitions in or out*. Any write is refused. If you need to process such data, do it before import.
{% endhint %}

Writes participate in **optimistic locking**. The `x-get-leasable-lock`, `x-use-leasable-locks`, `x-refresh-leasable-locks` and `x-release-leasable-locks` request headers, and `x-active-leasable-locks` in the response, are part of the contract — respect them when several clients or users touch the same document.

## Lines

```
POST   /erp/documents/document/{documentId}/line 
POST   /erp/documents/document/{documentId}/multiple_lines 
PUT    /erp/documents/document/{documentId}/line/{lineId} 
DELETE /erp/documents/document/{documentId}/line/{lineId} 
PUT    /erp/documents/document/{documentId}/line/{lineId}/move/{position}
```

{% hint style="warning" %}
Note the **doubled** `/document` segment — a frequent 404.
{% endhint %}

```json
{ "documentLine": { "lineType": "…", "articleId": null, "texts": [] }, 
  "additionalParameters": [] }
```

* Every line write returns the **whole document**, not the line.
* On the document, the collection is called `lines`.
* `PUT …/line/{lineId}` **ignores** a changed position — it forces the stored one. Use `…/move/{position}` (1-based) to reorder.

## Document texts

Two positions: `HEADER_TEXT` and `FOOTER_TEXT`. A text either carries free `content` or references a text template, and `transferableIntoSubsequentDocuments` controls whether it survives a takeover.

Texts can be sent at creation, per line, or on the document header via `PUT /erp/documents`.

Two traps on the header update:

{% stepper %}
{% step %}

### Do not include `lines`

The body must **not** contain `lines` — the request is rejected.
{% endstep %}

{% step %}

### Send the complete `texts` array

The `texts` array is treated as **complete**. A text you omit is **deleted**. Read the existing texts first and send them back in full.
{% endstep %}
{% endstepper %}

When reading, note that deleted texts are included in the response — filter them out.

## How documents reference each other

**The link lives on the lines, not on the document.** A line carries `sourceLine` pointing at the line it came from; the document-level chain is derived from that.

To show a document's history, use `GET /erp/documents/{id}/types/history` — a tree of predecessors and successors with number, date, type, state and totals.

### Partial delivery

Three line fields express it:

| Field               | Meaning                                                                     |
| ------------------- | --------------------------------------------------------------------------- |
| `quantity`          | The line's quantity                                                         |
| `quantityCommitted` | How much has been passed to follow-up documents — **can exceed** `quantity` |
| `complete`          | Whether the line is finally settled                                         |

The predecessor's state is then **recomputed** from those, not set by you: fully taken over → `ACCEPTED`, partly → `PARTIALLY_ACCEPTED`, otherwise back to `SAVED`. While a successor is being edited, the predecessor may show "successor is being edited" instead.

To offer a partial takeover, ask for the open quantities — for example `GET /erp/documents/{id}/lines-to-transfer-to-customer-delivery` — then send the chosen `sourceLineId` + `quantity` pairs in `document.lines` of the takeover call. That is the only way a reduced (or negative) quantity travels along.

### A worked example: order → delivery note

The `ORDER_TO_DELIVERY` transition accepts a `CUSTOMER_ORDER` in state `SAVED`, `PARTIALLY_ACCEPTED` or `PICKING_FINISHED`, and produces a delivery note in `SAVED` with its lines linked back to the order and a fresh document number. Yes — the delivery note is created **with reference to** the order; that reference is what later drives the order's own state.

## Document numbers

**They are generated automatically.** Each document type has a sequence configuration — typically fully automatic, with a length, a pad character and a prefix expression (for example the short year).

Consequences:

* Don't send `number` on create; it is not part of the create body. `externalNumber` is the field for *your* reference.
* A blank number is rejected on save (except for historical imports).
* **Deleting a document returns its number to the pool**, so numbers are not strictly monotonic. Don't derive ordering or counts from them.

Sequence configuration and next/last values can be inspected under `/cmn/sequencer`.

## Deleting

Deletion is the `DELETE` **transition**, and it is allowed from exactly one state: `EDIT`. A saved document must first go through `BEGIN_EDITING`.

It is refused when:

* **active successor documents exist** — take them back or cancel them first (references to already-inactive successors are cleaned up silently);
* **open items with movements exist** — payments have already been recorded against it.

{% hint style="success" %}
After a successful deletion the document may no longer exist, and the call answers `301 Moved Permanently` with an empty body rather than a document. Treat that as success, not as an error.
{% endhint %}

For documents that must remain on record, use cancellation (`CANCELLED`) or dissolution (`DISSOLVED`) instead of deletion — those keep the document and its number.

## Querying documents

Sales documents are queried through the `document.querySalesDocuments` template. It is **already scoped** — restricted to the sales-side qualifiers and excluding internal POS categories — so you cannot widen it; use the purchase or POS template instead.

Do not guess field paths. Enumerate them:

```
GET /cmn/computed-queries/document/querySalesDocuments/fields?filterable=true&depth=2
```

`depth` is what walks into relations. To explore an entity's own attributes and relations, use `GET /cmn/computed-queries/fields?entityIdentifier=…`.

The semantics you usually want: a delivery note that is posted but **not yet invoiced** is one whose state is not `ACCEPTED` (so `SAVED`, or `PARTIALLY_ACCEPTED` when partly invoiced), with lines where `complete` is false and `quantityCommitted` is below `quantity`.

{% hint style="info" %}
There is **no "shipped" flag** on a document. The closest things are line-level shipping and delivery dates, and the document's `published` flag — which means "output has been produced", not "goods have left".
{% endhint %}

## Checklist

* `transitionId` fetched at runtime, never cached
* Right endpoint for the question — `/states` for state changes, `/types` for follow-ups
* Header fields patched **before** lines are added
* `PUT /erp/documents` sent without `lines`, and with the **complete** `texts` array
* Line paths use the doubled `/document` segment
* Number left to the sequencer
* `301` after deletion handled as success
* Leasable-lock headers respected

## Related

* [App Backends](https://developer.vario-software.de/cookbook/app-backends/app-backends) · [Onlineshops and Marketplaces](https://developer.vario-software.de/cookbook/app-backends/onlineshops-and-marketplaces) — order import creates documents
* [VQL](https://developer.vario-software.de/documentation/fundamentals/vql) · [Pagination and filtering](https://developer.vario-software.de/documentation/rest-api/pagination-and-filtering) · [FAQ](https://developer.vario-software.de/documentation/faq)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://developer.vario-software.de/documentation/rest-api/concepts/documents.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
