StabisoDocs

API reference

Sales

Operation Summary Scope Idempotency-Key
GET /v1/entities/{entity}/quotes List Quotes read
POST /v1/entities/{entity}/quotes Create Quote draft required
GET /v1/entities/{entity}/sales-orders List Sales Orders read
POST /v1/entities/{entity}/sales-orders Create Sales Order draft required
GET /v1/public/quotes/{token} View Quote Online
POST /v1/public/quotes/{token}/accept Accept Quote Online
POST /v1/public/quotes/{token}/decline Decline Quote Online
GET /v1/public/quotes/{token}/pdf Quote Online Pdf
GET /v1/sales-documents/{document} Get Sales Document read
PATCH /v1/sales-documents/{document} Update Sales Document draft
POST /v1/sales-documents/{document}/accept Accept Quote draft required
POST /v1/sales-documents/{document}/cancel Cancel Sales Document draft
POST /v1/sales-documents/{document}/close Close Sales Order draft
POST /v1/sales-documents/{document}/confirm Confirm Sales Order draft
POST /v1/sales-documents/{document}/decline Decline Quote draft
GET /v1/sales-documents/{document}/history Sales Document History read
POST /v1/sales-documents/{document}/invoice Invoice Sales Order draft required
POST /v1/sales-documents/{document}/links Create Quote Link post
POST /v1/sales-documents/{document}/links/{quote_link}/revoke Revoke Quote Link post
POST /v1/sales-documents/{document}/mark-sent Mark Sales Document Sent post required
GET /v1/sales-documents/{document}/pdf Download a quote or order confirmation read
GET /v1/sales-documents/{document}/revisions/{revision}/pdf Download a version of a quote as the customer got it read
POST /v1/sales-documents/{document}/send Send Sales Document post required

GET /v1/entities/{entity}/quotes

List Quotes

Quotes of an entity, newest first. A draft or sent quote past its valid-until day is expired.

Required scope read (admin implies every scope)
Operation id list_quotes_v1_entities__entity__quotes_get

Parameters

In Name Type Required Description
path entity string yes Entity code, e.g. CH01 (GET /v1/entities).
query status array of "accepted" | "cancelled" | "declined" | "draft" | "expired" | "sent" or null One or more statuses.
query customer string or null Customer code or id.
query q string or null Text in the number, reference or customer.
query limit integer How many to return, newest first; at most 1000. (min 1; max 1000; default 100)

Example request

curl "$ERP_URL/v1/entities/CH01/quotes" \
  -H "Authorization: Bearer $ERP_KEY"

Responses

Status Meaning Body
200 OK array of SalesDocumentOut
401 Missing or invalid API key (unauthorized). ErrorEnvelope
403 The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). ErrorEnvelope
404 The resource does not exist, or belongs to another organization (not_found). ErrorEnvelope
422 Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. HTTPValidationError

Example response (shape)

[
  {
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "kind": "order",
    "number": "string",
    "status": "string",
    "customer": "string",
    "customer_name": "string",
    "customer_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "currency": "string",
    "document_date": "2026-09-21",
    "valid_until": "2026-09-21",
    "customer_reference": "string",
    "opportunity_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "discount_percent": "string",
    "discount_amount": "0.00",
    "net_total": "string",
    "tax_total": "string",
    "total": "string",
    "created_at": "2026-09-21T09:30:00Z",
    "version": 1,
    "revision": 1
  }
]

POST /v1/entities/{entity}/quotes

Create Quote

Create a draft quote, numbered at once (QT-2026-000001). The currency and payment terms default to the customer's, valid_until to 30 days on. Each line is priced as an invoice of that day would be: the product's defaults and the VAT determined for the customer. Nothing is posted.

Required scope draft (admin implies every scope)
Idempotency-Key required
Operation id create_quote_v1_entities__entity__quotes_post

Parameters

In Name Type Required Description
path entity string yes Entity code, e.g. CH01 (GET /v1/entities).

Request body

application/json (required): SalesDocumentInput

Field Type Required Description
customer string yes Code (or id) of an active customer of the entity.
document_date string (date) or null Day of the quote or order (ISO 8601); default today where the company is.
valid_until string (date) or null Quotes: the last day the customer may accept it, not before the quote's day; default 30 days after it. Orders take none.
currency string or null ISO 4217 code; default the customer's. (pattern ^[A-Z]{3}$)
customer_reference string or null The customer's reference, e.g. its request or purchase order number; printed and carried to the invoice as its order reference. (max length 200)
payment_terms_days integer or null Payment terms in days; default the customer's. (min 0; max 365)
notes string or null Printed under the lines. (max length 2000)
opportunity_id string (uuid) or null The deal it is for (GET /v1/opportunities): one of the same customer, not lost. It never changes, and an order made from the quote is for the same deal.
lines array of SalesLineInput yes The lines. (min items 1; max items 200)
discount_percent decimal string or null A discount on the whole document in percent of the lines after their own discounts; split over the lines, so the VAT of each rate goes down by its share. Give this or discount_amount, not both. (max 100; greater than 0)
discount_amount decimal string or null A discount on the whole document as an amount, split over the lines like discount_percent; an invoice of part of an order takes its part of it. (greater than 0)

Example request

curl -X POST "$ERP_URL/v1/entities/CH01/quotes" \
  -H "Authorization: Bearer $ERP_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "customer": "C-DE",
  "customer_reference": "RFQ-2026-118",
  "document_date": "2026-10-11",
  "lines": [
    {
      "description": "Consulting, senior",
      "product": "CONSULT",
      "quantity": "8",
      "unit_price": "180.00"
    },
    {
      "description": "Travel, flat rate",
      "quantity": "1",
      "unit_price": "450.00"
    }
  ],
  "valid_until": "2026-11-10"
}'

Responses

Status Meaning Body
201 Created SalesDocumentCard
401 Missing or invalid API key (unauthorized). ErrorEnvelope
403 The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). ErrorEnvelope
404 The resource does not exist, or belongs to another organization (not_found). ErrorEnvelope
409 The request conflicts with the data: an Idempotency-Key reused with a different request, a closed period, or an integrity rule of the database (conflict, integrity_violation). ErrorEnvelope
422 Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. HTTPValidationError

Example response (shape)

{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "kind": "order",
  "number": "string",
  "status": "string",
  "customer": "string",
  "customer_name": "string",
  "customer_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "currency": "string",
  "document_date": "2026-09-21",
  "valid_until": "2026-09-21",
  "customer_reference": "string",
  "opportunity_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "discount_percent": "string",
  "discount_amount": "0.00",
  "net_total": "string",
  "tax_total": "string",
  "total": "string",
  "created_at": "2026-09-21T09:30:00Z",
  "version": 1,
  "revision": 1,
  "entity": "string",
  "payment_terms_days": 1,
  "notes": "string",
  "legal_notes": [
    "string"
  ],
  "reason": "string",
  "decided_by": {},
  "decided_at": "2026-09-21T09:30:00Z",
  "created_by": {},
  "opportunity_number": "string",
  "quote": {
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "number": "string",
    "status": "string"
  },
  "order": {
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "number": "string",
    "status": "string"
  },
  "lines": [
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "line_no": 1,
      "description": "string",
      "product_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "product": "string",
      "supply_type": "string",
      "rate_type": "string",
      "tax_category": "string",
      "quantity": "string",
      "unit_price": "string",
      "discount_percent": "string",
      "discount_amount": "0.00",
      "document_discount_amount": "0.00",
      "net_amount": "string",
      "tax_rate": "string",
      "tax_amount": "string",
      "closed_quantity": "string",
      "invoiced_quantity": "0",
      "left_quantity": "0",
      "shipped_quantity": "string",
      "left_to_ship_quantity": "string"
    }
  ],
  "invoices": [
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "number": "string",
      "status": "string",
      "document_date": "2026-09-21",
      "net_total": "string",
      "total": "string"
    }
  ],
  "invoiced_net": "0.00",
  "dispatches": [
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "channel": "email",
      "handed_over_on": "2026-09-21",
      "recipients": [
        "string"
      ],
      "subject": "string",
      "filename": "string",
      "sha256": "string",
      "sent_by": {},
      "sent_at": "2026-09-21T09:30:00Z"
    }
  ],
  "sending_configured": true,
  "suggested_recipient": "string",
  "online": {
    "revision": 1,
    "revisions": [
      null
    ],
    "links": [
      null
    ],
    "answer": null
  }
}

GET /v1/entities/{entity}/sales-orders

List Sales Orders

Sales orders of an entity, newest first. An open order is partially_invoiced or invoiced by what its invoices that are not void or rejected add up to.

Required scope read (admin implies every scope)
Operation id list_sales_orders_v1_entities__entity__sales_orders_get

Parameters

In Name Type Required Description
path entity string yes Entity code, e.g. CH01 (GET /v1/entities).
query status array of "cancelled" | "closed" | "draft" | "invoiced" | "open" | "partially_invoiced" or null One or more statuses.
query customer string or null Customer code or id.
query q string or null Text in the number, reference or customer.
query limit integer How many to return, newest first; at most 1000. (min 1; max 1000; default 100)

Example request

curl "$ERP_URL/v1/entities/CH01/sales-orders" \
  -H "Authorization: Bearer $ERP_KEY"

Responses

Status Meaning Body
200 OK array of SalesDocumentOut
401 Missing or invalid API key (unauthorized). ErrorEnvelope
403 The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). ErrorEnvelope
404 The resource does not exist, or belongs to another organization (not_found). ErrorEnvelope
422 Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. HTTPValidationError

Example response (shape)

[
  {
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "kind": "order",
    "number": "string",
    "status": "string",
    "customer": "string",
    "customer_name": "string",
    "customer_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "currency": "string",
    "document_date": "2026-09-21",
    "valid_until": "2026-09-21",
    "customer_reference": "string",
    "opportunity_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "discount_percent": "string",
    "discount_amount": "0.00",
    "net_total": "string",
    "tax_total": "string",
    "total": "string",
    "created_at": "2026-09-21T09:30:00Z",
    "version": 1,
    "revision": 1
  }
]

POST /v1/entities/{entity}/sales-orders

Create Sales Order

Enter a sales order directly as a draft (SO-2026-000001), for an order that came without a quote; confirm it to invoice it. An accepted quote makes its order itself. Nothing is posted.

Required scope draft (admin implies every scope)
Idempotency-Key required
Operation id create_sales_order_v1_entities__entity__sales_orders_post

Parameters

In Name Type Required Description
path entity string yes Entity code, e.g. CH01 (GET /v1/entities).

Request body

application/json (required): SalesDocumentInput

