Appearance
Document elements
Document elements are line items on an Invoice or Offer object.
Locked documents
Once an offer or invoice is locked (is_locked: true, i.e. sent or issued), its line items are frozen. Creating, cloning, reordering or deleting an element, or changing what it charges, fails with 422 Unprocessable Entity and a translated message:
json
// HTTP 422 Unprocessable Entity
{
"message": "This document is locked. Its line items can only be changed again after you mark it as draft."
}These changes stay possible on a locked document:
tag_idandpage_breakof any elementrevenue_account_noof any element, as long as the invoice's booking is not in a closed fiscal year — otherwise the request fails with422and the invoice keeps its booking- on a locked offer, an optional element (
is_optional: true) may be taken over (is_optional: false) or deleted
Sending unchanged values along with an allowed change is fine — only attributes whose value actually changes count. Mark the document as draft first to edit its line items freely.
The same rule applies to the add products to an offer and add products to an invoice endpoints.
Allowed query parameters
Filters
| Filter | Type | Description |
|---|---|---|
search | String | Full-text search |
document_type | String | A document type ( Modules\Salesforce\Invoice, Modules\Salesforce\Offer) |
document_id | String | A document Id (Invoice or Offer) |
Includes
document, vatRate, rate, tag, product.tag, product.unit, account
Create an element
post
/document_element
| Attribute (* required) | Type | Description |
|---|---|---|
document_type * | String | Modules\Salesforce\Invoice, Modules\Salesforce\Offer |
document_id * | String | A document Id (Invoice or Offer) |
name * | String | Position title |
description | String? | Position text |
remark | String? | Position remark |
type * | String | Position type (paragraph, heading, percentage, subtotal) |
unit | String? | Position unit |
quantity | Float? | Position quantity |
amount | Float? | Position amount |
vat_rate_id | String? | VAT Rate ID |
tag_id | String? | Tag ID |
is_external | Boolean | Whether the element is external |
is_excluded_from_budget | Boolean | Whether the element is excluded from budget |
is_optional | Boolean | Whether the element is optional |
discount_type | String? | Discount type enum value |
discount_value | Float? | Discount value |
product_id | String? | A product Id (Product) |
page_break | String? | Page break position (before, after) |
revenue_account_no | String? | Revenue account number |
Example response
json
// HTTP 201 Created
{
// an element object
}Update an element
put
/document_element/{id}
| Attribute (* required) | Type | Description |
|---|---|---|
name | String | Position title |
description | String? | Position text |
remark | String? | Position remark |
type | String | Position type (paragraph, heading, percentage, subtotal) |
unit | String? | Position unit |
quantity | Float? | Position quantity |
amount | Float? | Position amount |
vat_rate_id | String? | VAT Rate ID |
tag_id | String? | Tag ID |
is_external | Boolean | Whether the element is external |
is_excluded_from_budget | Boolean | Whether the element is excluded from budget |
is_optional | Boolean | Whether the element is optional |
discount_type | String? | Discount type enum value |
discount_value | Float? | Discount value |
product_id | String? | A product Id (Product) |
page_break | String? | Page break position (before, after) |
revenue_account_no | String? | Revenue account number |
An element always stays on the document it was created on: document_type and document_id are ignored on update and clone.
Example response
json
// HTTP 200 OK
{
// an element object
}Retrieve an element
get
/document_element/{id}
Example response
json
// HTTP 200 OK
{
"id": "1A5m6kqoVw",
"order": 1,
"name": "Client Meeting",
"type": "paragraph",
"description": "2 Std. Administration à 100.00/Std.",
"remark": "",
"quantity": 2,
"amount": 100,
"vat_rate": 7.7,
"vat_rate_id": "abc123",
"unit": null,
"has_discount": false,
"discount_value": null,
"discount_type": null,
"discount_total": null,
"discount_formatted": null,
"total": 200,
"subtotal_at_position": null,
"percentage": null,
"is_external": false,
"is_optional": false,
"is_excluded_from_budget": false,
"revenue_account_no": "3400",
"account": null,
"data": null,
"page_break": null,
"rate": null,
"tag": null,
"document": {
// A document object (Invoice or Offer)
},
"product": {
// A product object
}
}Delete an element
delete
/document_element/{id}
Example response
json
// HTTP 204 No ContentDelete many elements
post
/document_element/delete-many
Deletes multiple document elements at once. The request body is a JSON array of element ids. Returns 200 OK with deleted and skipped arrays; elements of a locked document are reported under skipped (reason: "document_locked") and left intact — see Bulk delete.
List elements
To list a documents elements, you may include the elements relation when retrieving an Invoice or Offer object.
get
/invoice/{id}?include=elements
Example response
json
// HTTP 200 OK
{
"id": "D4rNdYjlGy",
"number": "RE-01234",
"name": "Jahresretainer 2023",
...
"elements": [
// an array of document elements
]
}