Products
What the entities sell: products with the revenue account, supply type and VAT rate type their invoice lines take, list prices by currency from a day, and the standalone selling prices a person records with their basis. Nothing here posts.
| Operation | Summary | Scope | Idempotency-Key |
|---|---|---|---|
GET /v1/products |
List products | read |
|
POST /v1/products |
Create a product | draft |
required |
GET /v1/products/{product_id} |
Get a product | read |
|
PATCH /v1/products/{product_id} |
Change a product | draft |
|
GET /v1/products/{product_id}/price |
Get the price in force | read |
|
POST /v1/products/{product_id}/prices |
Record a list price | draft |
required |
POST /v1/products/{product_id}/ssps |
Record a standalone selling price | draft |
required |
GET /v1/products
List products
What the entities sell, by code, each with its list prices and standalone selling prices. Read-only.
| Required scope | read (admin implies every scope) |
| Operation id | list_products_v1_products_get |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| query | entity |
string or null | Only this entity's products (code or id). | |
| query | kind |
"one_off" | "recurring" or null |
Only this kind. | |
| query | active |
boolean or null | true: only those sold; false: only inactive ones. |
|
| query | limit |
integer | How many, by code. (min 1; max 1000; default 100) |
Example request
curl "$ERP_URL/v1/products" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | array of ProductOut |
| 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 |
| 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)
[
{
"active": true,
"code": "SEAT",
"created_at": "2026-10-04T08:00:00Z",
"created_by": {
"id": "user:alice",
"name": "Alice",
"type": "human"
},
"entity": "CH01",
"id": "5b8c9d0e-1f2a-4b3c-8d4e-5f6a7b8c9d0e",
"kind": "recurring",
"name": "Platform seat",
"prices": [
{
"created_at": "2026-10-04T08:00:00Z",
"created_by": {
"id": "user:alice",
"name": "Alice",
"type": "human"
},
"currency": "CHF",
"id": "7d0e1f2a-3b4c-4d5e-8f6a-7b8c9d0e1f2a",
"price_period": "month",
"unit_price": "50",
"valid_from": "2026-10-04"
}
],
"rate_type": "standard",
"revenue_account": "3400",
"revenue_account_id": "6c9d0e1f-2a3b-4c4d-9e5f-6a7b8c9d0e1f",
"ssps": [],
"supply_type": "electronic_services",
"unit": "seat"
}
]
POST /v1/products
Create a product
A product of an entity, with the revenue account (a revenue account of the entity; default
the entity's default revenue account), supply type and VAT rate type its invoice lines take,
and optionally its first list prices. 409 when the entity has a product with the code; 422
for an account that is not a revenue account or a price without the period its kind needs.
Nothing is posted. Needs an Idempotency-Key.
| Required scope | draft (admin implies every scope) |
| Idempotency-Key | required |
| Operation id | create_product_v1_products_post |
Request body
application/json (required): ProductInput
| Field | Type | Required | Description |
|---|---|---|---|
entity |
string | yes | Code (or id) of the entity that sells it. |
code |
string or null | Leave it out and the product gets the company's next number (P-00001, counted without gaps); give one (e.g. SEAT, an article number of yours) to keep it. Unique in the entity and never changed. (pattern ^[A-Za-z0-9][A-Za-z0-9._-]{0,39}$) |
|
name |
string | yes | What it is called on invoices. (min length 1; max length 200) |
description |
string or null | More detail. (max length 1000) | |
kind |
"one_off" | "recurring" |
yes | recurring (priced per month or year, e.g. a seat) or one_off (priced per unit, e.g. a day of onboarding). Never changed. |
unit |
string | What one is: seat, licence, day, piece, ...; default unit. (min length 1; max length 40; default "unit") |
|
revenue_account |
string or null | Revenue account (code or id) of the entity its lines are booked to; omitted: the entity's revenue from goods for goods where its chart has one, else its default revenue account. |
|
supply_type |
"electronic_services" | "goods" | "services" |
goods, services (default) or electronic_services, for its lines' VAT. (default "services") |
|
rate_type |
"exempt" | "parking" | "reduced" | "special" | "standard" | "super_reduced" | "zero" |
The VAT rate type of its lines; default standard. (default "standard") |
|
prices |
array of PriceInput |
List prices to record with it. (max items 50) |
Example request
curl -X POST "$ERP_URL/v1/products" \
-H "Authorization: Bearer $ERP_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"code": "SEAT",
"entity": "CH01",
"kind": "recurring",
"name": "Platform seat",
"prices": [
{
"currency": "CHF",
"price_period": "month",
"unit_price": "50.00"
}
],
"revenue_account": "3400",
"supply_type": "electronic_services",
"unit": "seat"
}'
Responses
| Status | Meaning | Body |
|---|---|---|
| 201 | Created | ProductOut |
| 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 |
| 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)
{
"active": true,
"code": "SEAT",
"created_at": "2026-10-04T08:00:00Z",
"created_by": {
"id": "user:alice",
"name": "Alice",
"type": "human"
},
"entity": "CH01",
"id": "5b8c9d0e-1f2a-4b3c-8d4e-5f6a7b8c9d0e",
"kind": "recurring",
"name": "Platform seat",
"prices": [
{
"created_at": "2026-10-04T08:00:00Z",
"created_by": {
"id": "user:alice",
"name": "Alice",
"type": "human"
},
"currency": "CHF",
"id": "7d0e1f2a-3b4c-4d5e-8f6a-7b8c9d0e1f2a",
"price_period": "month",
"unit_price": "50",
"valid_from": "2026-10-04"
}
],
"rate_type": "standard",
"revenue_account": "3400",
"revenue_account_id": "6c9d0e1f-2a3b-4c4d-9e5f-6a7b8c9d0e1f",
"ssps": [],
"supply_type": "electronic_services",
"unit": "seat"
}
GET /v1/products/{product_id}
Get a product
One product with every list price and standalone selling price. 404 when it does not exist.
| Required scope | read (admin implies every scope) |
| Operation id | get_product_v1_products__product_id__get |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | product_id |
string (uuid) | yes | Id of the product (GET /v1/products). |
Example request
curl "$ERP_URL/v1/products/5b8c9d0e-1f2a-4b3c-8d4e-5f6a7b8c9d0e" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | ProductOut |
| 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)
{
"active": true,
"code": "SEAT",
"created_at": "2026-10-04T08:00:00Z",
"created_by": {
"id": "user:alice",
"name": "Alice",
"type": "human"
},
"entity": "CH01",
"id": "5b8c9d0e-1f2a-4b3c-8d4e-5f6a7b8c9d0e",
"kind": "recurring",
"name": "Platform seat",
"prices": [
{
"created_at": "2026-10-04T08:00:00Z",
"created_by": {
"id": "user:alice",
"name": "Alice",
"type": "human"
},
"currency": "CHF",
"id": "7d0e1f2a-3b4c-4d5e-8f6a-7b8c9d0e1f2a",
"price_period": "month",
"unit_price": "50",
"valid_from": "2026-10-04"
}
],
"rate_type": "standard",
"revenue_account": "3400",
"revenue_account_id": "6c9d0e1f-2a3b-4c4d-9e5f-6a7b8c9d0e1f",
"ssps": [],
"supply_type": "electronic_services",
"unit": "seat"
}
PATCH /v1/products/{product_id}
Change a product
Change what the body names; active: false stops new invoice lines from naming it (a credit
note still credits it). Its entity, code and kind never change, and lines already written keep
what they took from it. The change is audited with the previous values.
| Required scope | draft (admin implies every scope) |
| Idempotency-Key | not used |
| Operation id | update_product_v1_products__product_id__patch |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | product_id |
string (uuid) | yes | Id of the product (GET /v1/products). |
Request body
application/json (required): ProductUpdate
| Field | Type | Required | Description |
|---|---|---|---|
name |
string or null | New name. (min length 1; max length 200) | |
description |
string or null | "" clears. (max length 1000) |
|
unit |
string or null | New unit. (min length 1; max length 40) | |
revenue_account |
string or null | New revenue account (code or id) for new lines. | |
supply_type |
"electronic_services" | "goods" | "services" or null |
New supply type. | |
rate_type |
"exempt" | "parking" | "reduced" | "special" | "standard" | "super_reduced" | "zero" or null |
New VAT rate type. | |
active |
boolean or null | false: no new line names it; true: it is sold again. |
Example request
curl -X PATCH "$ERP_URL/v1/products/5b8c9d0e-1f2a-4b3c-8d4e-5f6a7b8c9d0e" \
-H "Authorization: Bearer $ERP_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Platform seat (Pro)"
}'
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | ProductOut |
| 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)
{
"active": true,
"code": "SEAT",
"created_at": "2026-10-04T08:00:00Z",
"created_by": {
"id": "user:alice",
"name": "Alice",
"type": "human"
},
"entity": "CH01",
"id": "5b8c9d0e-1f2a-4b3c-8d4e-5f6a7b8c9d0e",
"kind": "recurring",
"name": "Platform seat",
"prices": [
{
"created_at": "2026-10-04T08:00:00Z",
"created_by": {
"id": "user:alice",
"name": "Alice",
"type": "human"
},
"currency": "CHF",
"id": "7d0e1f2a-3b4c-4d5e-8f6a-7b8c9d0e1f2a",
"price_period": "month",
"unit_price": "50",
"valid_from": "2026-10-04"
}
],
"rate_type": "standard",
"revenue_account": "3400",
"revenue_account_id": "6c9d0e1f-2a3b-4c4d-9e5f-6a7b8c9d0e1f",
"ssps": [],
"supply_type": "electronic_services",
"unit": "seat"
}
GET /v1/products/{product_id}/price
Get the price in force
The list price and the standalone selling price in force on a day: what an invoice line of
the product proposes. With party, also the customer's price (customer_price), from the
active price list the customer takes in the currency, for the quantity: a line proposes it
before the list price. 404 for an unknown customer. Read-only.
| Required scope | read (admin implies every scope) |
| Operation id | get_price_in_force_v1_products__product_id__price_get |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | product_id |
string (uuid) | yes | Id of the product (GET /v1/products). |
| query | currency |
string | yes | Currency. (pattern ^[A-Z]{3}$) |
| query | on |
string (date) or null | The day; default today. | |
| query | party |
string or null | A customer of the product's entity (code or id): the answer then also carries the customer's price from its price list in the currency, if any. | |
| query | quantity |
decimal string or null | The quantity of the line, which picks the volume tier of the customer's price; default 1. (greater than 0) |
Example request
curl "$ERP_URL/v1/products/5b8c9d0e-1f2a-4b3c-8d4e-5f6a7b8c9d0e/price?currency=CHF" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | PriceInForce |
| 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)
{
"code": "SEAT",
"currency": "CHF",
"on": "2026-10-04",
"price": {
"created_at": "2026-10-04T08:00:00Z",
"created_by": {
"id": "user:alice",
"name": "Alice",
"type": "human"
},
"currency": "CHF",
"id": "7d0e1f2a-3b4c-4d5e-8f6a-7b8c9d0e1f2a",
"price_period": "month",
"unit_price": "50",
"valid_from": "2026-10-04"
},
"product_id": "5b8c9d0e-1f2a-4b3c-8d4e-5f6a7b8c9d0e"
}
POST /v1/products/{product_id}/prices
Record a list price
A list price in a currency from a day (default today). A price is never changed: a new price
is a new one from a later day, and the one in force on a day is the latest from on or before
it. 409 when the product has a price in the currency from that day. Needs an
Idempotency-Key.
| Required scope | draft (admin implies every scope) |
| Idempotency-Key | required |
| Operation id | add_product_price_v1_products__product_id__prices_post |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | product_id |
string (uuid) | yes | Id of the product (GET /v1/products). |
Request body
application/json (required): PriceInput
| Field | Type | Required | Description |
|---|---|---|---|
currency |
string or null | ISO 4217 code, e.g. CHF. Omitted: the entity's functional currency. (pattern ^[A-Z]{3}$) |
|
unit_price |
decimal string | yes | Net list price of one unit, e.g. 50.00. (min 0) |
price_period |
"month" | "year" or null |
month or year for a recurring product (the price of one unit for that long); omitted for a one-off product (the price of one unit). |
|
valid_from |
string (date) or null | The first day the price applies; omitted: today. |
Example request
curl -X POST "$ERP_URL/v1/products/5b8c9d0e-1f2a-4b3c-8d4e-5f6a7b8c9d0e/prices" \
-H "Authorization: Bearer $ERP_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"currency": "CHF",
"price_period": "month",
"unit_price": "50.00"
}'
Responses
| Status | Meaning | Body |
|---|---|---|
| 201 | Created | PriceOut |
| 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",
"currency": "string",
"unit_price": "string",
"price_period": "month",
"valid_from": "2026-09-21",
"created_by": {},
"created_at": "2026-09-21T09:30:00Z"
}
POST /v1/products/{product_id}/ssps
Record a standalone selling price
The price the product sells for on its own (IFRS 15.77), with how it was established, in a
currency from a day (default today). It allocates the price of a revenue contract to its
obligations, so a person records it: the policy action set_ssp, which agents are always
denied (403). Never changed, like a list price (409 for a second one from the same day).
Needs an Idempotency-Key.
| Required scope | draft (admin implies every scope) |
| Idempotency-Key | required |
| Operation id | add_product_ssp_v1_products__product_id__ssps_post |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | product_id |
string (uuid) | yes | Id of the product (GET /v1/products). |
Request body
application/json (required): SspInput
| Field | Type | Required | Description |
|---|---|---|---|
currency |
string or null | ISO 4217 code, e.g. CHF. Omitted: the entity's functional currency. (pattern ^[A-Z]{3}$) |
|
ssp |
decimal string | yes | The price the product sells for on its own, per unit and period. (min 0) |
price_period |
"month" | "year" or null |
month or year for a recurring product (the price of one unit for that long); omitted for a one-off product (the price of one unit). |
|
ssp_basis |
string | yes | How it was established (IFRS 15.77-79): an observable price, adjusted market assessment, expected cost plus a margin, or the residual approach, and the evidence. (min length 3; max length 2000) |
valid_from |
string (date) or null | The first day it applies; omitted: today. |
Example request
curl -X POST "$ERP_URL/v1/products/5b8c9d0e-1f2a-4b3c-8d4e-5f6a7b8c9d0e/ssps" \
-H "Authorization: Bearer $ERP_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"currency": "CHF",
"price_period": "month",
"ssp": "50.00",
"ssp_basis": "Observable: the list price, at which 90 % of seats sold in 2026 without a discount (IFRS 15.77).",
"valid_from": "2026-01-01"
}'
Responses
| Status | Meaning | Body |
|---|---|---|
| 201 | Created | SspOut |
| 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",
"currency": "string",
"ssp": "string",
"price_period": "month",
"ssp_basis": "string",
"valid_from": "2026-09-21",
"created_by": {},
"created_at": "2026-09-21T09:30:00Z"
}
Schemas
CustomerPrice
| Field | Type | Required | Description |
|---|---|---|---|
price_list_id |
string (uuid) | yes | The customer's price list. |
price_list |
string | yes | Its code. |
price_list_name |
string | yes | Its name. |
price_id |
string (uuid) | yes | The price on the list. |
currency |
string | yes | Currency. |
unit_price |
string | yes | Net price of one unit for the customer. |
price_period |
"month" | "year" or null |
yes | Per month or year, or per unit. |
min_quantity |
string | yes | The tier it came from: its smallest quantity. |
valid_from |
string (date) | yes | The first day it applies. |
PriceInForce
| Field | Type | Required | Description |
|---|---|---|---|
product_id |
string (uuid) | yes | Product id. |
code |
string | yes | Product code. |
currency |
string | yes | The currency asked for. |
on |
string (date) | yes | The day asked for. |
price |
PriceOut or null |
yes | The list price in force: the latest from on or before the day; null when there is none in the currency. |
ssp |
SspOut or null |
yes | The standalone selling price in force, likewise. |
party |
string or null | The customer asked for (its code), when one was. | |
quantity |
string or null | The quantity asked for, when a customer was (default 1). | |
customer_price |
CustomerPrice or null |
The customer's price, when the customer takes an active price list in the currency with a price of the product for the quantity on the day: the highest tier at or below the quantity, its latest from on or before the day, with the list and tier it came from. What a line proposes before the list price; null otherwise. |
PriceInput
| Field | Type | Required | Description |
|---|---|---|---|
currency |
string or null | ISO 4217 code, e.g. CHF. Omitted: the entity's functional currency. (pattern ^[A-Z]{3}$) |
|
unit_price |
decimal string | yes | Net list price of one unit, e.g. 50.00. (min 0) |
price_period |
"month" | "year" or null |
month or year for a recurring product (the price of one unit for that long); omitted for a one-off product (the price of one unit). |
|
valid_from |
string (date) or null | The first day the price applies; omitted: today. |
PriceOut
| Field | Type | Required | Description |
|---|---|---|---|
id |
string (uuid) | yes | Price id. |
currency |
string | yes | Currency. |
unit_price |
string | yes | Net list price of one unit. |
price_period |
"month" | "year" or null |
yes | Per month or year, or per unit. |
valid_from |
string (date) | yes | The first day it applies. |
created_by |
object | yes | Who recorded it. |
created_at |
string (date-time) | yes | When (UTC). |
ProductInput
| Field | Type | Required | Description |
|---|---|---|---|
entity |
string | yes | Code (or id) of the entity that sells it. |
code |
string or null | Leave it out and the product gets the company's next number (P-00001, counted without gaps); give one (e.g. SEAT, an article number of yours) to keep it. Unique in the entity and never changed. (pattern ^[A-Za-z0-9][A-Za-z0-9._-]{0,39}$) |
|
name |
string | yes | What it is called on invoices. (min length 1; max length 200) |
description |
string or null | More detail. (max length 1000) | |
kind |
"one_off" | "recurring" |
yes | recurring (priced per month or year, e.g. a seat) or one_off (priced per unit, e.g. a day of onboarding). Never changed. |
unit |
string | What one is: seat, licence, day, piece, ...; default unit. (min length 1; max length 40; default "unit") |
|
revenue_account |
string or null | Revenue account (code or id) of the entity its lines are booked to; omitted: the entity's revenue from goods for goods where its chart has one, else its default revenue account. |
|
supply_type |
"electronic_services" | "goods" | "services" |
goods, services (default) or electronic_services, for its lines' VAT. (default "services") |
|
rate_type |
"exempt" | "parking" | "reduced" | "special" | "standard" | "super_reduced" | "zero" |
The VAT rate type of its lines; default standard. (default "standard") |
|
prices |
array of PriceInput |
List prices to record with it. (max items 50) |
ProductOut
| Field | Type | Required | Description |
|---|---|---|---|
id |
string (uuid) | yes | Product id. |
entity |
string | yes | Entity code. |
code |
string | yes | Code, unique in the entity. |
name |
string | yes | Name. |
description |
string or null | yes | More detail. |
kind |
"one_off" | "recurring" |
yes | recurring or one_off. |
unit |
string | yes | What one is. |
revenue_account |
string | yes | Code of the revenue account. |
revenue_account_id |
string (uuid) | yes | Id of the revenue account. |
supply_type |
string | yes | Supply type of its lines. |
rate_type |
string | yes | VAT rate type of its lines. |
active |
boolean | yes | Whether new lines may name it. |
prices |
array of PriceOut |
yes | Every list price, by currency and then the newest first. |
ssps |
array of SspOut |
yes | Every standalone selling price, by currency and then the newest first. |
created_by |
object | yes | Who created it. |
created_at |
string (date-time) | yes | When (UTC). |
updated_by |
object or null | yes | Who changed it last. |
updated_at |
string (date-time) or null | yes | When (UTC). |
ProductUpdate
| Field | Type | Required | Description |
|---|---|---|---|
name |
string or null | New name. (min length 1; max length 200) | |
description |
string or null | "" clears. (max length 1000) |
|
unit |
string or null | New unit. (min length 1; max length 40) | |
revenue_account |
string or null | New revenue account (code or id) for new lines. | |
supply_type |
"electronic_services" | "goods" | "services" or null |
New supply type. | |
rate_type |
"exempt" | "parking" | "reduced" | "special" | "standard" | "super_reduced" | "zero" or null |
New VAT rate type. | |
active |
boolean or null | false: no new line names it; true: it is sold again. |
SspInput
| Field | Type | Required | Description |
|---|---|---|---|
currency |
string or null | ISO 4217 code, e.g. CHF. Omitted: the entity's functional currency. (pattern ^[A-Z]{3}$) |
|
ssp |
decimal string | yes | The price the product sells for on its own, per unit and period. (min 0) |
price_period |
"month" | "year" or null |
month or year for a recurring product (the price of one unit for that long); omitted for a one-off product (the price of one unit). |
|
ssp_basis |
string | yes | How it was established (IFRS 15.77-79): an observable price, adjusted market assessment, expected cost plus a margin, or the residual approach, and the evidence. (min length 3; max length 2000) |
valid_from |
string (date) or null | The first day it applies; omitted: today. |
SspOut
| Field | Type | Required | Description |
|---|---|---|---|
id |
string (uuid) | yes | SSP id. |
currency |
string | yes | Currency. |
ssp |
string | yes | Standalone selling price of one unit. |
price_period |
"month" | "year" or null |
yes | Per month or year, or per unit. |
ssp_basis |
string | yes | How it was established. |
valid_from |
string (date) | yes | The first day it applies. |
created_by |
object | yes | Who recorded it (a person). |
created_at |
string (date-time) | yes | When (UTC). |