Field Type Required Description
customer string yes Code (or id) of an active customer of the entity.
document_date string (date) or null Day of the quote or order (ISO 8601); default today where the company is.
valid_until string (date) or null Quotes: the last day the customer may accept it, not before the quote's day; default 30 days after it. Orders take none.
currency string or null ISO 4217 code; default the customer's. (pattern ^[A-Z]{3}$)
customer_reference string or null The customer's reference, e.g. its request or purchase order number; printed and carried to the invoice as its order reference. (max length 200)
payment_terms_days integer or null Payment terms in days; default the customer's. (min 0; max 365)
notes string or null Printed under the lines. (max length 2000)
opportunity_id string (uuid) or null The deal it is for (GET /v1/opportunities): one of the same customer, not lost. It never changes, and an order made from the quote is for the same deal.
lines array of SalesLineInput yes The lines. (min items 1; max items 200)
discount_percent decimal string or null A discount on the whole document in percent of the lines after their own discounts; split over the lines, so the VAT of each rate goes down by its share. Give this or discount_amount, not both. (max 100; greater than 0)
discount_amount decimal string or null A discount on the whole document as an amount, split over the lines like discount_percent; an invoice of part of an order takes its part of it. (greater than 0)

Example request

curl -X POST "$ERP_URL/v1/entities/CH01/sales-orders" \
  -H "Authorization: Bearer $ERP_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "customer": "C-DE",
  "customer_reference": "RFQ-2026-118",
  "document_date": "2026-10-11",
  "lines": [
    {
      "description": "Consulting, senior",
      "product": "CONSULT",
      "quantity": "8",
      "unit_price": "180.00"
    },
    {
      "description": "Travel, flat rate",
      "quantity": "1",
      "unit_price": "450.00"
    }
  ],
  "valid_until": "2026-11-10"
}'

Responses

Status Meaning Body
201 Created SalesDocumentCard
401 Missing or invalid API key (unauthorized). ErrorEnvelope
403 The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). ErrorEnvelope
404 The resource does not exist, or belongs to another organization (not_found). ErrorEnvelope
409 The request conflicts with the data: an Idempotency-Key reused with a different request, a closed period, or an integrity rule of the database (conflict, integrity_violation). ErrorEnvelope
422 Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. HTTPValidationError

Example response (shape)

{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "kind": "order",
  "number": "string",
  "status": "string",
  "customer": "string",
  "customer_name": "string",
  "customer_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "currency": "string",
  "document_date": "2026-09-21",
  "valid_until": "2026-09-21",
  "customer_reference": "string",
  "opportunity_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "discount_percent": "string",
  "discount_amount": "0.00",
  "net_total": "string",
  "tax_total": "string",
  "total": "string",
  "created_at": "2026-09-21T09:30:00Z",
  "version": 1,
  "revision": 1,
  "entity": "string",
  "payment_terms_days": 1,
  "notes": "string",
  "legal_notes": [
    "string"
  ],
  "reason": "string",
  "decided_by": {},
  "decided_at": "2026-09-21T09:30:00Z",
  "created_by": {},
  "opportunity_number": "string",
  "quote": {
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "number": "string",
    "status": "string"
  },
  "order": {
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "number": "string",
    "status": "string"
  },
  "lines": [
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "line_no": 1,
      "description": "string",
      "product_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "product": "string",
      "supply_type": "string",
      "rate_type": "string",
      "tax_category": "string",
      "quantity": "string",
      "unit_price": "string",
      "discount_percent": "string",
      "discount_amount": "0.00",
      "document_discount_amount": "0.00",
      "net_amount": "string",
      "tax_rate": "string",
      "tax_amount": "string",
      "closed_quantity": "string",
      "invoiced_quantity": "0",
      "left_quantity": "0",
      "shipped_quantity": "string",
      "left_to_ship_quantity": "string"
    }
  ],
  "invoices": [
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "number": "string",
      "status": "string",
      "document_date": "2026-09-21",
      "net_total": "string",
      "total": "string"
    }
  ],
  "invoiced_net": "0.00",
  "dispatches": [
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "channel": "email",
      "handed_over_on": "2026-09-21",
      "recipients": [
        "string"
      ],
      "subject": "string",
      "filename": "string",
      "sha256": "string",
      "sent_by": {},
      "sent_at": "2026-09-21T09:30:00Z"
    }
  ],
  "sending_configured": true,
  "suggested_recipient": "string",
  "online": {
    "revision": 1,
    "revisions": [
      null
    ],
    "links": [
      null
    ],
    "answer": null
  }
}

GET /v1/public/quotes/{token}

View Quote Online

The quote as its customer sees it on the link, without a sign-in: the seller, the lines, VAT, totals, until when it is valid and whether it can be answered (state). 404 for a link that is wrong or turned off.

Required scope any valid credential
Operation id view_quote_online_v1_public_quotes__token__get

Parameters

In Name Type Required Description
path token string yes The link's token, as in /app/quote/{token}. (max length 100)
query language "de" | "en" | "fr" | "it" | "sv" or null de, fr, it, en or sv.

Example request

curl "$ERP_URL/v1/public/quotes/{token}" \
  -H "Authorization: Bearer $ERP_KEY"

Responses

Status Meaning Body
200 OK QuoteOnlineView
401 Missing or invalid API key (unauthorized). ErrorEnvelope
404 The resource does not exist, or belongs to another organization (not_found). ErrorEnvelope
422 Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. HTTPValidationError

Example response (shape)

{
  "state": "accepted",
  "language": "de",
  "test_copy": true,
  "seller_name": "string",
  "seller_lines": [
    "string"
  ],
  "seller_logo": "string",
  "seller_colour": "string",
  "customer_name": "string",
  "buyer_lines": [
    "string"
  ],
  "number": "string",
  "revision": 1,
  "document_date": "2026-09-21",
  "valid_until": "2026-09-21",
  "currency": "string",
  "customer_reference": "string",
  "payment_terms_days": 1,
  "lines": [
    {
      "position": 1,
      "description": "string",
      "quantity": "string",
      "unit_price": "string",
      "discount_percent": "string",
      "discount_amount": "string",
      "vat_rate": "string",
      "net_amount": "string"
    }
  ],
  "vat": [
    {
      "rate": "string",
      "name": "string",
      "net": "string",
      "tax": "string"
    }
  ],
  "discount_percent": "string",
  "discount_amount": "string",
  "net_total": "string",
  "tax_total": "string",
  "total": "string",
  "notes": [
    "string"
  ],
  "answered_by": "string",
  "answered_at": "2026-09-21T09:30:00Z"
}

POST /v1/public/quotes/{token}/accept

Accept Quote Online

Accept the quote for your company, naming the version you read (revision): kept with your name, the time and your internet address, and the seller's order is made from it. 409 quote_not_open_online when it changed, expired or was answered since.

Required scope any valid credential
Idempotency-Key not used
Operation id accept_quote_online_v1_public_quotes__token__accept_post

Parameters

In Name Type Required Description
path token string yes The link's token, as in /app/quote/{token}. (max length 100)

Request body

application/json (required): OnlineAcceptInput

Field Type Required Description
name string yes Your full name. (min length 1; max length 200)
email string or null Your e-mail address. (max length 320)
revision integer yes The version you read (revision of the page). (min 1)
agree true yes You accept the quote for your company.
customer_reference string or null Your purchase order number, if any. (max length 200)

Example request

curl -X POST "$ERP_URL/v1/public/quotes/{token}/accept" \
  -H "Authorization: Bearer $ERP_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "agree": true,
  "customer_reference": "PO-4711",
  "email": "anna@werkstatt.example",
  "name": "Anna Muster",
  "revision": 2
}'

Responses

Status Meaning Body
200 OK QuoteOnlineView
401 Missing or invalid API key (unauthorized). ErrorEnvelope
404 The resource does not exist, or belongs to another organization (not_found). ErrorEnvelope
409 The request conflicts with the data: an Idempotency-Key reused with a different request, a closed period, or an integrity rule of the database (conflict, integrity_violation). ErrorEnvelope
422 Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. HTTPValidationError

Example response (shape)

{
  "state": "accepted",
  "language": "de",
  "test_copy": true,
  "seller_name": "string",
  "seller_lines": [
    "string"
  ],
  "seller_logo": "string",
  "seller_colour": "string",
  "customer_name": "string",
  "buyer_lines": [
    "string"
  ],
  "number": "string",
  "revision": 1,
  "document_date": "2026-09-21",
  "valid_until": "2026-09-21",
  "currency": "string",
  "customer_reference": "string",
  "payment_terms_days": 1,
  "lines": [
    {
      "position": 1,
      "description": "string",
      "quantity": "string",
      "unit_price": "string",
      "discount_percent": "string",
      "discount_amount": "string",
      "vat_rate": "string",
      "net_amount": "string"
    }
  ],
  "vat": [
    {
      "rate": "string",
      "name": "string",
      "net": "string",
      "tax": "string"
    }
  ],
  "discount_percent": "string",
  "discount_amount": "string",
  "net_total": "string",
  "tax_total": "string",
  "total": "string",
  "notes": [
    "string"
  ],
  "answered_by": "string",
  "answered_at": "2026-09-21T09:30:00Z"
}

POST /v1/public/quotes/{token}/decline

Decline Quote Online

Decline the quote, with why if you like, naming the version you read. 409 quote_not_open_online when it changed, expired or was answered since.

Required scope any valid credential
Idempotency-Key not used
Operation id decline_quote_online_v1_public_quotes__token__decline_post

Parameters

In Name Type Required Description
path token string yes The link's token, as in /app/quote/{token}. (max length 100)

Request body

application/json (required): OnlineDeclineInput

Field Type Required Description
name string yes Your full name. (min length 1; max length 200)
email string or null Your e-mail address. (max length 320)
revision integer yes The version you read. (min 1)
reason string or null Why, if you like. (max length 500)

Example request

curl -X POST "$ERP_URL/v1/public/quotes/{token}/decline" \
  -H "Authorization: Bearer $ERP_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Anna Muster",
  "reason": "Budget",
  "revision": 1
}'

Responses

Status Meaning Body
200 OK QuoteOnlineView
401 Missing or invalid API key (unauthorized). ErrorEnvelope
404 The resource does not exist, or belongs to another organization (not_found). ErrorEnvelope
409 The request conflicts with the data: an Idempotency-Key reused with a different request, a closed period, or an integrity rule of the database (conflict, integrity_violation). ErrorEnvelope
422 Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. HTTPValidationError

Example response (shape)

{
  "state": "accepted",
  "language": "de",
  "test_copy": true,
  "seller_name": "string",
  "seller_lines": [
    "string"
  ],
  "seller_logo": "string",
  "seller_colour": "string",
  "customer_name": "string",
  "buyer_lines": [
    "string"
  ],
  "number": "string",
  "revision": 1,
  "document_date": "2026-09-21",
  "valid_until": "2026-09-21",
  "currency": "string",
  "customer_reference": "string",
  "payment_terms_days": 1,
  "lines": [
    {
      "position": 1,
      "description": "string",
      "quantity": "string",
      "unit_price": "string",
      "discount_percent": "string",
      "discount_amount": "string",
      "vat_rate": "string",
      "net_amount": "string"
    }
  ],
  "vat": [
    {
      "rate": "string",
      "name": "string",
      "net": "string",
      "tax": "string"
    }
  ],
  "discount_percent": "string",
  "discount_amount": "string",
  "net_total": "string",
  "tax_total": "string",
  "total": "string",
  "notes": [
    "string"
  ],
  "answered_by": "string",
  "answered_at": "2026-09-21T09:30:00Z"
}

GET /v1/public/quotes/{token}/pdf

Quote Online Pdf

The PDF of the version the page shows, as it was handed over. 409 while it is being changed.

Required scope any valid credential
Operation id quote_online_pdf_v1_public_quotes__token__pdf_get

Parameters

In Name Type Required Description
path token string yes The link's token, as in /app/quote/{token}. (max length 100)
query download boolean true sends the file as an attachment. (default false)

Example request

curl "$ERP_URL/v1/public/quotes/{token}/pdf" \
  -H "Authorization: Bearer $ERP_KEY"

Responses

Status Meaning Body
200 The PDF. application/pdf
401 Missing or invalid API key (unauthorized). ErrorEnvelope
404 The resource does not exist, or belongs to another organization (not_found). ErrorEnvelope
422 Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. HTTPValidationError

GET /v1/sales-documents/{document}

Get Sales Document

A quote or sales order by id or number: lines (with invoiced and left quantities on an order), the linked quote or order, its invoices and hand-overs. The ETag names its version.

Required scope read (admin implies every scope)
Operation id get_sales_document_v1_sales_documents__document__get

Parameters

In Name Type Required Description
path document string yes Quote or sales order id (uuid) or number, e.g. QT-2026-000001 or SO-2026-000001.

Example request

curl "$ERP_URL/v1/sales-documents/QT-2026-000001" \
  -H "Authorization: Bearer $ERP_KEY"

Responses

Status Meaning Body
200 OK SalesDocumentCard
401 Missing or invalid API key (unauthorized). ErrorEnvelope
403 The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). ErrorEnvelope
404 The resource does not exist, or belongs to another organization (not_found). ErrorEnvelope
422 Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. HTTPValidationError

Example response (shape)

{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "kind": "order",
  "number": "string",
  "status": "string",
  "customer": "string",
  "customer_name": "string",
  "customer_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "currency": "string",
  "document_date": "2026-09-21",
  "valid_until": "2026-09-21",
  "customer_reference": "string",
  "opportunity_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "discount_percent": "string",
  "discount_amount": "0.00",
  "net_total": "string",
  "tax_total": "string",
  "total": "string",
  "created_at": "2026-09-21T09:30:00Z",
  "version": 1,
  "revision": 1,
  "entity": "string",
  "payment_terms_days": 1,
  "notes": "string",
  "legal_notes": [
    "string"
  ],
  "reason": "string",
  "decided_by": {},
  "decided_at": "2026-09-21T09:30:00Z",
  "created_by": {},
  "opportunity_number": "string",
  "quote": {
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "number": "string",
    "status": "string"
  },
  "order": {
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "number": "string",
    "status": "string"
  },
  "lines": [
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "line_no": 1,
      "description": "string",
      "product_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "product": "string",
      "supply_type": "string",
      "rate_type": "string",
      "tax_category": "string",
      "quantity": "string",
      "unit_price": "string",
      "discount_percent": "string",
      "discount_amount": "0.00",
      "document_discount_amount": "0.00",
      "net_amount": "string",
      "tax_rate": "string",
      "tax_amount": "string",
      "closed_quantity": "string",
      "invoiced_quantity": "0",
      "left_quantity": "0",
      "shipped_quantity": "string",
      "left_to_ship_quantity": "string"
    }
  ],
  "invoices": [
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "number": "string",
      "status": "string",
      "document_date": "2026-09-21",
      "net_total": "string",
      "total": "string"
    }
  ],
  "invoiced_net": "0.00",
  "dispatches": [
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "channel": "email",
      "handed_over_on": "2026-09-21",
      "recipients": [
        "string"
      ],
      "subject": "string",
      "filename": "string",
      "sha256": "string",
      "sent_by": {},
      "sent_at": "2026-09-21T09:30:00Z"
    }
  ],
  "sending_configured": true,
  "suggested_recipient": "string",
  "online": {
    "revision": 1,
    "revisions": [
      null
    ],
    "links": [
      null
    ],
    "answer": null
  }
}

PATCH /v1/sales-documents/{document}

Update Sales Document

Change a draft (lines replaces every line; every line is priced again). A sent quote goes back to draft and is sent again. 409 otherwise; 412 when If-Match names an older version.

Required scope draft (admin implies every scope)
Idempotency-Key not used
ETag / If-Match If-Match optional: a changed version answers 412
Response header ETag names the current version
Operation id update_sales_document_v1_sales_documents__document__patch

Parameters

In Name Type Required Description
path document string yes Quote or sales order id (uuid) or number, e.g. QT-2026-000001 or SO-2026-000001.
header If-Match string or null The ETag of the version you read; a changed quote or order answers 412. (max length 200)

Request body

application/json (required): SalesDocumentUpdate

Field Type Required Description
customer string or null Code (or id) of an active customer.
document_date string (date) or null New day of the document.
valid_until string (date) or null Quotes: new last day.
currency string or null ISO 4217 code. (pattern ^[A-Z]{3}$)
customer_reference string or null The customer's reference; null clears it. (max length 200)
payment_terms_days integer or null Payment terms in days, 0 to 365. (min 0; max 365)
notes string or null Printed under the lines; null clears it. (max length 2000)
lines array of SalesLineInput or null Replaces every line. (min items 1; max items 200)
discount_percent decimal string or null A document discount in percent; replaces any. null removes it. (max 100; greater than 0)
discount_amount decimal string or null A document discount as an amount; replaces any. null removes it. (greater than 0)

Example request

curl -X PATCH "$ERP_URL/v1/sales-documents/QT-2026-000001" \
  -H "Authorization: Bearer $ERP_KEY" \
  -H 'If-Match: "1"' \
  -H "Content-Type: application/json" \
  -d '{
  "lines": [
    {
      "description": "Consulting, senior",
      "product": "CONSULT",
      "quantity": "8",
      "unit_price": "180.00"
    },
    {
      "description": "Travel, flat rate",
      "quantity": "1",
      "unit_price": "450.00"
    }
  ],
  "valid_until": "2026-11-30"
}'

Responses

Status Meaning Body
200 OK SalesDocumentCard, ETag header
401 Missing or invalid API key (unauthorized). ErrorEnvelope
403 The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). ErrorEnvelope
404 The resource does not exist, or belongs to another organization (not_found). ErrorEnvelope
409 The request conflicts with the data: an Idempotency-Key reused with a different request, a closed period, or an integrity rule of the database (conflict, integrity_violation). ErrorEnvelope
412 If-Match names a version that is no longer current (precondition_failed). ErrorEnvelope
422 Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. HTTPValidationError

Example response (shape)

{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "kind": "order",
  "number": "string",
  "status": "string",
  "customer": "string",
  "customer_name": "string",
  "customer_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "currency": "string",
  "document_date": "2026-09-21",
  "valid_until": "2026-09-21",
  "customer_reference": "string",
  "opportunity_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "discount_percent": "string",
  "discount_amount": "0.00",
  "net_total": "string",
  "tax_total": "string",
  "total": "string",
  "created_at": "2026-09-21T09:30:00Z",
  "version": 1,
  "revision": 1,
  "entity": "string",
  "payment_terms_days": 1,
  "notes": "string",
  "legal_notes": [
    "string"
  ],
  "reason": "string",
  "decided_by": {},
  "decided_at": "2026-09-21T09:30:00Z",
  "created_by": {},
  "opportunity_number": "string",
  "quote": {
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "number": "string",
    "status": "string"
  },
  "order": {
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "number": "string",
    "status": "string"
  },
  "lines": [
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "line_no": 1,
      "description": "string",
      "product_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "product": "string",
      "supply_type": "string",
      "rate_type": "string",
      "tax_category": "string",
      "quantity": "string",
      "unit_price": "string",
      "discount_percent": "string",
      "discount_amount": "0.00",
      "document_discount_amount": "0.00",
      "net_amount": "string",
      "tax_rate": "string",
      "tax_amount": "string",
      "closed_quantity": "string",
      "invoiced_quantity": "0",
      "left_quantity": "0",
      "shipped_quantity": "string",
      "left_to_ship_quantity": "string"
    }
  ],
  "invoices": [
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "number": "string",
      "status": "string",
      "document_date": "2026-09-21",
      "net_total": "string",
      "total": "string"
    }
  ],
  "invoiced_net": "0.00",
  "dispatches": [
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "channel": "email",
      "handed_over_on": "2026-09-21",
      "recipients": [
        "string"
      ],
      "subject": "string",
      "filename": "string",
      "sha256": "string",
      "sent_by": {},
      "sent_at": "2026-09-21T09:30:00Z"
    }
  ],
  "sending_configured": true,
  "suggested_recipient": "string",
  "online": {
    "revision": 1,
    "revisions": [
      null
    ],
    "links": [
      null
    ],
    "answer": null
  }
}

POST /v1/sales-documents/{document}/accept

Accept Quote

The customer accepted the quote: it is accepted, and the open sales order is made from it (same customer, currency, terms and lines, dated the day accepted, its own number). Answers the order. 409 quote_expired when the quote was no longer valid on that day (change its valid-until day and send it again) or when it is not draft or sent; 409 credit_limit_exceeded when the order would take the customer above its credit limit (the quote stays as it is); 422 for a day before the quote or in the future. Only a person records the customer's answer (an agent key answers 403 agent_records_no_answer); the customer may give it on the quote's link.

Required scope draft (admin implies every scope)
Idempotency-Key required
ETag / If-Match If-Match optional: a changed version answers 412
Response header ETag names the current version
Operation id accept_quote_v1_sales_documents__document__accept_post

Parameters

In Name Type Required Description
path document string yes Quote or sales order id (uuid) or number, e.g. QT-2026-000001 or SO-2026-000001.
header If-Match string or null The ETag of the version you read; a changed quote or order answers 412. (max length 200)

Request body

application/json (required): AcceptInput

Field Type Required Description
accepted_on string (date) or null The day the customer accepted, within the quote's validity and not after today; default today. It is the day of the order.
customer_reference string or null The customer's purchase order number, if any. (max length 200)

Example request

curl -X POST "$ERP_URL/v1/sales-documents/QT-2026-000001/accept" \
  -H "Authorization: Bearer $ERP_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H 'If-Match: "1"' \
  -H "Content-Type: application/json" \
  -d '{}'

Responses

Status Meaning Body
200 OK SalesDocumentCard, ETag header
401 Missing or invalid API key (unauthorized). ErrorEnvelope
403 The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). ErrorEnvelope
404 The resource does not exist, or belongs to another organization (not_found). ErrorEnvelope
409 The request conflicts with the data: an Idempotency-Key reused with a different request, a closed period, or an integrity rule of the database (conflict, integrity_violation). ErrorEnvelope
412 If-Match names a version that is no longer current (precondition_failed). ErrorEnvelope
422 Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. HTTPValidationError

Example response (shape)

{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "kind": "order",
  "number": "string",
  "status": "string",
  "customer": "string",
  "customer_name": "string",
  "customer_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "currency": "string",
  "document_date": "2026-09-21",
  "valid_until": "2026-09-21",
  "customer_reference": "string",
  "opportunity_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "discount_percent": "string",
  "discount_amount": "0.00",
  "net_total": "string",
  "tax_total": "string",
  "total": "string",
  "created_at": "2026-09-21T09:30:00Z",
  "version": 1,
  "revision": 1,
  "entity": "string",
  "payment_terms_days": 1,
  "notes": "string",
  "legal_notes": [
    "string"
  ],
  "reason": "string",
  "decided_by": {},
  "decided_at": "2026-09-21T09:30:00Z",
  "created_by": {},
  "opportunity_number": "string",
  "quote": {
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "number": "string",
    "status": "string"
  },
  "order": {
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "number": "string",
    "status": "string"
  },
  "lines": [
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "line_no": 1,
      "description": "string",
      "product_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "product": "string",
      "supply_type": "string",
      "rate_type": "string",
      "tax_category": "string",
      "quantity": "string",
      "unit_price": "string",
      "discount_percent": "string",
      "discount_amount": "0.00",
      "document_discount_amount": "0.00",
      "net_amount": "string",
      "tax_rate": "string",
      "tax_amount": "string",
      "closed_quantity": "string",
      "invoiced_quantity": "0",
      "left_quantity": "0",
      "shipped_quantity": "string",
      "left_to_ship_quantity": "string"
    }
  ],
  "invoices": [
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "number": "string",
      "status": "string",
      "document_date": "2026-09-21",
      "net_total": "string",
      "total": "string"
    }
  ],
  "invoiced_net": "0.00",
  "dispatches": [
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "channel": "email",
      "handed_over_on": "2026-09-21",
      "recipients": [
        "string"
      ],
      "subject": "string",
      "filename": "string",
      "sha256": "string",
      "sent_by": {},
      "sent_at": "2026-09-21T09:30:00Z"
    }
  ],
  "sending_configured": true,
  "suggested_recipient": "string",
  "online": {
    "revision": 1,
    "revisions": [
      null
    ],
    "links": [
      null
    ],
    "answer": null
  }
}

POST /v1/sales-documents/{document}/cancel

Cancel Sales Document

Withdraw a quote not yet decided, or cancel a draft or open order nothing was invoiced on (a draft invoice counts: void it first). It keeps its number. 409 order_invoiced for an order with invoices: close it for the rest instead.

Required scope draft (admin implies every scope)
Idempotency-Key not used
ETag / If-Match If-Match optional: a changed version answers 412
Response header ETag names the current version
Operation id cancel_sales_document_v1_sales_documents__document__cancel_post

Parameters

In Name Type Required Description
path document string yes Quote or sales order id (uuid) or number, e.g. QT-2026-000001 or SO-2026-000001.
header If-Match string or null The ETag of the version you read; a changed quote or order answers 412. (max length 200)

Request body

application/json (required): erp__features__crm__contracts__sales__ReasonInput

Field Type Required Description
reason string yes Why; kept on the document. (min length 1; max length 500)

Example request

curl -X POST "$ERP_URL/v1/sales-documents/QT-2026-000001/cancel" \
  -H "Authorization: Bearer $ERP_KEY" \
  -H 'If-Match: "1"' \
  -H "Content-Type: application/json" \
  -d '{
  "reason": "Customer chose another offer"
}'

Responses

Status Meaning Body
200 OK SalesDocumentCard, ETag header
401 Missing or invalid API key (unauthorized). ErrorEnvelope
403 The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). ErrorEnvelope
404 The resource does not exist, or belongs to another organization (not_found). ErrorEnvelope
409 The request conflicts with the data: an Idempotency-Key reused with a different request, a closed period, or an integrity rule of the database (conflict, integrity_violation). ErrorEnvelope
412 If-Match names a version that is no longer current (precondition_failed). ErrorEnvelope
422 Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. HTTPValidationError

Example response (shape)

{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "kind": "order",
  "number": "string",
  "status": "string",
  "customer": "string",
  "customer_name": "string",
  "customer_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "currency": "string",
  "document_date": "2026-09-21",
  "valid_until": "2026-09-21",
  "customer_reference": "string",
  "opportunity_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "discount_percent": "string",
  "discount_amount": "0.00",
  "net_total": "string",
  "tax_total": "string",
  "total": "string",
  "created_at": "2026-09-21T09:30:00Z",
  "version": 1,
  "revision": 1,
  "entity": "string",
  "payment_terms_days": 1,
  "notes": "string",
  "legal_notes": [
    "string"
  ],
  "reason": "string",
  "decided_by": {},
  "decided_at": "2026-09-21T09:30:00Z",
  "created_by": {},
  "opportunity_number": "string",
  "quote": {
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "number": "string",
    "status": "string"
  },
  "order": {
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "number": "string",
    "status": "string"
  },
  "lines": [
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "line_no": 1,
      "description": "string",
      "product_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "product": "string",
      "supply_type": "string",
      "rate_type": "string",
      "tax_category": "string",
      "quantity": "string",
      "unit_price": "string",
      "discount_percent": "string",
      "discount_amount": "0.00",
      "document_discount_amount": "0.00",
      "net_amount": "string",
      "tax_rate": "string",
      "tax_amount": "string",
      "closed_quantity": "string",
      "invoiced_quantity": "0",
      "left_quantity": "0",
      "shipped_quantity": "string",
      "left_to_ship_quantity": "string"
    }
  ],
  "invoices": [
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "number": "string",
      "status": "string",
      "document_date": "2026-09-21",
      "net_total": "string",
      "total": "string"
    }
  ],
  "invoiced_net": "0.00",
  "dispatches": [
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "channel": "email",
      "handed_over_on": "2026-09-21",
      "recipients": [
        "string"
      ],
      "subject": "string",
      "filename": "string",
      "sha256": "string",
      "sent_by": {},
      "sent_at": "2026-09-21T09:30:00Z"
    }
  ],
  "sending_configured": true,
  "suggested_recipient": "string",
  "online": {
    "revision": 1,
    "revisions": [
      null
    ],
    "links": [
      null
    ],
    "answer": null
  }
}

POST /v1/sales-documents/{document}/close

Close Sales Order

Close an open order for the rest, with why (the customer no longer wants it): what was invoiced stays, the rest of each line is closed and no longer invoiced. Final. 409 when nothing was invoiced (cancel it instead) or nothing is left.

Required scope draft (admin implies every scope)
Idempotency-Key not used
ETag / If-Match If-Match optional: a changed version answers 412
Response header ETag names the current version
Operation id close_sales_order_v1_sales_documents__document__close_post

Parameters

In Name Type Required Description
path document string yes Quote or sales order id (uuid) or number, e.g. QT-2026-000001 or SO-2026-000001.
header If-Match string or null The ETag of the version you read; a changed quote or order answers 412. (max length 200)

Request body

application/json (required): erp__features__crm__contracts__sales__ReasonInput

Field Type Required Description
reason string yes Why; kept on the document. (min length 1; max length 500)

Example request

curl -X POST "$ERP_URL/v1/sales-documents/QT-2026-000001/close" \
  -H "Authorization: Bearer $ERP_KEY" \
  -H 'If-Match: "1"' \
  -H "Content-Type: application/json" \
  -d '{
  "reason": "Customer chose another offer"
}'

Responses

Status Meaning Body
200 OK SalesDocumentCard, ETag header
401 Missing or invalid API key (unauthorized). ErrorEnvelope
403 The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). ErrorEnvelope
404 The resource does not exist, or belongs to another organization (not_found). ErrorEnvelope
409 The request conflicts with the data: an Idempotency-Key reused with a different request, a closed period, or an integrity rule of the database (conflict, integrity_violation). ErrorEnvelope
412 If-Match names a version that is no longer current (precondition_failed). ErrorEnvelope
422 Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. HTTPValidationError

Example response (shape)

{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "kind": "order",
  "number": "string",
  "status": "string",
  "customer": "string",
  "customer_name": "string",
  "customer_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "currency": "string",
  "document_date": "2026-09-21",
  "valid_until": "2026-09-21",
  "customer_reference": "string",
  "opportunity_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "discount_percent": "string",
  "discount_amount": "0.00",
  "net_total": "string",
  "tax_total": "string",
  "total": "string",
  "created_at": "2026-09-21T09:30:00Z",
  "version": 1,
  "revision": 1,
  "entity": "string",
  "payment_terms_days": 1,
  "notes": "string",
  "legal_notes": [
    "string"
  ],
  "reason": "string",
  "decided_by": {},
  "decided_at": "2026-09-21T09:30:00Z",
  "created_by": {},
  "opportunity_number": "string",
  "quote": {
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "number": "string",
    "status": "string"
  },
  "order": {
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "number": "string",
    "status": "string"
  },
  "lines": [
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "line_no": 1,
      "description": "string",
      "product_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "product": "string",
      "supply_type": "string",
      "rate_type": "string",
      "tax_category": "string",
      "quantity": "string",
      "unit_price": "string",
      "discount_percent": "string",
      "discount_amount": "0.00",
      "document_discount_amount": "0.00",
      "net_amount": "string",
      "tax_rate": "string",
      "tax_amount": "string",
      "closed_quantity": "string",
      "invoiced_quantity": "0",
      "left_quantity": "0",
      "shipped_quantity": "string",
      "left_to_ship_quantity": "string"
    }
  ],
  "invoices": [
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "number": "string",
      "status": "string",
      "document_date": "2026-09-21",
      "net_total": "string",
      "total": "string"
    }
  ],
  "invoiced_net": "0.00",
  "dispatches": [
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "channel": "email",
      "handed_over_on": "2026-09-21",
      "recipients": [
        "string"
      ],
      "subject": "string",
      "filename": "string",
      "sha256": "string",
      "sent_by": {},
      "sent_at": "2026-09-21T09:30:00Z"
    }
  ],
  "sending_configured": true,
  "suggested_recipient": "string",
  "online": {
    "revision": 1,
    "revisions": [
      null
    ],
    "links": [
      null
    ],
    "answer": null
  }
}

POST /v1/sales-documents/{document}/confirm

Confirm Sales Order

Confirm a draft sales order: it is open, to be invoiced, and no longer changes. 409 credit_limit_exceeded when it would take the customer above its credit limit (what it owes, its invoices waiting for approval and its open orders, with this one): it stays a draft.

Required scope draft (admin implies every scope)
Idempotency-Key not used
ETag / If-Match If-Match optional: a changed version answers 412
Response header ETag names the current version
Operation id confirm_sales_order_v1_sales_documents__document__confirm_post

Parameters

In Name Type Required Description
path document string yes Quote or sales order id (uuid) or number, e.g. QT-2026-000001 or SO-2026-000001.
header If-Match string or null The ETag of the version you read; a changed quote or order answers 412. (max length 200)

Example request

curl -X POST "$ERP_URL/v1/sales-documents/QT-2026-000001/confirm" \
  -H "Authorization: Bearer $ERP_KEY" \
  -H 'If-Match: "1"'

Responses

Status Meaning Body
200 OK SalesDocumentCard, ETag header
401 Missing or invalid API key (unauthorized). ErrorEnvelope
403 The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). ErrorEnvelope
404 The resource does not exist, or belongs to another organization (not_found). ErrorEnvelope
409 The request conflicts with the data: an Idempotency-Key reused with a different request, a closed period, or an integrity rule of the database (conflict, integrity_violation). ErrorEnvelope
412 If-Match names a version that is no longer current (precondition_failed). ErrorEnvelope
422 Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. HTTPValidationError

Example response (shape)

{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "kind": "order",
  "number": "string",
  "status": "string",
  "customer": "string",
  "customer_name": "string",
  "customer_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "currency": "string",
  "document_date": "2026-09-21",
  "valid_until": "2026-09-21",
  "customer_reference": "string",
  "opportunity_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "discount_percent": "string",
  "discount_amount": "0.00",
  "net_total": "string",
  "tax_total": "string",
  "total": "string",
  "created_at": "2026-09-21T09:30:00Z",
  "version": 1,
  "revision": 1,
  "entity": "string",
  "payment_terms_days": 1,
  "notes": "string",
  "legal_notes": [
    "string"
  ],
  "reason": "string",
  "decided_by": {},
  "decided_at": "2026-09-21T09:30:00Z",
  "created_by": {},
  "opportunity_number": "string",
  "quote": {
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "number": "string",
    "status": "string"
  },
  "order": {
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "number": "string",
    "status": "string"
  },
  "lines": [
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "line_no": 1,
      "description": "string",
      "product_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "product": "string",
      "supply_type": "string",
      "rate_type": "string",
      "tax_category": "string",
      "quantity": "string",
      "unit_price": "string",
      "discount_percent": "string",
      "discount_amount": "0.00",
      "document_discount_amount": "0.00",
      "net_amount": "string",
      "tax_rate": "string",
      "tax_amount": "string",
      "closed_quantity": "string",
      "invoiced_quantity": "0",
      "left_quantity": "0",
      "shipped_quantity": "string",
      "left_to_ship_quantity": "string"
    }
  ],
  "invoices": [
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "number": "string",
      "status": "string",
      "document_date": "2026-09-21",
      "net_total": "string",
      "total": "string"
    }
  ],
  "invoiced_net": "0.00",
  "dispatches": [
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "channel": "email",
      "handed_over_on": "2026-09-21",
      "recipients": [
        "string"
      ],
      "subject": "string",
      "filename": "string",
      "sha256": "string",
      "sent_by": {},
      "sent_at": "2026-09-21T09:30:00Z"
    }
  ],
  "sending_configured": true,
  "suggested_recipient": "string",
  "online": {
    "revision": 1,
    "revisions": [
      null
    ],
    "links": [
      null
    ],
    "answer": null
  }
}

POST /v1/sales-documents/{document}/decline

Decline Quote

The customer declined the quote, with why (also once it expired). Final. Only a person records it (an agent key answers 403).

Required scope draft (admin implies every scope)
Idempotency-Key not used
ETag / If-Match If-Match optional: a changed version answers 412
Response header ETag names the current version
Operation id decline_quote_v1_sales_documents__document__decline_post

Parameters

In Name Type Required Description
path document string yes Quote or sales order id (uuid) or number, e.g. QT-2026-000001 or SO-2026-000001.
header If-Match string or null The ETag of the version you read; a changed quote or order answers 412. (max length 200)

Request body

application/json (required): erp__features__crm__contracts__sales__ReasonInput

Field Type Required Description
reason string yes Why; kept on the document. (min length 1; max length 500)

Example request

curl -X POST "$ERP_URL/v1/sales-documents/QT-2026-000001/decline" \
  -H "Authorization: Bearer $ERP_KEY" \
  -H 'If-Match: "1"' \
  -H "Content-Type: application/json" \
  -d '{
  "reason": "Customer chose another offer"
}'

Responses

Status Meaning Body
200 OK SalesDocumentCard, ETag header
401 Missing or invalid API key (unauthorized). ErrorEnvelope
403 The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). ErrorEnvelope
404 The resource does not exist, or belongs to another organization (not_found). ErrorEnvelope
409 The request conflicts with the data: an Idempotency-Key reused with a different request, a closed period, or an integrity rule of the database (conflict, integrity_violation). ErrorEnvelope
412 If-Match names a version that is no longer current (precondition_failed). ErrorEnvelope
422 Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. HTTPValidationError

Example response (shape)

{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "kind": "order",
  "number": "string",
  "status": "string",
  "customer": "string",
  "customer_name": "string",
  "customer_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "currency": "string",
  "document_date": "2026-09-21",
  "valid_until": "2026-09-21",
  "customer_reference": "string",
  "opportunity_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "discount_percent": "string",
  "discount_amount": "0.00",
  "net_total": "string",
  "tax_total": "string",
  "total": "string",
  "created_at": "2026-09-21T09:30:00Z",
  "version": 1,
  "revision": 1,
  "entity": "string",
  "payment_terms_days": 1,
  "notes": "string",
  "legal_notes": [
    "string"
  ],
  "reason": "string",
  "decided_by": {},
  "decided_at": "2026-09-21T09:30:00Z",
  "created_by": {},
  "opportunity_number": "string",
  "quote": {
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "number": "string",
    "status": "string"
  },
  "order": {
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "number": "string",
    "status": "string"
  },
  "lines": [
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "line_no": 1,
      "description": "string",
      "product_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "product": "string",
      "supply_type": "string",
      "rate_type": "string",
      "tax_category": "string",
      "quantity": "string",
      "unit_price": "string",
      "discount_percent": "string",
      "discount_amount": "0.00",
      "document_discount_amount": "0.00",
      "net_amount": "string",
      "tax_rate": "string",
      "tax_amount": "string",
      "closed_quantity": "string",
      "invoiced_quantity": "0",
      "left_quantity": "0",
      "shipped_quantity": "string",
      "left_to_ship_quantity": "string"
    }
  ],
  "invoices": [
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "number": "string",
      "status": "string",
      "document_date": "2026-09-21",
      "net_total": "string",
      "total": "string"
    }
  ],
  "invoiced_net": "0.00",
  "dispatches": [
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "channel": "email",
      "handed_over_on": "2026-09-21",
      "recipients": [
        "string"
      ],
      "subject": "string",
      "filename": "string",
      "sha256": "string",
      "sent_by": {},
      "sent_at": "2026-09-21T09:30:00Z"
    }
  ],
  "sending_configured": true,
  "suggested_recipient": "string",
  "online": {
    "revision": 1,
    "revisions": [
      null
    ],
    "links": [
      null
    ],
    "answer": null
  }
}

GET /v1/sales-documents/{document}/history

Sales Document History

The audit events of a quote or order: changes, hand-overs, decisions and invoices, oldest first.

Required scope read (admin implies every scope)
Operation id sales_document_history_v1_sales_documents__document__history_get

Parameters

In Name Type Required Description
path document string yes Quote or sales order id (uuid) or number, e.g. QT-2026-000001 or SO-2026-000001.

Example request

curl "$ERP_URL/v1/sales-documents/QT-2026-000001/history" \
  -H "Authorization: Bearer $ERP_KEY"

Responses

Status Meaning Body
200 OK array of AuditEventOut
401 Missing or invalid API key (unauthorized). ErrorEnvelope
403 The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). ErrorEnvelope
404 The resource does not exist, or belongs to another organization (not_found). ErrorEnvelope
422 Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. HTTPValidationError

Example response (shape)

[
  {
    "id": 1,
    "at": "2026-09-21T09:30:00Z",
    "actor": {},
    "action": "string",
    "object_type": "string",
    "object_id": "string",
    "request_id": "string",
    "idempotency_key": "string",
    "data": {}
  }
]

POST /v1/sales-documents/{document}/invoice

Invoice Sales Order

Draft the sales invoice of an open order: everything left to invoice, or the quantities lines asks per order line. Each invoice line names its order line (sales_line_id), at the order's price, product and VAT rate type; the VAT is determined on the invoice's day, the customer's reference becomes the order reference and the order's payment terms the due date. Answers the draft invoice, to be checked, posted and sent like any other (POST /v1/documents/{id}/submit). 409 for an order that is not open or has nothing left; 422 for more than is left on a line. Needs an Idempotency-Key.

Required scope draft (admin implies every scope)
Idempotency-Key required
ETag / If-Match If-Match optional: a changed version answers 412
Response header ETag names the current version
Operation id invoice_sales_order_v1_sales_documents__document__invoice_post

Parameters

In Name Type Required Description
path document string yes Quote or sales order id (uuid) or number, e.g. QT-2026-000001 or SO-2026-000001.
header If-Match string or null The ETag of the version you read; a changed quote or order answers 412. (max length 200)

Request body

application/json (required): InvoiceOrderInput

Field Type Required Description
document_date string (date) or null Day of the invoice; default today.
supply_date string (date) or null The date of supply, e.g. the day the goods were delivered; default: the day of the invoice. Where the law of the company's country says so, it decides the VAT period (see supply_date of POST /v1/documents).
lines array of InvoiceLineInput or null What to invoice per order line; omitted: everything left to invoice. (min items 1; max items 200)

Example request

curl -X POST "$ERP_URL/v1/sales-documents/QT-2026-000001/invoice" \
  -H "Authorization: Bearer $ERP_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H 'If-Match: "1"' \
  -H "Content-Type: application/json" \
  -d '{}'

Responses

Status Meaning Body
201 Created DocumentOut, ETag header
401 Missing or invalid API key (unauthorized). ErrorEnvelope
403 The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). ErrorEnvelope
404 The resource does not exist, or belongs to another organization (not_found). ErrorEnvelope
409 The request conflicts with the data: an Idempotency-Key reused with a different request, a closed period, or an integrity rule of the database (conflict, integrity_violation). ErrorEnvelope
412 If-Match names a version that is no longer current (precondition_failed). ErrorEnvelope
422 Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. HTTPValidationError

Example response (shape)

{
  "withholding_code": "string",
  "withholding_rate": "string",
  "withholding_base": "0.00",
  "withholding_amount": "0.00",
  "creditable_amount": "string",
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "entity_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "kind": "string",
  "party_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "party_code": "string",
  "party_name": "string",
  "party_status": "string",
  "number": "string",
  "external_number": "string",
  "status": "string",
  "document_date": "2026-09-21",
  "supply_date": "2026-09-21",
  "due_date": "2026-09-21",
  "currency": "string",
  "net_total": "string",
  "tax_total": "string",
  "total": "string",
  "functional_total": "string",
  "credits_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "journal_entry_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "source_document_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "notes": "string",
  "legal_notes": [
    "string"
  ],
  "payment_iban": "string",
  "payment_reference": "string",
  "buyer_reference": "string",
  "order_reference": "string",
  "open_amount": "string",
  "payment_blocked": false,
  "tax_fx": {},
  "tax_fx_rate": "string",
  "tax_fx_net_total": "string",
  "tax_fx_tax_total": "string",
  "cash_discount_percent": "string",
  "cash_discount_date": "2026-09-21",
  "cash_discount_amount": "string",
  "discount_percent": "string",
  "discount_amount": "0.00",
  "provenance": {},
  "created_by": {},
  "created_at": "2026-09-21T09:30:00Z",
  "version": 1,
  "missing_content": [
    {
      "party": "buyer",
      "field": "name",
      "basis": "string",
      "record_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
    }
  ],
  "source_file": {
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "kind": "string",
    "filename": "string",
    "content_type": "string",
    "size": 1,
    "sha256": "string",
    "created_at": "2026-09-21T09:30:00Z"
  },
  "ic": {
    "link_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "role": "mirror",
    "status": "string",
    "partner_entity": "string",
    "other_document_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
  },
  "lines": [
    {
      "line_no": 1,
      "description": "string",
      "quantity": "string",
      "creditable_quantity": "string",
      "creditable_tax_amount": "string",
      "creditable_discount_amount": "string",
      "unit_price": "string",
      "account_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "product_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "obligation_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "sales_line_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "credits_line": 1,
      "delivery_line_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "goods_returned": false,
      "warehouse_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "as_credited": false,
      "supply_type": "string",
      "rate_type": "string",
      "tax_category": "string",
      "use_tax": false,
      "vat_scope": "in_scope",
      "vat_scope_reason": "damages",
      "discount_percent": "string",
      "discount_amount": "0.00",
      "document_discount_amount": "0.00",
      "net_amount": "string",
      "tax_rate": "string",
      "tax_amount": "string",
      "tax": {},
      "vat_recovery_percent": "100",
      "tax_fx_net_amount": "string",
      "tax_fx_tax_amount": "string",
      "dimensions": {}
    }
  ],
  "delivery_notes": [
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "number": "string"
    }
  ]
}

POST /v1/sales-documents/{document}/links

Create Quote Link

Make a link for a sent quote, to give the customer yourself (your own e-mail, a chat): on it they see the quote's current version and accept or decline it with their name. The token is shown this once; only its hash is kept. A quote this server e-mails carries a link of its own. Only a person makes one (an agent key answers 403). 409 for a quote that is not sent; 422 for an expired one.

Required scope post (admin implies every scope)
Idempotency-Key not used
Operation id create_quote_link_v1_sales_documents__document__links_post

Parameters

In Name Type Required Description
path document string yes Quote or sales order id (uuid) or number, e.g. QT-2026-000001 or SO-2026-000001.

Request body

application/json (required): QuoteLinkInput

Field Type Required Description
handed_to string yes To whom you give the link, for the record. (min length 1; max length 320)

Example request

curl -X POST "$ERP_URL/v1/sales-documents/QT-2026-000001/links" \
  -H "Authorization: Bearer $ERP_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "handed_to": "Anna Muster"
}'

Responses

Status Meaning Body
201 Created QuoteLinkIssued
401 Missing or invalid API key (unauthorized). ErrorEnvelope
403 The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). ErrorEnvelope
404 The resource does not exist, or belongs to another organization (not_found). ErrorEnvelope
409 The request conflicts with the data: an Idempotency-Key reused with a different request, a closed period, or an integrity rule of the database (conflict, integrity_violation). ErrorEnvelope
422 Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. HTTPValidationError

Example response (shape)

{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "prefix": "string",
  "handed_to": "string",
  "created_by": {},
  "created_at": "2026-09-21T09:30:00Z",
  "first_viewed_at": "2026-09-21T09:30:00Z",
  "last_viewed_at": "2026-09-21T09:30:00Z",
  "revoked_at": "2026-09-21T09:30:00Z",
  "token": "string"
}

POST /v1/sales-documents/{document}/links/{quote_link}/revoke

Revoke Quote Link

Turn a quote's link off: its page no longer opens. Final; turning it off again changes nothing.

Required scope post (admin implies every scope)
Idempotency-Key not used
Operation id revoke_quote_link_v1_sales_documents__document__links__quote_link__revoke_post

Parameters

In Name Type Required Description
path document string yes Quote or sales order id (uuid) or number, e.g. QT-2026-000001 or SO-2026-000001.
path quote_link string (uuid) yes Id of the link (online.links[].id).

Example request

curl -X POST "$ERP_URL/v1/sales-documents/QT-2026-000001/links/{quote_link}/revoke" \
  -H "Authorization: Bearer $ERP_KEY"

Responses

Status Meaning Body
200 OK QuoteLinkOut
401 Missing or invalid API key (unauthorized). ErrorEnvelope
403 The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). ErrorEnvelope
404 The resource does not exist, or belongs to another organization (not_found). ErrorEnvelope
409 The request conflicts with the data: an Idempotency-Key reused with a different request, a closed period, or an integrity rule of the database (conflict, integrity_violation). ErrorEnvelope
422 Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. HTTPValidationError

Example response (shape)

{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "prefix": "string",
  "handed_to": "string",
  "created_by": {},
  "created_at": "2026-09-21T09:30:00Z",
  "first_viewed_at": "2026-09-21T09:30:00Z",
  "last_viewed_at": "2026-09-21T09:30:00Z",
  "revoked_at": "2026-09-21T09:30:00Z"
}

POST /v1/sales-documents/{document}/mark-sent

Mark Sales Document Sent

Record that a person handed the PDF over outside this system (their own e-mail, post, the customer's portal); nothing is sent. A draft quote is then sent and its version's PDF kept; POST /v1/sales-documents/{document}/links gives a link to answer it online. Only a person records. 422 for a day before the document or after today. Needs an Idempotency-Key.

Required scope post (admin implies every scope)
Idempotency-Key required
Operation id mark_sales_document_sent_v1_sales_documents__document__mark_sent_post

Parameters

In Name Type Required Description
path document string yes Quote or sales order id (uuid) or number, e.g. QT-2026-000001 or SO-2026-000001.

Request body

application/json (required): SalesMarkSentInput

Field Type Required Description
channel "email" | "other" | "portal" | "post" yes email, post, portal or other.
recipient string yes Who got it. (min length 1; max length 200)
handed_over_on string (date) yes The day it was handed over: not before the document's day, not after today.

Example request

curl -X POST "$ERP_URL/v1/sales-documents/QT-2026-000001/mark-sent" \
  -H "Authorization: Bearer $ERP_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "channel": "post",
  "handed_over_on": "2026-10-12",
  "recipient": "Berlin GmbH"
}'

Responses

Status Meaning Body
201 Created SalesDocumentCard
401 Missing or invalid API key (unauthorized). ErrorEnvelope
403 The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). ErrorEnvelope
404 The resource does not exist, or belongs to another organization (not_found). ErrorEnvelope
409 The request conflicts with the data: an Idempotency-Key reused with a different request, a closed period, or an integrity rule of the database (conflict, integrity_violation). ErrorEnvelope
422 Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. HTTPValidationError

Example response (shape)

{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "kind": "order",
  "number": "string",
  "status": "string",
  "customer": "string",
  "customer_name": "string",
  "customer_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "currency": "string",
  "document_date": "2026-09-21",
  "valid_until": "2026-09-21",
  "customer_reference": "string",
  "opportunity_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "discount_percent": "string",
  "discount_amount": "0.00",
  "net_total": "string",
  "tax_total": "string",
  "total": "string",
  "created_at": "2026-09-21T09:30:00Z",
  "version": 1,
  "revision": 1,
  "entity": "string",
  "payment_terms_days": 1,
  "notes": "string",
  "legal_notes": [
    "string"
  ],
  "reason": "string",
  "decided_by": {},
  "decided_at": "2026-09-21T09:30:00Z",
  "created_by": {},
  "opportunity_number": "string",
  "quote": {
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "number": "string",
    "status": "string"
  },
  "order": {
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "number": "string",
    "status": "string"
  },
  "lines": [
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "line_no": 1,
      "description": "string",
      "product_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "product": "string",
      "supply_type": "string",
      "rate_type": "string",
      "tax_category": "string",
      "quantity": "string",
      "unit_price": "string",
      "discount_percent": "string",
      "discount_amount": "0.00",
      "document_discount_amount": "0.00",
      "net_amount": "string",
      "tax_rate": "string",
      "tax_amount": "string",
      "closed_quantity": "string",
      "invoiced_quantity": "0",
      "left_quantity": "0",
      "shipped_quantity": "string",
      "left_to_ship_quantity": "string"
    }
  ],
  "invoices": [
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "number": "string",
      "status": "string",
      "document_date": "2026-09-21",
      "net_total": "string",
      "total": "string"
    }
  ],
  "invoiced_net": "0.00",
  "dispatches": [
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "channel": "email",
      "handed_over_on": "2026-09-21",
      "recipients": [
        "string"
      ],
      "subject": "string",
      "filename": "string",
      "sha256": "string",
      "sent_by": {},
      "sent_at": "2026-09-21T09:30:00Z"
    }
  ],
  "sending_configured": true,
  "suggested_recipient": "string",
  "online": {
    "revision": 1,
    "revisions": [
      null
    ],
    "links": [
      null
    ],
    "answer": null
  }
}

GET /v1/sales-documents/{document}/pdf

Download a quote or order confirmation

The quote or order confirmation as a PDF in the customer's language, with its lines, VAT, totals and payment terms; a quote says until when it is valid. The header X-Content-SHA256 carries its SHA-256.

Required scope read (admin implies every scope)
Operation id get_sales_document_pdf_v1_sales_documents__document__pdf_get

Parameters

In Name Type Required Description
path document string yes Quote or sales order id (uuid) or number, e.g. QT-2026-000001 or SO-2026-000001.
query download boolean true sends the file as an attachment; false inline. (default false)
query language "de" | "en" | "fr" | "it" | "sv" or null de, fr, it, en or sv; default the customer's.

Example request

curl "$ERP_URL/v1/sales-documents/QT-2026-000001/pdf" \
  -H "Authorization: Bearer $ERP_KEY"

Responses

Status Meaning Body
200 The PDF. application/pdf
401 Missing or invalid API key (unauthorized). ErrorEnvelope
403 The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). ErrorEnvelope
404 The resource does not exist, or belongs to another organization (not_found). ErrorEnvelope
422 Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. HTTPValidationError

GET /v1/sales-documents/{document}/revisions/{revision}/pdf

Download a version of a quote as the customer got it

The PDF of a version of the quote exactly as it was handed over. 404 for a version that was never handed over.

Required scope read (admin implies every scope)
Operation id get_quote_revision_pdf_v1_sales_documents__document__revisions__revision__pdf_get

Parameters

In Name Type Required Description
path document string yes Quote or sales order id (uuid) or number, e.g. QT-2026-000001 or SO-2026-000001.
path revision integer yes The version, from 1. (min 1)
query download boolean true sends the file as an attachment. (default false)

Example request

curl "$ERP_URL/v1/sales-documents/QT-2026-000001/revisions/{revision}/pdf" \
  -H "Authorization: Bearer $ERP_KEY"

Responses

Status Meaning Body
200 The PDF. application/pdf
401 Missing or invalid API key (unauthorized). ErrorEnvelope
403 The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). ErrorEnvelope
404 The resource does not exist, or belongs to another organization (not_found). ErrorEnvelope
422 Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. HTTPValidationError

POST /v1/sales-documents/{document}/send

Send Sales Document

E-mail the PDF to the customer, as an invoice is sent: to to (only the customer's own addresses) or its billing address, within the organization's hourly limit. A draft quote is then sent, and its mail carries a link on which the customer accepts or declines it online (ADR-0088); its version's PDF is kept. Only a person sends (the policy action send_document; an agent key answers 403). 409 in a test copy, for a declined or cancelled document, or when this server has no mail server (download the PDF instead); 422 for an expired quote or an address that is not the customer's; 429 beyond the hourly limit; 502 when the mail server refuses, and nothing is recorded. Needs an Idempotency-Key.

Required scope post (admin implies every scope)
Idempotency-Key required
Operation id send_sales_document_v1_sales_documents__document__send_post

Parameters

In Name Type Required Description
path document string yes Quote or sales order id (uuid) or number, e.g. QT-2026-000001 or SO-2026-000001.

Request body

application/json (required): SalesSendInput

Field Type Required Description
to array of string or null Addresses of the customer (as for an invoice); omitted: its billing address. (min items 1; max items 5)
message string or null A text placed before the standard text. (max length 1000)

Example request

curl -X POST "$ERP_URL/v1/sales-documents/QT-2026-000001/send" \
  -H "Authorization: Bearer $ERP_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{}'

Responses

Status Meaning Body
201 Created SalesDocumentCard
401 Missing or invalid API key (unauthorized). ErrorEnvelope
403 The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). ErrorEnvelope
404 The resource does not exist, or belongs to another organization (not_found). ErrorEnvelope
409 The request conflicts with the data: an Idempotency-Key reused with a different request, a closed period, or an integrity rule of the database (conflict, integrity_violation). ErrorEnvelope
422 Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. HTTPValidationError
429 A per-organization limit was reached, e.g. messages sent per hour (rate_limited, with retry_after_seconds). ErrorEnvelope

Example response (shape)

{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "kind": "order",
  "number": "string",
  "status": "string",
  "customer": "string",
  "customer_name": "string",
  "customer_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "currency": "string",
  "document_date": "2026-09-21",
  "valid_until": "2026-09-21",
  "customer_reference": "string",
  "opportunity_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "discount_percent": "string",
  "discount_amount": "0.00",
  "net_total": "string",
  "tax_total": "string",
  "total": "string",
  "created_at": "2026-09-21T09:30:00Z",
  "version": 1,
  "revision": 1,
  "entity": "string",
  "payment_terms_days": 1,
  "notes": "string",
  "legal_notes": [
    "string"
  ],
  "reason": "string",
  "decided_by": {},
  "decided_at": "2026-09-21T09:30:00Z",
  "created_by": {},
  "opportunity_number": "string",
  "quote": {
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "number": "string",
    "status": "string"
  },
  "order": {
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "number": "string",
    "status": "string"
  },
  "lines": [
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "line_no": 1,
      "description": "string",
      "product_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "product": "string",
      "supply_type": "string",
      "rate_type": "string",
      "tax_category": "string",
      "quantity": "string",
      "unit_price": "string",
      "discount_percent": "string",
      "discount_amount": "0.00",
      "document_discount_amount": "0.00",
      "net_amount": "string",
      "tax_rate": "string",
      "tax_amount": "string",
      "closed_quantity": "string",
      "invoiced_quantity": "0",
      "left_quantity": "0",
      "shipped_quantity": "string",
      "left_to_ship_quantity": "string"
    }
  ],
  "invoices": [
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "number": "string",
      "status": "string",
      "document_date": "2026-09-21",
      "net_total": "string",
      "total": "string"
    }
  ],
  "invoiced_net": "0.00",
  "dispatches": [
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "channel": "email",
      "handed_over_on": "2026-09-21",
      "recipients": [
        "string"
      ],
      "subject": "string",
      "filename": "string",
      "sha256": "string",
      "sent_by": {},
      "sent_at": "2026-09-21T09:30:00Z"
    }
  ],
  "sending_configured": true,
  "suggested_recipient": "string",
  "online": {
    "revision": 1,
    "revisions": [
      null
    ],
    "links": [
      null
    ],
    "answer": null
  }
}

Schemas

AcceptInput

Field Type Required Description
accepted_on string (date) or null The day the customer accepted, within the quote's validity and not after today; default today. It is the day of the order.
customer_reference string or null The customer's purchase order number, if any. (max length 200)

InvoiceLineInput

Field Type Required Description
line_no integer yes Number of the order line. (min 1)
quantity decimal string yes Quantity to invoice now. (greater than 0)

InvoiceOrderInput

Field Type Required Description
document_date string (date) or null Day of the invoice; default today.
supply_date string (date) or null The date of supply, e.g. the day the goods were delivered; default: the day of the invoice. Where the law of the company's country says so, it decides the VAT period (see supply_date of POST /v1/documents).
lines array of InvoiceLineInput or null What to invoice per order line; omitted: everything left to invoice. (min items 1; max items 200)

OnlineAcceptInput

Field Type Required Description
name string yes Your full name. (min length 1; max length 200)
email string or null Your e-mail address. (max length 320)
revision integer yes The version you read (revision of the page). (min 1)
agree true yes You accept the quote for your company.
customer_reference string or null Your purchase order number, if any. (max length 200)

OnlineDeclineInput

Field Type Required Description
name string yes Your full name. (min length 1; max length 200)
email string or null Your e-mail address. (max length 320)
revision integer yes The version you read. (min 1)
reason string or null Why, if you like. (max length 500)

OnlineLine

Field Type Required Description
position integer yes Position, from 1.
description string yes What is offered.
quantity string yes Quantity.
unit_price string yes Net unit price.
discount_percent string or null yes Line discount in percent, if asked so.
discount_amount string yes Line discount in money; 0 for none.
vat_rate string yes VAT rate in percent.
net_amount string yes Net of the line after its own discount.

OnlineVat

Field Type Required Description
rate string yes VAT rate in percent.
name string or null yes The tax's name where a line bears several.
net string yes Net at this rate.
tax string yes VAT at this rate.

OrderInvoiceOut

An invoice made from the order.

Field Type Required Description
id string (uuid) yes Id of the invoice (GET /v1/documents/{id}).
number string or null yes Its number; null before it is posted.
status string yes draft, pending_approval, posted, rejected or void.
document_date string (date) yes Day of the invoice.
net_total string yes Net total of the invoice.
total string yes Total including VAT.

QuoteAnswerOut

Field Type Required Description
revision integer yes The version answered.
decision "accepted" | "declined" yes accepted or declined.
signer_name string yes The name the customer's person gave.
signer_email string or null yes The address they gave; null if none.
reason string or null yes Why they declined, if they said.
link_prefix string yes The link it was given on.
sha256 string yes SHA-256 of the PDF of the version answered.
client_address string yes The internet address it came from.
user_agent string or null yes The browser, as it named itself.
answered_at string (date-time) yes When (UTC).

QuoteLinkInput

Field Type Required Description
handed_to string yes To whom you give the link, for the record. (min length 1; max length 320)

QuoteLinkIssued

Field Type Required Description
id string (uuid) yes Id of the link.
prefix string yes The link's public part, shown in the customer's answer.
handed_to string yes To whom it was given.
created_by object yes Who made it.
created_at string (date-time) yes When (UTC).
first_viewed_at string (date-time) or null yes When the customer first opened it.
last_viewed_at string (date-time) or null yes When the customer last opened it.
revoked_at string (date-time) or null yes When it was turned off; null if it works.
token string yes The link's secret, shown this once: the customer's page is /app/quote/{token}. Only its hash is kept.

QuoteLinkOut

Field Type Required Description
id string (uuid) yes Id of the link.
prefix string yes The link's public part, shown in the customer's answer.
handed_to string yes To whom it was given.
created_by object yes Who made it.
created_at string (date-time) yes When (UTC).
first_viewed_at string (date-time) or null yes When the customer first opened it.
last_viewed_at string (date-time) or null yes When the customer last opened it.
revoked_at string (date-time) or null yes When it was turned off; null if it works.

QuoteOnlineCard

Of a quote: its version, the versions handed over, its links and the online answer.

Field Type Required Description
revision integer yes The current version.
revisions array of QuoteRevisionOut yes Versions handed over, the first first.
links array of QuoteLinkOut yes Links given to the customer, oldest first.
answer QuoteAnswerOut or null yes The customer's answer online; null if none.

QuoteOnlineView

The quote as its customer sees it on the link.

Field Type Required Description
state "accepted" | "declined" | "expired" | "open" | "revising" | "withdrawn" yes open (it can be answered), expired, revising (the seller is changing it: a new version follows), accepted, declined or withdrawn.
language "de" | "en" | "fr" | "it" | "sv" yes The language of the texts.
test_copy boolean yes From a test copy: not a real offer.
seller_name string yes Who makes the offer.
seller_lines array of string yes The seller's address and contact.
seller_logo string or null The seller's logo as a data:image/png;base64, address; null: none.
seller_colour string or null The seller's brand colour, #rrggbb; null: the default.
customer_name string yes To whom.
buyer_lines array of string yes The customer's address as printed.
number string yes The quote's number.
revision integer yes Its version.
document_date string (date) yes Its day.
valid_until string (date) yes The last day to accept it.
currency string yes ISO 4217 currency.
customer_reference string or null yes The customer's own reference.
payment_terms_days integer yes Days to pay the invoices of the order.
lines array of OnlineLine yes The lines; empty while it is being changed.
vat array of OnlineVat yes VAT by rate.
discount_percent string or null yes Document discount in percent.
discount_amount string yes Document discount in money.
net_total string yes Net total.
tax_total string yes VAT.
total string yes Total including VAT.
notes array of string yes Notes and the VAT remarks the lines need.
answered_by string or null yes Who answered online; null if not online.
answered_at string (date-time) or null yes When it was accepted or declined.

QuoteRevisionOut

Field Type Required Description
revision integer yes The version, from 1.
document_date string (date) yes The quote's day in that version.
valid_until string (date) yes The last day to accept that version.
currency string yes ISO 4217 currency.
net_total string yes Net total, after discounts.
tax_total string yes VAT.
total string yes Total including VAT.
sha256 string yes SHA-256 of the PDF the customer got.
created_by object yes Who handed it over first.
created_at string (date-time) yes When it was first handed over (UTC).

SalesDispatchOut

Field Type Required Description
id string (uuid) yes Id of the hand-over.
channel "email" | "other" | "portal" | "post" | "system_email" yes system_email (this server sent it) or what a person recorded.
handed_over_on string (date) yes The day it was handed over.
recipients array of string yes To whom.
subject string or null yes Subject of the e-mail; null for a recorded one.
filename string yes The PDF handed over.
sha256 string yes SHA-256 of that PDF, hex.
sent_by object yes Who: type, id and name.
sent_at string (date-time) yes When it was sent or recorded (UTC).

SalesDocumentCard

Field Type Required Description
id string (uuid) yes Id of the quote or order.
kind "order" | "quote" yes quote or order.
number string yes QT-2026-000001 or SO-2026-000001, per entity and year.
status string yes A quote is draft, sent, expired (draft or sent, and its valid_until has passed), accepted, declined or cancelled. An order is draft, open, partially_invoiced, invoiced, closed (for the rest, with a reason) or cancelled.
customer string yes Customer code.
customer_name string yes Name of the customer.
customer_id string (uuid) yes Id of the customer.
currency string yes ISO 4217 currency.
document_date string (date) yes Day of the quote or order.
valid_until string (date) or null yes Quotes: last day to accept; null on orders.
customer_reference string or null yes The customer's reference; null if none.
opportunity_id string (uuid) or null The deal it is for; null if none.
discount_percent string or null The document discount in percent; null for an amount or none.
discount_amount string The document discount in money; 0 for none. (default "0.00")
net_total string yes Net total, after all discounts.
tax_total string yes VAT, as the invoice would determine it on the day.
total string yes Total including VAT.
created_at string (date-time) yes When it was created (UTC).
version integer yes Raised on every change; the ETag names it.
revision integer Quotes: the version, from 1; changing a sent quote begins the next (it is a draft until sent again). Orders: 1. (default 1)
entity string yes Code of the entity.
payment_terms_days integer yes Payment terms in days.
notes string or null yes Free text printed under the lines.
legal_notes array of string yes Remarks the VAT treatment of the lines requires.
reason string or null yes Why it was declined, cancelled or closed for the rest; null otherwise.
decided_by object or null yes Who accepted, declined, cancelled or closed it; null otherwise.
decided_at string (date-time) or null yes When (UTC); null otherwise.
created_by object yes Who created it (type, id, name).
opportunity_number string or null The number of the deal it is for; null if none.
quote SalesLinkOut or null yes Of an order: the quote it was made from.
order SalesLinkOut or null yes Of an accepted quote: the order it became.
lines array of SalesLineOut yes The lines.
invoices array of OrderInvoiceOut Of an order: the invoices made from it, oldest first.
invoiced_net string Of an order: the net invoiced on invoices that are not void or rejected. (default "0.00")
dispatches array of SalesDispatchOut yes Hand-overs, oldest first.
sending_configured boolean yes Whether this server can e-mail it (a mail server, and not a test copy).
suggested_recipient string or null yes Where an e-mail goes without addresses given; null when the customer has none.
online QuoteOnlineCard or null Of a quote: the versions handed over, the links given to answer it online and the customer's answer there (ADR-0088); null on an order.

SalesDocumentInput

Field Type Required Description
customer string yes Code (or id) of an active customer of the entity.
document_date string (date) or null Day of the quote or order (ISO 8601); default today where the company is.
valid_until string (date) or null Quotes: the last day the customer may accept it, not before the quote's day; default 30 days after it. Orders take none.
currency string or null ISO 4217 code; default the customer's. (pattern ^[A-Z]{3}$)
customer_reference string or null The customer's reference, e.g. its request or purchase order number; printed and carried to the invoice as its order reference. (max length 200)
payment_terms_days integer or null Payment terms in days; default the customer's. (min 0; max 365)
notes string or null Printed under the lines. (max length 2000)
opportunity_id string (uuid) or null The deal it is for (GET /v1/opportunities): one of the same customer, not lost. It never changes, and an order made from the quote is for the same deal.
lines array of SalesLineInput yes The lines. (min items 1; max items 200)
discount_percent decimal string or null A discount on the whole document in percent of the lines after their own discounts; split over the lines, so the VAT of each rate goes down by its share. Give this or discount_amount, not both. (max 100; greater than 0)
discount_amount decimal string or null A discount on the whole document as an amount, split over the lines like discount_percent; an invoice of part of an order takes its part of it. (greater than 0)

SalesDocumentOut

Field Type Required Description
id string (uuid) yes Id of the quote or order.
kind "order" | "quote" yes quote or order.
number string yes QT-2026-000001 or SO-2026-000001, per entity and year.
status string yes A quote is draft, sent, expired (draft or sent, and its valid_until has passed), accepted, declined or cancelled. An order is draft, open, partially_invoiced, invoiced, closed (for the rest, with a reason) or cancelled.
customer string yes Customer code.
customer_name string yes Name of the customer.
customer_id string (uuid) yes Id of the customer.
currency string yes ISO 4217 currency.
document_date string (date) yes Day of the quote or order.
valid_until string (date) or null yes Quotes: last day to accept; null on orders.
customer_reference string or null yes The customer's reference; null if none.
opportunity_id string (uuid) or null The deal it is for; null if none.
discount_percent string or null The document discount in percent; null for an amount or none.
discount_amount string The document discount in money; 0 for none. (default "0.00")
net_total string yes Net total, after all discounts.
tax_total string yes VAT, as the invoice would determine it on the day.
total string yes Total including VAT.
created_at string (date-time) yes When it was created (UTC).
version integer yes Raised on every change; the ETag names it.
revision integer Quotes: the version, from 1; changing a sent quote begins the next (it is a draft until sent again). Orders: 1. (default 1)

SalesDocumentUpdate

Changes to a draft (a sent quote goes back to draft and is sent again). Omitted fields keep their value; lines replaces every line.

Field Type Required Description
customer string or null Code (or id) of an active customer.
document_date string (date) or null New day of the document.
valid_until string (date) or null Quotes: new last day.
currency string or null ISO 4217 code. (pattern ^[A-Z]{3}$)
customer_reference string or null The customer's reference; null clears it. (max length 200)
payment_terms_days integer or null Payment terms in days, 0 to 365. (min 0; max 365)
notes string or null Printed under the lines; null clears it. (max length 2000)
lines array of SalesLineInput or null Replaces every line. (min items 1; max items 200)
discount_percent decimal string or null A document discount in percent; replaces any. null removes it. (max 100; greater than 0)
discount_amount decimal string or null A document discount as an amount; replaces any. null removes it. (greater than 0)

SalesLineInput

Field Type Required Description
description string or null What is offered; default the product's name (required on a line without a product). (min length 1; max length 500)
quantity decimal string Quantity, a decimal string greater than 0. (greater than 0; default "1")
unit_price decimal string or null Net unit price in the document's currency; default the customer's price from its price list, else the product's list price on the document's day (required on a line without a product; 422 product_unpriced when the product has no price). (min 0)
product string or null Code (or id) of an active product of the entity (GET /v1/products): the line takes its name, price, supply and rate type unless it gives its own, and the invoice its revenue account.
supply_type "electronic_services" | "goods" | "services" goods, services (default) or electronic_services. (default "services")
rate_type "exempt" | "parking" | "reduced" | "special" | "standard" | "super_reduced" | "zero" Which VAT rate applies; default standard. (default "standard")
tax_category string or null One of the categories of the rate (GET /v1/entities/{entity}/vat-rate-types), e.g. books: it chooses the rate where the rate type has several (France reduced: 5.5 % or 10 %) and names the kind of an exempt supply (insurance). Omit for none. (max length 200)
discount_percent decimal string or null A discount on the line in percent (above 0, at most 100); carried to the order and its invoices. Give this or discount_amount, not both. (max 100; greater than 0)
discount_amount decimal string or null A discount on the line as an amount off quantity times unit price; an invoice of part of the line takes its part of it. (greater than 0)

SalesLineOut

Field Type Required Description
id string (uuid) yes Id of the line.
line_no integer yes Position, from 1.
description string yes What is offered or ordered.
product_id string (uuid) or null yes Id of the product; null for a free line.
product string or null Code of the product; null for none.
supply_type string yes goods, services or electronic_services.
rate_type string yes The VAT rate type.
tax_category string or null yes Named tax category; null when none.
quantity string yes Quantity.
unit_price string yes Net unit price.
discount_percent string or null The line discount in percent; null for an amount or none.
discount_amount string The line discount in money; 0 for none. (default "0.00")
document_discount_amount string The line's share of the document discount. (default "0.00")
net_amount string yes Quantity times unit price, rounded, less the line discount and its share of the document discount.
tax_rate string yes VAT rate in percent, as the invoice would charge it.
tax_amount string yes VAT of the line.
closed_quantity string yes Of an order closed for the rest: what will not be invoiced any more.
invoiced_quantity string Of an order: the quantity on its invoices that are not void or rejected (drafts too). (default "0")
left_quantity string Of an open order: what is left to invoice (ordered, less closed, less invoiced); 0 otherwise. (default "0")
shipped_quantity string or null Of an order line of a stock item: what left on delivery notes (drafts too) and with invoices that ship it; null for other lines.
left_to_ship_quantity string or null Of an open order's line of a stock item: what is left to ship (ordered, less closed, less shipped); 0 on an order no longer open; null for other lines.

SalesLinkOut

Field Type Required Description
id string (uuid) yes Id of the linked quote or order.
number string yes Its number.
status string yes Its status.

SalesMarkSentInput

Field Type Required Description
channel "email" | "other" | "portal" | "post" yes email, post, portal or other.
recipient string yes Who got it. (min length 1; max length 200)
handed_over_on string (date) yes The day it was handed over: not before the document's day, not after today.

SalesSendInput

Field Type Required Description
to array of string or null Addresses of the customer (as for an invoice); omitted: its billing address. (min items 1; max items 5)
message string or null A text placed before the standard text. (max length 1000)

erp__features__crm__contracts__sales__ReasonInput

Field Type Required Description
reason string yes Why; kept on the document. (min length 1; max length 500)
Stabiso, the Enterprise System of Record · generated from the code and its guides · release 20b7dd5b0c4d