Raccoon API reference

Raccoon ERP API (1.0.0)

Multi-tenant SaaS REST API for Raccoon, the agentic cloud ERP for small and medium businesses: business partners, products, invoices, files and tax. Authentication uses JWT Bearer tokens.

Conventions

  • Resource paths are versioned and plural/resource/v{n}/{objects}.
  • A link to another object is an embedded reference, never a bare id{ "_id", "_class", "_name" }. Only _id is required on input; _name is a read-only display label the API composes on output.
  • DELETE responses are always 204 No Content with an empty body — including soft-deletes, cancellations, and "reset to default" semantics. Clients that need the post-deletion state must re-fetch.

Auth

Register a new tenant and admin user

Request Body schema: application/json
required
admin_email
required
string <email>
admin_password
required
string <password> >= 8 characters
admin_first_name
required
string non-empty
admin_last_name
required
string non-empty
required
object (RegisterAddress)

Address block on the registration payload. Used by the post- registration seed (Phase D) to build the starter Company and sample customers. Tax IDs (VAT, legal/tax registration, IBAN) are NOT accepted on the form and are not seeded either — neither is required to issue an invoice, and a private person or small business may have none of them. The user adds real values in Settings → Companies if and when they have them.

invite_code
required
string [ 1 .. 40 ] characters

A registration invite, issued by a Raccoon administrator. Registration is closed without one. Matched case-insensitively and ignoring whitespace; an unknown, revoked, expired or used-up code is rejected with 422.

company_name
string or null non-empty

The business's legal/trading name. Optional — a sole trader who sells under their own name omits it, and the tenant, its starter Company and the seeded letterhead are all named " ".

Responses

Response Schema: application/json
required
object (ReferenceValue)

Stored value for a field of type "reference"

user_id
required
string
access_token
required
string
refresh_token
required
string
token_type
required
string
expires_in
required
integer
auth_source
required
string
Enum: "login" "api_key"

Indicates how this session was authenticated. login for email + password, api_key when the caller exchanged an API key for the token pair. The flag is propagated through refresh; clients use it to disable actions that must not chain off an API-key session (e.g. creating another API key).

Request samples

Content type
application/json
{
  • "company_name": "Acme GmbH",
  • "admin_email": "admin@acme.de",
  • "admin_password": "pa$$word",
  • "admin_first_name": "Jane",
  • "admin_last_name": "Doe",
  • "address": {
    },
  • "invite_code": "RCCN-7K3P-QX92"
}

Response samples

Content type
application/json
{
  • "tenant": {
    },
  • "user_id": "usr_def456",
  • "access_token": "string",
  • "refresh_token": "string",
  • "token_type": "Bearer",
  • "expires_in": 900,
  • "auth_source": "login"
}

Authenticate and obtain tokens

Request Body schema: application/json
required
email
string <email>
password
string <password>
api_key
string

Raw API key string (rccn_{key_id}_{secret}). When present, email and password are ignored. The returned token pair carries auth_source: api_key.

Responses

Response Schema: application/json
required
object (ReferenceValue)

Stored value for a field of type "reference"

user_id
required
string
access_token
required
string
refresh_token
required
string
token_type
required
string
expires_in
required
integer
auth_source
required
string
Enum: "login" "api_key"

Indicates how this session was authenticated. login for email + password, api_key when the caller exchanged an API key for the token pair. The flag is propagated through refresh; clients use it to disable actions that must not chain off an API-key session (e.g. creating another API key).

Request samples

Content type
application/json
{
  • "email": "user@example.com",
  • "password": "pa$$word",
  • "api_key": "string"
}

Response samples

Content type
application/json
{
  • "tenant": {
    },
  • "user_id": "usr_def456",
  • "access_token": "string",
  • "refresh_token": "string",
  • "token_type": "Bearer",
  • "expires_in": 900,
  • "auth_source": "login"
}

Rotate refresh token and obtain new token pair

Request Body schema: application/json
required
refresh_token
required
string

Responses

Response Schema: application/json
required
object (ReferenceValue)

Stored value for a field of type "reference"

user_id
required
string
access_token
required
string
refresh_token
required
string
token_type
required
string
expires_in
required
integer
auth_source
required
string
Enum: "login" "api_key"

Indicates how this session was authenticated. login for email + password, api_key when the caller exchanged an API key for the token pair. The flag is propagated through refresh; clients use it to disable actions that must not chain off an API-key session (e.g. creating another API key).

Request samples

Content type
application/json
{
  • "refresh_token": "string"
}

Response samples

Content type
application/json
{
  • "tenant": {
    },
  • "user_id": "usr_def456",
  • "access_token": "string",
  • "refresh_token": "string",
  • "token_type": "Bearer",
  • "expires_in": 900,
  • "auth_source": "login"
}

Revoke refresh token (logout)

Authorizations:
BearerAuth

Responses

Response samples

Content type
application/json
{
  • "error": "not_found",
  • "message": "Business partner not found",
  • "details": { }
}

Email a time-limited password-reset link

Always returns 200 regardless of whether email matches a registered, active user — this prevents callers from using the response to enumerate registered accounts.

Request Body schema: application/json
required
email
required
string <email>

Responses

Request samples

Content type
application/json
{
  • "email": "user@example.com"
}

Response samples

Content type
application/json
{
  • "error": "not_found",
  • "message": "Business partner not found",
  • "details": { }
}

Consume a reset-link token and set a new password

Single-use: the token is invalidated on first use whether or not the password update succeeds. On success, every outstanding refresh token for the user is revoked.

Request Body schema: application/json
required
token
required
string

The signed, single-use token from the reset-link email.

new_password
required
string <password> >= 8 characters

Responses

Request samples

Content type
application/json
{
  • "token": "string",
  • "new_password": "pa$$word"
}

Response samples

Content type
application/json
{
  • "error": "not_found",
  • "message": "Business partner not found",
  • "details": { }
}

API Keys

List the calling user's API keys

Returns the API keys owned by the authenticated user (never another user's keys). The bcrypt hash is never returned — only safe metadata.

Authorizations:
BearerAuth

Responses

Response Schema: application/json
required
Array of objects (ApiKeyResponse)
next_token
required
string or null

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_token": "string"
}

Create a new API key for the calling user

The raw key string is returned in the key field exactly once and cannot be recovered. Only a bcrypt hash of the secret portion (salt embedded) is persisted.

Forbidden when the calling session was itself authenticated with an API key (auth_source == "api_key") — preventing privilege chaining. An API key inherits the privileges of the owning user, so the role of the resulting session matches the user's role.

Authorizations:
BearerAuth
Request Body schema: application/json
required
name
required
string [ 1 .. 100 ] characters

Responses

Response Schema: application/json
key_id
required
string
key
required
string

The raw key string in the form rccn_{key_id}_{secret}. Pass this verbatim in POST /auth/login body field api_key to exchange it for a JWT pair (see LoginRequest).

name
required
string
created_at
required
integer
Reference (object) or null

Reference to the user who created the key.

Request samples

Content type
application/json
{
  • "name": "CI deploy key"
}

Response samples

Content type
application/json
{
  • "key_id": "a1b2c3d4e5f6",
  • "key": "rccn_a1b2c3d4e5f6_0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
  • "name": "CI deploy key",
  • "created_at": 1748815200,
  • "created_by": {
    }
}

Get a single API key (metadata only)

Authorizations:
BearerAuth
path Parameters
key_id
required
string

Short identifier embedded in the raw key string.

Responses

Response Schema: application/json
key_id
required
string

Short identifier embedded in the raw key string.

name
required
string
created_at
required
integer

Unix epoch seconds.

last_used_at
integer or null

Unix epoch seconds of the most recent successful exchange of this API key for a JWT pair at POST /auth/auth. Stays unchanged for subsequent requests within the same JWT session.

Reference (object) or null

Reference to the user who created the key. Carries the user display name denormalized at creation time. Null when the creator could not be resolved.

Response samples

Content type
application/json
{
  • "key_id": "a1b2c3d4e5f6",
  • "name": "CI deploy key",
  • "created_at": 1748815200,
  • "last_used_at": 0,
  • "created_by": {
    }
}

Revoke an API key

Authorizations:
BearerAuth
path Parameters
key_id
required
string

Short identifier embedded in the raw key string.

Responses

Response samples

Content type
application/json
{
  • "error": "not_found",
  • "message": "Business partner not found",
  • "details": { }
}

List every API key in the tenant

The owner-facing audit surface. Returns every API key in the calling tenant, whoever owns it, each carrying a user reference to its owner. Owner role required — administrators manage only their own keys, through /resource/v1/api-keys.

Keys are never minted here: an owner cannot create a key on another user's behalf.

Authorizations:
BearerAuth

Responses

Response Schema: application/json
required
Array of objects (TenantApiKeyResponse)
next_token
required
string or null

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_token": "string"
}

Revoke any API key in the tenant

Revokes the key regardless of which user owns it — the route an owner uses to cut off a departed colleague's credential. The revocation is published as an api_key deletion event, so the audit trail records the acting owner.

Authorizations:
BearerAuth
path Parameters
key_id
required
string

Short identifier embedded in the raw key string.

Responses

Response samples

Content type
application/json
{
  • "error": "not_found",
  • "message": "Business partner not found",
  • "details": { }
}

Business Partners

List business partners (paginated)

Authorizations:
BearerAuth
query Parameters
limit
integer [ 1 .. 200 ]
Default: 50
next_token
string

Base64-encoded pagination cursor from previous response

partner_type
string
Enum: "person" "company"
status
string
Enum: "active" "inactive"
q
string non-empty

Full-text search term. When provided, the endpoint delegates to the shared search index (matches across name/title/email/identifier fields, tenant-scoped) and returns the matching entities in the same response shape as the unfiltered list. Pagination (limit, next_token) is not honoured in this mode; results are capped at the search index size limit (20 items in MVP).

Responses

Response Schema: application/json
required
Array of any (BusinessPartnerResponse)
next_token
required
string or null

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_token": "string"
}

Create a business partner

Authorizations:
BearerAuth
Request Body schema: application/json
required
partner_type
required
string
first_name
required
string non-empty
last_name
required
string non-empty
phone
string
mobile
string
email
string <email>
Array of objects (EmailAddress)

All e-mail addresses linked to this partner. Send {"email": "..."} plus any role flags; email_id is assigned server-side.

website
string <uri>
notes
string
customer_number
string

The tenant's own reference for this partner (DIN 5008 Kundennummer), copied onto the documents it is billed on.

status
string
Default: "active"
Enum: "active" "inactive"
language
string [ 2 .. 5 ] characters
Default: "en"
discount_percent
number [ 0 .. 100 ]
Default: 0

Per-partner discount applied to product list prices on Order-to-Cash documents. Defaults to 0 (no discount). 0 % is semantically the "no override" state.

payment_due_days
integer or null >= 0

Calendar days from order date to payment due date. null (or absent) means inherit default_payment_due_days from the partner type.

Reference (object) or null

Reference to the BP's preferred invoice (billing) address. Must point at an address belonging to this BP that has is_invoice_address: true. The server validates both invariants on write. null means no preferred address is set; the invoice form falls back to the first address with the matching role flag.

Reference (object) or null

Like preferred_invoice_address but for the shipping slot.

Reference (object) or null

Like preferred_invoice_address but for the service slot.

Reference (object) or null

Optional tenant-defined sales channel this partner belongs to. null means unassigned.

vat_partner_type
string
Default: "b2c"
Enum: "b2c" "b2b" "public_authority"

Buyer kind, and the first input to VAT regime resolution. b2b with a VIES-validated VAT-ID unlocks intra-EU reverse-charge. Distinct from partner_type (the person/company discriminator). The VAT-ID itself is a vat_eu identifier (see the identifiers sub-resource).

type_id
string [ 1 .. 100 ] characters

The business-partner-class tenant type this partner is created under. Every partner has one. Omitted, the partner is created under the tenant's default business-partner type; that is a 422 when the tenant has several and marks none of them default.

schema_version
string or null

Type version. Omitted = latest.

object

Custom field values keyed by section field IDs.

salutation
string
salutation_code
string
Enum: "frau" "herr" "divers" "mme" "m" "ms" "mr" "mx"

Machine-readable salutation feeding a future formal-greeting helper (deferred Phase 3 invoice-text epic).

date_of_birth
string <date>
gender
string
Enum: "male" "female" "other" "unknown"

Responses

Response Schema: application/json
_id
required
string
_class
required
string
_name
required
string (EntityName)

Read-only, backend-composed display label of this entity (a person's full name, a product's name, a project's title, …). A backend-meta field (sibling of _id / _class) — never accepted on input.

partner_type
required
string
first_name
required
string
last_name
required
string
type_id
required
string

The tenant type this partner was created under.

status
required
string
Enum: "active" "inactive"
language
required
string
required
object (ReferenceValue)

Stored value for a field of type "reference"

created_at
required
integer

Unix epoch seconds

updated_at
required
integer

Unix epoch seconds

required
object

Type-specific custom-field data; {} when untyped

required
Array of objects (EmailAddress)

All e-mail addresses linked to this partner (including login users)

vat_partner_type
required
string
Enum: "b2c" "b2b" "public_authority"

Buyer kind for VAT regime resolution. The VAT-ID itself is a vat_eu identifier (see the identifiers sub-resource), not a field here.

required
ReferenceValue (object) or null
required
Array of objects (IdentifierResponse)

The numbers this record carries (ISBN, GTIN, VAT ID, ...). Stored on the record itself; the identifiers sub-resource addresses one entry.

required
Array of objects (PrintedIdentifier)

Read-only. The numbers the record's type marked primary, in the order it declared them — what a picker option, a search row or a detail header shows without resolving the type itself.

required
object

self is the partner's own resource URL, texts the collection of the texts it carries. Further rels may be present; a client follows the ones it knows.

Request samples

Content type
application/json
Example
{
  • "phone": "+49 30 123456",
  • "mobile": "+49 170 1234567",
  • "email": "user@example.com",
  • "email_addresses": [
    ],
  • "website": "http://example.com",
  • "notes": "string",
  • "customer_number": "string",
  • "status": "active",
  • "language": "de",
  • "discount_percent": 10,
  • "payment_due_days": 30,
  • "preferred_invoice_address": {
    },
  • "preferred_shipping_address": {
    },
  • "preferred_service_address": {
    },
  • "channel": {
    },
  • "vat_partner_type": "b2c",
  • "type_id": "customer",
  • "schema_version": "string",
  • "data": { },
  • "partner_type": "person",
  • "salutation": "Ms.",
  • "salutation_code": "ms",
  • "first_name": "Jane",
  • "last_name": "Doe",
  • "date_of_birth": "1985-03-15",
  • "gender": "male"
}

Response samples

Content type
application/json
Example
{
  • "_links": {
    },
  • "primary_identifiers": [
    ],
  • "identifiers": [
    ],
  • "_id": "bp_a1b2c3",
  • "_class": "business_partner",
  • "_name": "Acme Corp",
  • "partner_type": "person",
  • "first_name": "Jane",
  • "last_name": "Doe",
  • "type_id": "string",
  • "status": "active",
  • "language": "string",
  • "tenant": {
    },
  • "created_at": 1746144000,
  • "updated_at": 1746144000,
  • "data": { },
  • "email_addresses": [
    ],
  • "vat_partner_type": "b2c",
  • "created_by": {
    }
}

Get a business partner (includes embedded addresses)

Authorizations:
BearerAuth
path Parameters
business_partner_id
required
string
Example: bp_a1b2c3d4e5f6

Responses

Response Schema: application/json
_id
required
string
_class
required
string
_name
required
string (EntityName)

Read-only, backend-composed display label of this entity (a person's full name, a product's name, a project's title, …). A backend-meta field (sibling of _id / _class) — never accepted on input.

partner_type
required
string
first_name
required
string
last_name
required
string
type_id
required
string

The tenant type this partner was created under.

status
required
string
Enum: "active" "inactive"
language
required
string
required
object (ReferenceValue)

Stored value for a field of type "reference"

created_at
required
integer

Unix epoch seconds

updated_at
required
integer

Unix epoch seconds

required
object

Type-specific custom-field data; {} when untyped

required
Array of objects (EmailAddress)

All e-mail addresses linked to this partner (including login users)

vat_partner_type
required
string
Enum: "b2c" "b2b" "public_authority"

Buyer kind for VAT regime resolution. The VAT-ID itself is a vat_eu identifier (see the identifiers sub-resource), not a field here.

required
ReferenceValue (object) or null
required
Array of objects (IdentifierResponse)

The numbers this record carries (ISBN, GTIN, VAT ID, ...). Stored on the record itself; the identifiers sub-resource addresses one entry.

required
Array of objects (PrintedIdentifier)

Read-only. The numbers the record's type marked primary, in the order it declared them — what a picker option, a search row or a detail header shows without resolving the type itself.

required
object

self is the partner's own resource URL, texts the collection of the texts it carries. Further rels may be present; a client follows the ones it knows.

Response samples

Content type
application/json
Example
{
  • "_links": {
    },
  • "primary_identifiers": [
    ],
  • "identifiers": [
    ],
  • "_id": "bp_a1b2c3",
  • "_class": "business_partner",
  • "_name": "Acme Corp",
  • "partner_type": "person",
  • "first_name": "Jane",
  • "last_name": "Doe",
  • "type_id": "string",
  • "status": "active",
  • "language": "string",
  • "tenant": {
    },
  • "created_at": 1746144000,
  • "updated_at": 1746144000,
  • "data": { },
  • "email_addresses": [
    ],
  • "vat_partner_type": "b2c",
  • "created_by": {
    }
}

Replace a business partner (full update)

Authorizations:
BearerAuth
path Parameters
business_partner_id
required
string
Example: bp_a1b2c3d4e5f6
Request Body schema: application/json
required
phone
string
mobile
string
email
string <email>
Array of objects (EmailAddress)

Replaces the partner's whole e-mail list. email_id is assigned server-side for new entries and preserved for existing ones.

website
string <uri>
notes
string
customer_number
string
status
string
Enum: "active" "inactive"
language
string
salutation
string
first_name
string
last_name
string
date_of_birth
string <date>
gender
string
Enum: "male" "female" "other" "unknown"
company_name
string
legal_form
string
tax_id
string
vat_partner_type
string
Enum: "b2c" "b2b" "public_authority"
commercial_register_number
string
type_id
string or null
schema_version
string or null
object
discount_percent
number [ 0 .. 100 ]
payment_due_days
integer or null >= 0
Reference (object) or null
Reference (object) or null
Reference (object) or null
Reference (object) or null

Tenant-defined sales channel, or null to unassign.

Responses

Response Schema: application/json
_id
required
string
_class
required
string
_name
required
string (EntityName)

Read-only, backend-composed display label of this entity (a person's full name, a product's name, a project's title, …). A backend-meta field (sibling of _id / _class) — never accepted on input.

partner_type
required
string
first_name
required
string
last_name
required
string
type_id
required
string

The tenant type this partner was created under.

status
required
string
Enum: "active" "inactive"
language
required
string
required
object (ReferenceValue)

Stored value for a field of type "reference"

created_at
required
integer

Unix epoch seconds

updated_at
required
integer

Unix epoch seconds

required
object

Type-specific custom-field data; {} when untyped

required
Array of objects (EmailAddress)

All e-mail addresses linked to this partner (including login users)

vat_partner_type
required
string
Enum: "b2c" "b2b" "public_authority"

Buyer kind for VAT regime resolution. The VAT-ID itself is a vat_eu identifier (see the identifiers sub-resource), not a field here.

required
ReferenceValue (object) or null
required
Array of objects (IdentifierResponse)

The numbers this record carries (ISBN, GTIN, VAT ID, ...). Stored on the record itself; the identifiers sub-resource addresses one entry.

required
Array of objects (PrintedIdentifier)

Read-only. The numbers the record's type marked primary, in the order it declared them — what a picker option, a search row or a detail header shows without resolving the type itself.

required
object

self is the partner's own resource URL, texts the collection of the texts it carries. Further rels may be present; a client follows the ones it knows.

Request samples

Content type
application/json
{
  • "phone": "string",
  • "mobile": "string",
  • "email": "user@example.com",
  • "email_addresses": [
    ],
  • "website": "http://example.com",
  • "notes": "string",
  • "customer_number": "string",
  • "status": "active",
  • "language": "string",
  • "salutation": "string",
  • "first_name": "string",
  • "last_name": "string",
  • "date_of_birth": "2019-08-24",
  • "gender": "male",
  • "company_name": "string",
  • "legal_form": "string",
  • "tax_id": "string",
  • "vat_partner_type": "b2c",
  • "commercial_register_number": "string",
  • "type_id": "string",
  • "schema_version": "string",
  • "data": { },
  • "discount_percent": 100,
  • "payment_due_days": 0,
  • "preferred_invoice_address": {
    },
  • "preferred_shipping_address": {
    },
  • "preferred_service_address": {
    },
  • "channel": {
    }
}

Response samples

Content type
application/json
Example
{
  • "_links": {
    },
  • "primary_identifiers": [
    ],
  • "identifiers": [
    ],
  • "_id": "bp_a1b2c3",
  • "_class": "business_partner",
  • "_name": "Acme Corp",
  • "partner_type": "person",
  • "first_name": "Jane",
  • "last_name": "Doe",
  • "type_id": "string",
  • "status": "active",
  • "language": "string",
  • "tenant": {
    },
  • "created_at": 1746144000,
  • "updated_at": 1746144000,
  • "data": { },
  • "email_addresses": [
    ],
  • "vat_partner_type": "b2c",
  • "created_by": {
    }
}

Partially update a business partner

Authorizations:
BearerAuth
path Parameters
business_partner_id
required
string
Example: bp_a1b2c3d4e5f6
Request Body schema: application/json
required
phone
string
mobile
string
email
string <email>
Array of objects (EmailAddress)

Replaces the partner's whole e-mail list. email_id is assigned server-side for new entries and preserved for existing ones.

website
string <uri>
notes
string
customer_number
string
status
string
Enum: "active" "inactive"
language
string
salutation
string
first_name
string
last_name
string
date_of_birth
string <date>
gender
string
Enum: "male" "female" "other" "unknown"
company_name
string
legal_form
string
tax_id
string
vat_partner_type
string
Enum: "b2c" "b2b" "public_authority"
commercial_register_number
string
type_id
string or null
schema_version
string or null
object
discount_percent
number [ 0 .. 100 ]
payment_due_days
integer or null >= 0
Reference (object) or null
Reference (object) or null
Reference (object) or null
Reference (object) or null

Tenant-defined sales channel, or null to unassign.

Responses

Response Schema: application/json
_id
required
string
_class
required
string
_name
required
string (EntityName)

Read-only, backend-composed display label of this entity (a person's full name, a product's name, a project's title, …). A backend-meta field (sibling of _id / _class) — never accepted on input.

partner_type
required
string
first_name
required
string
last_name
required
string
type_id
required
string

The tenant type this partner was created under.

status
required
string
Enum: "active" "inactive"
language
required
string
required
object (ReferenceValue)

Stored value for a field of type "reference"

created_at
required
integer

Unix epoch seconds

updated_at
required
integer

Unix epoch seconds

required
object

Type-specific custom-field data; {} when untyped

required
Array of objects (EmailAddress)

All e-mail addresses linked to this partner (including login users)

vat_partner_type
required
string
Enum: "b2c" "b2b" "public_authority"

Buyer kind for VAT regime resolution. The VAT-ID itself is a vat_eu identifier (see the identifiers sub-resource), not a field here.

required
ReferenceValue (object) or null
required
Array of objects (IdentifierResponse)

The numbers this record carries (ISBN, GTIN, VAT ID, ...). Stored on the record itself; the identifiers sub-resource addresses one entry.

required
Array of objects (PrintedIdentifier)

Read-only. The numbers the record's type marked primary, in the order it declared them — what a picker option, a search row or a detail header shows without resolving the type itself.

required
object

self is the partner's own resource URL, texts the collection of the texts it carries. Further rels may be present; a client follows the ones it knows.

Request samples

Content type
application/json
{
  • "phone": "string",
  • "mobile": "string",
  • "email": "user@example.com",
  • "email_addresses": [
    ],
  • "website": "http://example.com",
  • "notes": "string",
  • "customer_number": "string",
  • "status": "active",
  • "language": "string",
  • "salutation": "string",
  • "first_name": "string",
  • "last_name": "string",
  • "date_of_birth": "2019-08-24",
  • "gender": "male",
  • "company_name": "string",
  • "legal_form": "string",
  • "tax_id": "string",
  • "vat_partner_type": "b2c",
  • "commercial_register_number": "string",
  • "type_id": "string",
  • "schema_version": "string",
  • "data": { },
  • "discount_percent": 100,
  • "payment_due_days": 0,
  • "preferred_invoice_address": {
    },
  • "preferred_shipping_address": {
    },
  • "preferred_service_address": {
    },
  • "channel": {
    }
}

Response samples

Content type
application/json
Example
{
  • "_links": {
    },
  • "primary_identifiers": [
    ],
  • "identifiers": [
    ],
  • "_id": "bp_a1b2c3",
  • "_class": "business_partner",
  • "_name": "Acme Corp",
  • "partner_type": "person",
  • "first_name": "Jane",
  • "last_name": "Doe",
  • "type_id": "string",
  • "status": "active",
  • "language": "string",
  • "tenant": {
    },
  • "created_at": 1746144000,
  • "updated_at": 1746144000,
  • "data": { },
  • "email_addresses": [
    ],
  • "vat_partner_type": "b2c",
  • "created_by": {
    }
}

Soft-delete a business partner (sets status=inactive)

Authorizations:
BearerAuth
path Parameters
business_partner_id
required
string
Example: bp_a1b2c3d4e5f6

Responses

Response samples

Content type
application/json
{
  • "error": "not_found",
  • "message": "Business partner not found",
  • "details": { }
}

Grant login access to a person-type business partner (owner only)

Creates a user account linked to the business partner and sets its business_partner_role. Restricted to the tenant owner. Only person-type partners without existing login access are eligible.

Authorizations:
BearerAuth
path Parameters
business_partner_id
required
string
Example: bp_a1b2c3d4e5f6
Request Body schema: application/json
required
email
required
string <email>
password
required
string <password> >= 8 characters
role
string
Default: "administrator"
Value: "administrator"

Responses

Response Schema: application/json
user_id
required
string
business_partner_id
required
string
role
required
string
Value: "administrator"
email
required
string <email>
business_partner_role
required
string
Value: "administrator"

Request samples

Content type
application/json
{
  • "email": "user@example.com",
  • "password": "pa$$word",
  • "role": "administrator"
}

Response samples

Content type
application/json
{
  • "user_id": "string",
  • "business_partner_id": "string",
  • "role": "administrator",
  • "email": "user@example.com",
  • "business_partner_role": "administrator"
}

Revoke login access from a business partner (owner only)

Deactivates the linked user account and clears the partner's business_partner_role. Restricted to the tenant owner. The owner's own login cannot be revoked.

Authorizations:
BearerAuth
path Parameters
business_partner_id
required
string
Example: bp_a1b2c3d4e5f6

Responses

Response samples

Content type
application/json
{
  • "error": "not_found",
  • "message": "Business partner not found",
  • "details": { }
}

Products this partner was billed before

The products most recently billed to this partner, newest first and capped. Read off the partner's own invoice line items, so it reflects what was actually billed rather than a separately maintained list. Empty for a partner with no invoice history.

Authorizations:
BearerAuth
path Parameters
business_partner_id
required
string
Example: bp_a1b2c3d4e5f6

Responses

Response Schema: application/json
required
Array of objects (ProductResponse)
next_token
required
string or null

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_token": "string"
}

Addresses

List addresses for a business partner

Authorizations:
BearerAuth
path Parameters
business_partner_id
required
string
Example: bp_a1b2c3d4e5f6

Responses

Response Schema: application/json
required
Array of objects (AddressResponse)
next_token
required
string or null

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_token": "string"
}

Add an address to a business partner

Authorizations:
BearerAuth
path Parameters
business_partner_id
required
string
Example: bp_a1b2c3d4e5f6
Request Body schema: application/json
required
street_line_1
required
string non-empty
city
required
string non-empty
country_code
required
string = 2 characters

ISO 3166-1 alpha-2 country code

label
string

Free-text structural label (e.g. "HQ", "Munich warehouse"). Purely descriptive — has no behaviour.

is_invoice_address
boolean
Default: false

When true, this address is eligible to fill the invoice slot on invoices for this business partner. Auto-defaults to true for the very first address created on a BP (the freelancer-friendly default).

is_shipping_address
boolean
Default: false

When true, this address is eligible to fill the shipping slot on invoices for this business partner.

is_service_address
boolean
Default: false

When true, this address is eligible to fill the service slot on invoices for this business partner.

street_line_2
string
state_province
string
postal_code
string

Responses

Response Schema: application/json
_id
required
string
_class
required
string
_name
required
string (EntityName)

Read-only, backend-composed display label of this entity (a person's full name, a product's name, a project's title, …). A backend-meta field (sibling of _id / _class) — never accepted on input.

required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

is_invoice_address
required
boolean
is_shipping_address
required
boolean
is_service_address
required
boolean
street_line_1
required
string
city
required
string
country_code
required
string = 2 characters
required
object (ReferenceValue)

Stored value for a field of type "reference"

created_at
required
integer

Unix epoch seconds

updated_at
required
integer

Unix epoch seconds

required
ReferenceValue (object) or null

Request samples

Content type
application/json
{
  • "label": "HQ",
  • "is_invoice_address": false,
  • "is_shipping_address": false,
  • "is_service_address": false,
  • "street_line_1": "Musterstraße 1",
  • "street_line_2": "Apt 2B",
  • "city": "Berlin",
  • "state_province": "Berlin",
  • "postal_code": "10115",
  • "country_code": "DE"
}

Response samples

Content type
application/json
{
  • "_id": "addr_a1b2c3",
  • "_class": "address",
  • "_name": "Acme Corp",
  • "business_partner": {
    },
  • "is_invoice_address": true,
  • "is_shipping_address": true,
  • "is_service_address": true,
  • "street_line_1": "string",
  • "city": "string",
  • "country_code": "st",
  • "tenant": {
    },
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    }
}

Get a specific address

Authorizations:
BearerAuth
path Parameters
business_partner_id
required
string
Example: bp_a1b2c3d4e5f6
address_id
required
string
Example: addr_a1b2c3d4e5f6

Responses

Response Schema: application/json
_id
required
string
_class
required
string
_name
required
string (EntityName)

Read-only, backend-composed display label of this entity (a person's full name, a product's name, a project's title, …). A backend-meta field (sibling of _id / _class) — never accepted on input.

required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

is_invoice_address
required
boolean
is_shipping_address
required
boolean
is_service_address
required
boolean
street_line_1
required
string
city
required
string
country_code
required
string = 2 characters
required
object (ReferenceValue)

Stored value for a field of type "reference"

created_at
required
integer

Unix epoch seconds

updated_at
required
integer

Unix epoch seconds

required
ReferenceValue (object) or null

Response samples

Content type
application/json
{
  • "_id": "addr_a1b2c3",
  • "_class": "address",
  • "_name": "Acme Corp",
  • "business_partner": {
    },
  • "is_invoice_address": true,
  • "is_shipping_address": true,
  • "is_service_address": true,
  • "street_line_1": "string",
  • "city": "string",
  • "country_code": "st",
  • "tenant": {
    },
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    }
}

Replace an address (full update)

Authorizations:
BearerAuth
path Parameters
business_partner_id
required
string
Example: bp_a1b2c3d4e5f6
address_id
required
string
Example: addr_a1b2c3d4e5f6
Request Body schema: application/json
required
label
string
is_invoice_address
boolean
is_shipping_address
boolean
is_service_address
boolean
street_line_1
string
street_line_2
string
city
string
state_province
string
postal_code
string
country_code
string = 2 characters

Responses

Response Schema: application/json
_id
required
string
_class
required
string
_name
required
string (EntityName)

Read-only, backend-composed display label of this entity (a person's full name, a product's name, a project's title, …). A backend-meta field (sibling of _id / _class) — never accepted on input.

required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

is_invoice_address
required
boolean
is_shipping_address
required
boolean
is_service_address
required
boolean
street_line_1
required
string
city
required
string
country_code
required
string = 2 characters
required
object (ReferenceValue)

Stored value for a field of type "reference"

created_at
required
integer

Unix epoch seconds

updated_at
required
integer

Unix epoch seconds

required
ReferenceValue (object) or null

Request samples

Content type
application/json
{
  • "label": "string",
  • "is_invoice_address": true,
  • "is_shipping_address": true,
  • "is_service_address": true,
  • "street_line_1": "string",
  • "street_line_2": "string",
  • "city": "string",
  • "state_province": "string",
  • "postal_code": "string",
  • "country_code": "st"
}

Response samples

Content type
application/json
{
  • "_id": "addr_a1b2c3",
  • "_class": "address",
  • "_name": "Acme Corp",
  • "business_partner": {
    },
  • "is_invoice_address": true,
  • "is_shipping_address": true,
  • "is_service_address": true,
  • "street_line_1": "string",
  • "city": "string",
  • "country_code": "st",
  • "tenant": {
    },
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    }
}

Partially update an address

Authorizations:
BearerAuth
path Parameters
business_partner_id
required
string
Example: bp_a1b2c3d4e5f6
address_id
required
string
Example: addr_a1b2c3d4e5f6
Request Body schema: application/json
required
label
string
is_invoice_address
boolean
is_shipping_address
boolean
is_service_address
boolean
street_line_1
string
street_line_2
string
city
string
state_province
string
postal_code
string
country_code
string = 2 characters

Responses

Response Schema: application/json
_id
required
string
_class
required
string
_name
required
string (EntityName)

Read-only, backend-composed display label of this entity (a person's full name, a product's name, a project's title, …). A backend-meta field (sibling of _id / _class) — never accepted on input.

required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

is_invoice_address
required
boolean
is_shipping_address
required
boolean
is_service_address
required
boolean
street_line_1
required
string
city
required
string
country_code
required
string = 2 characters
required
object (ReferenceValue)

Stored value for a field of type "reference"

created_at
required
integer

Unix epoch seconds

updated_at
required
integer

Unix epoch seconds

required
ReferenceValue (object) or null

Request samples

Content type
application/json
{
  • "label": "string",
  • "is_invoice_address": true,
  • "is_shipping_address": true,
  • "is_service_address": true,
  • "street_line_1": "string",
  • "street_line_2": "string",
  • "city": "string",
  • "state_province": "string",
  • "postal_code": "string",
  • "country_code": "st"
}

Response samples

Content type
application/json
{
  • "_id": "addr_a1b2c3",
  • "_class": "address",
  • "_name": "Acme Corp",
  • "business_partner": {
    },
  • "is_invoice_address": true,
  • "is_shipping_address": true,
  • "is_service_address": true,
  • "street_line_1": "string",
  • "city": "string",
  • "country_code": "st",
  • "tenant": {
    },
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    }
}

Delete an address

Authorizations:
BearerAuth
path Parameters
business_partner_id
required
string
Example: bp_a1b2c3d4e5f6
address_id
required
string
Example: addr_a1b2c3d4e5f6

Responses

Response samples

Content type
application/json
{
  • "error": "not_found",
  • "message": "Business partner not found",
  • "details": { }
}

Products

List products (optionally filtered by project)

Authorizations:
BearerAuth
query Parameters
project_id
string
Example: project_id=prj_a1b2c3d4e5f6

Filter products by project ID

limit
integer [ 1 .. 200 ]
Default: 50
next_token
string

Base64-encoded pagination cursor from previous response

q
string non-empty

Full-text search term. When provided, the endpoint delegates to the shared search index (matches across name/title/email/identifier fields, tenant-scoped) and returns the matching entities in the same response shape as the unfiltered list. Pagination (limit, next_token) is not honoured in this mode; results are capped at the search index size limit (20 items in MVP).

Responses

Response Schema: application/json
required
Array of objects (ProductResponse)
next_token
required
string or null

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_token": "string"
}

Create a product

Authorizations:
BearerAuth
Request Body schema: application/json
required
name
required
string [ 1 .. 200 ] characters
Reference (object) or null

Optional project to assign this product to

Reference (object) or null

Optional tenant-defined sales channel to assign this product to

type_id
string [ 1 .. 100 ] characters

The product-class tenant type this product is created under. Every product has one. Omitted, the product is created under the tenant's default product type; that is a 422 when the tenant has several and marks none of them default.

schema_version
string [ 1 .. 50 ] characters
object
stocking_unit_of_measure
string or null <= 20 characters

Overrides the product type's inventory_config.stocking_unit_of_measure for this product. null (the default) inherits the type's unit.

vat_category
string or null
Enum: "H" "S" "R" "R2" "T" "Z" "EXEMPT" null

ONIX List 62 VAT category. Optional on input — defaults to the product type's recommended_vat_category, else the tenant default (vat.default_vat_category). The VAT rate is resolved per invoice.

Array of objects (ProductPriceInput)

Inline price entries. Single-price mode (pricing_config with no flags enabled on the product type) accepts at most one entry with no scoping fields. Multi-dimension mode accepts N entries with the corresponding scoping fields populated. The service layer rejects rows whose scoping fields aren't gated by the relevant pricing_config flag.

Responses

Response Schema: application/json
_id
required
string
_class
required
string
Value: "product"
_name
required
string (EntityName)

Read-only, backend-composed display label of this entity (a person's full name, a product's name, a project's title, …). A backend-meta field (sibling of _id / _class) — never accepted on input.

required
object

self is the product's own resource URL, texts the collection of the texts it carries. Further rels may be present; a client follows the ones it knows.

type_id
required
string
schema_version
required
string
name
required
string
required
object
stocking_unit_of_measure
required
string or null <= 20 characters

Overrides the product type's inventory_config.stocking_unit_of_measure for this product. null (the default) inherits the type's unit.

vat_category
required
string
Enum: "H" "S" "R" "R2" "T" "Z" "EXEMPT"

ONIX List 62 VAT category. The per-invoice rate is resolved from it.

required
object (ReferenceValue)

Stored value for a field of type "reference"

created_at
required
integer
updated_at
required
integer
required
ReferenceValue (object) or null
required
Array of objects (IdentifierResponse)

The numbers this record carries (ISBN, GTIN, VAT ID, ...). Stored on the record itself; the identifiers sub-resource addresses one entry.

required
Array of objects (PrintedIdentifier)

Read-only. The numbers the record's type marked primary, in the order it declared them — what a picker option, a search row or a detail header shows without resolving the type itself.

required
Array of objects (ProductSaveWarning)
Reference (object) or null

Project this product belongs to, null if unassigned

Reference (object) or null

Sales channel this product belongs to, null if unassigned

Array of objects (ProductPrice)

Inline price entries. Empty array when no price has been assigned. Single-price mode = 0 or 1 entries, no scoping fields populated. Multi-dim mode (any pricing_config flag on) = N entries with the gated scoping fields.

Request samples

Content type
application/json
{
  • "project": {
    },
  • "channel": {
    },
  • "type_id": "toy",
  • "schema_version": "1.0.0",
  • "name": "My Product",
  • "data": { },
  • "stocking_unit_of_measure": "string",
  • "vat_category": "H",
  • "prices": [
    ]
}

Response samples

Content type
application/json
{
  • "primary_identifiers": [
    ],
  • "identifiers": [
    ],
  • "_id": "prd_a1b2c3d4e5f6",
  • "_class": "product",
  • "_name": "Acme Corp",
  • "_links": {
    },
  • "type_id": "toy",
  • "schema_version": "1.0.0",
  • "name": "My Product",
  • "data": { },
  • "stocking_unit_of_measure": "string",
  • "vat_category": "H",
  • "tenant": {
    },
  • "created_at": 1672531200,
  • "updated_at": 1672531200,
  • "project": {
    },
  • "channel": {
    },
  • "prices": [
    ],
  • "created_by": {
    },
  • "warnings": [
    ]
}

Get a product

Authorizations:
BearerAuth
path Parameters
product_id
required
string
Example: prd_a1b2c3d4e5f6

Responses

Response Schema: application/json
_id
required
string
_class
required
string
Value: "product"
_name
required
string (EntityName)

Read-only, backend-composed display label of this entity (a person's full name, a product's name, a project's title, …). A backend-meta field (sibling of _id / _class) — never accepted on input.

required
object

self is the product's own resource URL, texts the collection of the texts it carries. Further rels may be present; a client follows the ones it knows.

type_id
required
string
schema_version
required
string
name
required
string
required
object
stocking_unit_of_measure
required
string or null <= 20 characters

Overrides the product type's inventory_config.stocking_unit_of_measure for this product. null (the default) inherits the type's unit.

vat_category
required
string
Enum: "H" "S" "R" "R2" "T" "Z" "EXEMPT"

ONIX List 62 VAT category. The per-invoice rate is resolved from it.

required
object (ReferenceValue)

Stored value for a field of type "reference"

created_at
required
integer
updated_at
required
integer
required
ReferenceValue (object) or null
required
Array of objects (IdentifierResponse)

The numbers this record carries (ISBN, GTIN, VAT ID, ...). Stored on the record itself; the identifiers sub-resource addresses one entry.

required
Array of objects (PrintedIdentifier)

Read-only. The numbers the record's type marked primary, in the order it declared them — what a picker option, a search row or a detail header shows without resolving the type itself.

Reference (object) or null

Project this product belongs to, null if unassigned

Reference (object) or null

Sales channel this product belongs to, null if unassigned

Array of objects (ProductPrice)

Inline price entries. Empty array when no price has been assigned. Single-price mode = 0 or 1 entries, no scoping fields populated. Multi-dim mode (any pricing_config flag on) = N entries with the gated scoping fields.

Response samples

Content type
application/json
{
  • "primary_identifiers": [
    ],
  • "identifiers": [
    ],
  • "_id": "prd_a1b2c3d4e5f6",
  • "_class": "product",
  • "_name": "Acme Corp",
  • "_links": {
    },
  • "type_id": "toy",
  • "schema_version": "1.0.0",
  • "name": "My Product",
  • "data": { },
  • "stocking_unit_of_measure": "string",
  • "vat_category": "H",
  • "tenant": {
    },
  • "created_at": 1672531200,
  • "updated_at": 1672531200,
  • "project": {
    },
  • "channel": {
    },
  • "prices": [
    ],
  • "created_by": {
    }
}

Partially update a product

Authorizations:
BearerAuth
path Parameters
product_id
required
string
Example: prd_a1b2c3d4e5f6
Request Body schema: application/json
required
name
string [ 1 .. 200 ] characters
object
Reference (object) or null

Sales channel to assign, or null to unassign

stocking_unit_of_measure
string or null <= 20 characters

Stocking-unit override for this product. Omit to leave it unchanged; send null to fall back to the product type's unit.

vat_category
string or null
Enum: "H" "S" "R" "R2" "T" "Z" "EXEMPT" null

ONIX List 62 VAT category. Omit or null to leave unchanged.

type_id
string or null
schema_version
string or null
Array of objects or null (ProductPriceInput)

Omit (or null) to leave the existing prices unchanged. Empty array to clear. Any non-empty list replaces the prices array.

Responses

Response Schema: application/json
_id
required
string
_class
required
string
Value: "product"
_name
required
string (EntityName)

Read-only, backend-composed display label of this entity (a person's full name, a product's name, a project's title, …). A backend-meta field (sibling of _id / _class) — never accepted on input.

required
object

self is the product's own resource URL, texts the collection of the texts it carries. Further rels may be present; a client follows the ones it knows.

type_id
required
string
schema_version
required
string
name
required
string
required
object
stocking_unit_of_measure
required
string or null <= 20 characters

Overrides the product type's inventory_config.stocking_unit_of_measure for this product. null (the default) inherits the type's unit.

vat_category
required
string
Enum: "H" "S" "R" "R2" "T" "Z" "EXEMPT"

ONIX List 62 VAT category. The per-invoice rate is resolved from it.

required
object (ReferenceValue)

Stored value for a field of type "reference"

created_at
required
integer
updated_at
required
integer
required
ReferenceValue (object) or null
required
Array of objects (IdentifierResponse)

The numbers this record carries (ISBN, GTIN, VAT ID, ...). Stored on the record itself; the identifiers sub-resource addresses one entry.

required
Array of objects (PrintedIdentifier)

Read-only. The numbers the record's type marked primary, in the order it declared them — what a picker option, a search row or a detail header shows without resolving the type itself.

required
Array of objects (ProductSaveWarning)
Reference (object) or null

Project this product belongs to, null if unassigned

Reference (object) or null

Sales channel this product belongs to, null if unassigned

Array of objects (ProductPrice)

Inline price entries. Empty array when no price has been assigned. Single-price mode = 0 or 1 entries, no scoping fields populated. Multi-dim mode (any pricing_config flag on) = N entries with the gated scoping fields.

Request samples

Content type
application/json
{
  • "name": "string",
  • "data": { },
  • "channel": {
    },
  • "stocking_unit_of_measure": "string",
  • "vat_category": "H",
  • "type_id": "string",
  • "schema_version": "string",
  • "prices": [
    ]
}

Response samples

Content type
application/json
{
  • "primary_identifiers": [
    ],
  • "identifiers": [
    ],
  • "_id": "prd_a1b2c3d4e5f6",
  • "_class": "product",
  • "_name": "Acme Corp",
  • "_links": {
    },
  • "type_id": "toy",
  • "schema_version": "1.0.0",
  • "name": "My Product",
  • "data": { },
  • "stocking_unit_of_measure": "string",
  • "vat_category": "H",
  • "tenant": {
    },
  • "created_at": 1672531200,
  • "updated_at": 1672531200,
  • "project": {
    },
  • "channel": {
    },
  • "prices": [
    ],
  • "created_by": {
    },
  • "warnings": [
    ]
}

Delete a product

Authorizations:
BearerAuth
path Parameters
product_id
required
string
Example: prd_a1b2c3d4e5f6

Responses

Response samples

Content type
application/json
{
  • "error": "not_found",
  • "message": "Business partner not found",
  • "details": { }
}

Assign or unassign a product's project

Authorizations:
BearerAuth
path Parameters
product_id
required
string
Example: prd_a1b2c3d4e5f6
Request Body schema: application/json
required
Reference (object) or null

Project reference to assign, or null to unassign

Responses

Response Schema: application/json
_id
required
string
_class
required
string
Value: "product"
_name
required
string (EntityName)

Read-only, backend-composed display label of this entity (a person's full name, a product's name, a project's title, …). A backend-meta field (sibling of _id / _class) — never accepted on input.

required
object

self is the product's own resource URL, texts the collection of the texts it carries. Further rels may be present; a client follows the ones it knows.

type_id
required
string
schema_version
required
string
name
required
string
required
object
stocking_unit_of_measure
required
string or null <= 20 characters

Overrides the product type's inventory_config.stocking_unit_of_measure for this product. null (the default) inherits the type's unit.

vat_category
required
string
Enum: "H" "S" "R" "R2" "T" "Z" "EXEMPT"

ONIX List 62 VAT category. The per-invoice rate is resolved from it.

required
object (ReferenceValue)

Stored value for a field of type "reference"

created_at
required
integer
updated_at
required
integer
required
ReferenceValue (object) or null
required
Array of objects (IdentifierResponse)

The numbers this record carries (ISBN, GTIN, VAT ID, ...). Stored on the record itself; the identifiers sub-resource addresses one entry.

required
Array of objects (PrintedIdentifier)

Read-only. The numbers the record's type marked primary, in the order it declared them — what a picker option, a search row or a detail header shows without resolving the type itself.

Reference (object) or null

Project this product belongs to, null if unassigned

Reference (object) or null

Sales channel this product belongs to, null if unassigned

Array of objects (ProductPrice)

Inline price entries. Empty array when no price has been assigned. Single-price mode = 0 or 1 entries, no scoping fields populated. Multi-dim mode (any pricing_config flag on) = N entries with the gated scoping fields.

Request samples

Content type
application/json
{
  • "project": {
    }
}

Response samples

Content type
application/json
{
  • "primary_identifiers": [
    ],
  • "identifiers": [
    ],
  • "_id": "prd_a1b2c3d4e5f6",
  • "_class": "product",
  • "_name": "Acme Corp",
  • "_links": {
    },
  • "type_id": "toy",
  • "schema_version": "1.0.0",
  • "name": "My Product",
  • "data": { },
  • "stocking_unit_of_measure": "string",
  • "vat_category": "H",
  • "tenant": {
    },
  • "created_at": 1672531200,
  • "updated_at": 1672531200,
  • "project": {
    },
  • "channel": {
    },
  • "prices": [
    ],
  • "created_by": {
    }
}

Append a price row to a product

Sub-resource sugar over the parent Product update. Single-price mode accepts at most one row across the product's lifetime — adding a second one returns 422 until the relevant pricing_config flag is enabled on the product type. Scoping fields (country_codes, channel, …) are rejected unless their gating flag is on.

The row is new by definition, so a body carrying _id is a 422 — the server assigns price ids. To change an existing row, address its own URL.

Authorizations:
BearerAuth
path Parameters
product_id
required
string
Example: prd_a1b2c3d4e5f6
Request Body schema: application/json
required
required
object (MonetaryAmountInput)

Request-side counterpart of MonetaryAmount. Identical, except that amount also accepts a quoted decimal string — the encoding the precision contract above tells clients to use for sub-cent values, which the server's Pydantic coerces to an exact Decimal.

Responses always carry amount as a JSON number; use MonetaryAmount for those.

_id
string or null

Optional; server generates a prc_… id when omitted.

_class
string or null
Enum: "price" null

Accepted on input but ignored — the server always assigns "price".

tax_mode
string or null
Enum: "net" "gross" null
vat_category
string or null
Enum: "H" "S" "R" "R2" "T" "Z" "EXEMPT" null
country_codes
Array of strings[ items = 2 characters ]
Reference (object) or null
valid_from
string or null <date>
valid_to
string or null <date>
is_campaign
boolean
Default: false
MonetaryAmountInput (object) or null
number or string
Default: 1
unit_of_measure
string
Default: "EA"
compute
object or null
priority
integer
Default: 0

Responses

Response Schema: application/json
_id
required
string
_class
required
string
Value: "price"
required
object

self is the row's own resource URL, under the product that owns it. It survives an edit: a price id is the server's and is kept when a caller round-trips it.

required
object (MonetaryAmount)

A monetary value with an ISO 4217 currency code.

currency may be null to denote the identity ("zero") element{"amount": 0, "currency": null}. The identity absorbs into arithmetic (identity + x = x, x - x = identity, identity * n = identity); any non-zero amount must carry a currency. This is the wire form returned for an invoice draft whose totals haven't been computed yet.

Precision contract

amount is emitted as a JSON number carrying the server's Decimal with full precision — the server writes the Decimal's exact textual form as a raw JSON number token, bypassing any IEEE-754 round-trip. Sub-cent values such as 0.0003 USD (audiobook-streaming royalty rates, FX conversions, regulated reference prices) come over the wire exactly.

Client-side parsing

Standard JSON parsers convert JSON numbers to IEEE-754 floats, which lose precision for arithmetic on decimals that aren't power-of-10 fractions. Pick the right tool for the job:

  • Display only — the default float is fine. Both Python's repr(0.0003) and JavaScript's (0.0003).toString() return "0.0003", so user-visible numbers render correctly.
  • Exact arithmetic (summing line items, computing tax) — parse with a Decimal / BigDecimal type. Python: json.loads(text, parse_float=Decimal). JavaScript: a BigDecimal library such as big.js or decimal.js.

Client-side encoding for sub-cent values

A client that produces a sub-cent amount with json.dumps (Python) or JSON.stringify (JS) will also lose precision before the bytes leave the process — both encoders convert Decimal/BigDecimal to float first. Two paths preserve precision on the way in:

  1. Send the amount as a quoted decimal string. Request bodies schema this as MonetaryAmountInput, whose amount is a oneOf over number and string; the server's Pydantic coerces the string to an exact Decimal. (MonetaryAmount itself is the response shape and stays type: number.)
  2. Or use a JSON encoder on the client that supports raw number tokens (same trick as our server's encoder).

For everyday cent-precision values, sending a JSON number is fine.

tax_mode
required
string
Enum: "net" "gross"
vat_category
required
string or null
Enum: "H" "S" "R" "R2" "T" "Z" "EXEMPT" null

ONIX List 62 VAT category override for this row. Null inherits the Product-level vat_category. Settable only under by_country.

country_codes
required
Array of strings[ items = 2 characters ]

ISO 3166-1 alpha-2 codes. Empty = any. Gated by by_country.

required
Reference (object) or null

Reference to a tenant-defined Channel. Null = applies to any channel. Gated by by_channel.

valid_from
required
string or null <date>

ISO calendar date (YYYY-MM-DD), inclusive. Gated by time_validity.

valid_to
required
string or null <date>
is_campaign
required
boolean
Default: false

Gated by campaigns.

required
MonetaryAmount (object) or null
min_quantity
required
number
Default: 1

Volume-tier breakpoint. Gated by scale_prices.

unit_of_measure
required
string
Default: "EA"
compute
required
object or null

Reserved for formula_prices.

priority
required
integer
Default: 0

Tie-breaker; higher wins. Always available.

Request samples

Content type
application/json
{
  • "_id": "string",
  • "_class": "price",
  • "amount": {
    },
  • "tax_mode": "net",
  • "vat_category": "H",
  • "country_codes": [
    ],
  • "channel": {
    },
  • "valid_from": "2019-08-24",
  • "valid_to": "2019-08-24",
  • "is_campaign": false,
  • "compare_at_amount": {
    },
  • "min_quantity": 1,
  • "unit_of_measure": "EA",
  • "compute": { },
  • "priority": 0
}

Response samples

Content type
application/json
{
  • "_id": "prc_a1b2c3d4e5f6",
  • "_class": "price",
  • "_links": {
    },
  • "amount": {
    },
  • "tax_mode": "net",
  • "vat_category": "H",
  • "country_codes": [
    ],
  • "channel": {
    },
  • "valid_from": "2019-08-24",
  • "valid_to": "2019-08-24",
  • "is_campaign": false,
  • "compare_at_amount": {
    },
  • "min_quantity": 1,
  • "unit_of_measure": "EA",
  • "compute": { },
  • "priority": 0
}

Get one price row

A price is an object with an id, so it answers at its own URL — the one every row carries as _links.self. 404 when this product does not hold that row, including when it belongs to another product.

Authorizations:
BearerAuth
path Parameters
product_id
required
string
Example: prd_a1b2c3d4e5f6
price_id
required
string
Example: prc_a1b2c3d4e5f6

Responses

Response Schema: application/json
_id
required
string
_class
required
string
Value: "price"
required
object

self is the row's own resource URL, under the product that owns it. It survives an edit: a price id is the server's and is kept when a caller round-trips it.

required
object (MonetaryAmount)

A monetary value with an ISO 4217 currency code.

currency may be null to denote the identity ("zero") element{"amount": 0, "currency": null}. The identity absorbs into arithmetic (identity + x = x, x - x = identity, identity * n = identity); any non-zero amount must carry a currency. This is the wire form returned for an invoice draft whose totals haven't been computed yet.

Precision contract

amount is emitted as a JSON number carrying the server's Decimal with full precision — the server writes the Decimal's exact textual form as a raw JSON number token, bypassing any IEEE-754 round-trip. Sub-cent values such as 0.0003 USD (audiobook-streaming royalty rates, FX conversions, regulated reference prices) come over the wire exactly.

Client-side parsing

Standard JSON parsers convert JSON numbers to IEEE-754 floats, which lose precision for arithmetic on decimals that aren't power-of-10 fractions. Pick the right tool for the job:

  • Display only — the default float is fine. Both Python's repr(0.0003) and JavaScript's (0.0003).toString() return "0.0003", so user-visible numbers render correctly.
  • Exact arithmetic (summing line items, computing tax) — parse with a Decimal / BigDecimal type. Python: json.loads(text, parse_float=Decimal). JavaScript: a BigDecimal library such as big.js or decimal.js.

Client-side encoding for sub-cent values

A client that produces a sub-cent amount with json.dumps (Python) or JSON.stringify (JS) will also lose precision before the bytes leave the process — both encoders convert Decimal/BigDecimal to float first. Two paths preserve precision on the way in:

  1. Send the amount as a quoted decimal string. Request bodies schema this as MonetaryAmountInput, whose amount is a oneOf over number and string; the server's Pydantic coerces the string to an exact Decimal. (MonetaryAmount itself is the response shape and stays type: number.)
  2. Or use a JSON encoder on the client that supports raw number tokens (same trick as our server's encoder).

For everyday cent-precision values, sending a JSON number is fine.

tax_mode
required
string
Enum: "net" "gross"
vat_category
required
string or null
Enum: "H" "S" "R" "R2" "T" "Z" "EXEMPT" null

ONIX List 62 VAT category override for this row. Null inherits the Product-level vat_category. Settable only under by_country.

country_codes
required
Array of strings[ items = 2 characters ]

ISO 3166-1 alpha-2 codes. Empty = any. Gated by by_country.

required
Reference (object) or null

Reference to a tenant-defined Channel. Null = applies to any channel. Gated by by_channel.

valid_from
required
string or null <date>

ISO calendar date (YYYY-MM-DD), inclusive. Gated by time_validity.

valid_to
required
string or null <date>
is_campaign
required
boolean
Default: false

Gated by campaigns.

required
MonetaryAmount (object) or null
min_quantity
required
number
Default: 1

Volume-tier breakpoint. Gated by scale_prices.

unit_of_measure
required
string
Default: "EA"
compute
required
object or null

Reserved for formula_prices.

priority
required
integer
Default: 0

Tie-breaker; higher wins. Always available.

Response samples

Content type
application/json
{
  • "_id": "prc_a1b2c3d4e5f6",
  • "_class": "price",
  • "_links": {
    },
  • "amount": {
    },
  • "tax_mode": "net",
  • "vat_category": "H",
  • "country_codes": [
    ],
  • "channel": {
    },
  • "valid_from": "2019-08-24",
  • "valid_to": "2019-08-24",
  • "is_campaign": false,
  • "compare_at_amount": {
    },
  • "min_quantity": 1,
  • "unit_of_measure": "EA",
  • "compute": { },
  • "priority": 0
}

Replace one price row

Full replace: the body is the new row contents; the price_id stays with the row across the swap so downstream snapshots (invoice lines, change logs) keep their back-link. A body _id that disagrees with the URL is a 422 rather than being silently overridden.

Authorizations:
BearerAuth
path Parameters
product_id
required
string
Example: prd_a1b2c3d4e5f6
price_id
required
string
Example: prc_a1b2c3d4e5f6
Request Body schema: application/json
required
required
object (MonetaryAmountInput)

Request-side counterpart of MonetaryAmount. Identical, except that amount also accepts a quoted decimal string — the encoding the precision contract above tells clients to use for sub-cent values, which the server's Pydantic coerces to an exact Decimal.

Responses always carry amount as a JSON number; use MonetaryAmount for those.

_id
string or null

Optional; server generates a prc_… id when omitted.

_class
string or null
Enum: "price" null

Accepted on input but ignored — the server always assigns "price".

tax_mode
string or null
Enum: "net" "gross" null
vat_category
string or null
Enum: "H" "S" "R" "R2" "T" "Z" "EXEMPT" null
country_codes
Array of strings[ items = 2 characters ]
Reference (object) or null
valid_from
string or null <date>
valid_to
string or null <date>
is_campaign
boolean
Default: false
MonetaryAmountInput (object) or null
number or string
Default: 1
unit_of_measure
string
Default: "EA"
compute
object or null
priority
integer
Default: 0

Responses

Response Schema: application/json
_id
required
string
_class
required
string
Value: "price"
required
object

self is the row's own resource URL, under the product that owns it. It survives an edit: a price id is the server's and is kept when a caller round-trips it.

required
object (MonetaryAmount)

A monetary value with an ISO 4217 currency code.

currency may be null to denote the identity ("zero") element{"amount": 0, "currency": null}. The identity absorbs into arithmetic (identity + x = x, x - x = identity, identity * n = identity); any non-zero amount must carry a currency. This is the wire form returned for an invoice draft whose totals haven't been computed yet.

Precision contract

amount is emitted as a JSON number carrying the server's Decimal with full precision — the server writes the Decimal's exact textual form as a raw JSON number token, bypassing any IEEE-754 round-trip. Sub-cent values such as 0.0003 USD (audiobook-streaming royalty rates, FX conversions, regulated reference prices) come over the wire exactly.

Client-side parsing

Standard JSON parsers convert JSON numbers to IEEE-754 floats, which lose precision for arithmetic on decimals that aren't power-of-10 fractions. Pick the right tool for the job:

  • Display only — the default float is fine. Both Python's repr(0.0003) and JavaScript's (0.0003).toString() return "0.0003", so user-visible numbers render correctly.
  • Exact arithmetic (summing line items, computing tax) — parse with a Decimal / BigDecimal type. Python: json.loads(text, parse_float=Decimal). JavaScript: a BigDecimal library such as big.js or decimal.js.

Client-side encoding for sub-cent values

A client that produces a sub-cent amount with json.dumps (Python) or JSON.stringify (JS) will also lose precision before the bytes leave the process — both encoders convert Decimal/BigDecimal to float first. Two paths preserve precision on the way in:

  1. Send the amount as a quoted decimal string. Request bodies schema this as MonetaryAmountInput, whose amount is a oneOf over number and string; the server's Pydantic coerces the string to an exact Decimal. (MonetaryAmount itself is the response shape and stays type: number.)
  2. Or use a JSON encoder on the client that supports raw number tokens (same trick as our server's encoder).

For everyday cent-precision values, sending a JSON number is fine.

tax_mode
required
string
Enum: "net" "gross"
vat_category
required
string or null
Enum: "H" "S" "R" "R2" "T" "Z" "EXEMPT" null

ONIX List 62 VAT category override for this row. Null inherits the Product-level vat_category. Settable only under by_country.

country_codes
required
Array of strings[ items = 2 characters ]

ISO 3166-1 alpha-2 codes. Empty = any. Gated by by_country.

required
Reference (object) or null

Reference to a tenant-defined Channel. Null = applies to any channel. Gated by by_channel.

valid_from
required
string or null <date>

ISO calendar date (YYYY-MM-DD), inclusive. Gated by time_validity.

valid_to
required
string or null <date>
is_campaign
required
boolean
Default: false

Gated by campaigns.

required
MonetaryAmount (object) or null
min_quantity
required
number
Default: 1

Volume-tier breakpoint. Gated by scale_prices.

unit_of_measure
required
string
Default: "EA"
compute
required
object or null

Reserved for formula_prices.

priority
required
integer
Default: 0

Tie-breaker; higher wins. Always available.

Request samples

Content type
application/json
{
  • "_id": "string",
  • "_class": "price",
  • "amount": {
    },
  • "tax_mode": "net",
  • "vat_category": "H",
  • "country_codes": [
    ],
  • "channel": {
    },
  • "valid_from": "2019-08-24",
  • "valid_to": "2019-08-24",
  • "is_campaign": false,
  • "compare_at_amount": {
    },
  • "min_quantity": 1,
  • "unit_of_measure": "EA",
  • "compute": { },
  • "priority": 0
}

Response samples

Content type
application/json
{
  • "_id": "prc_a1b2c3d4e5f6",
  • "_class": "price",
  • "_links": {
    },
  • "amount": {
    },
  • "tax_mode": "net",
  • "vat_category": "H",
  • "country_codes": [
    ],
  • "channel": {
    },
  • "valid_from": "2019-08-24",
  • "valid_to": "2019-08-24",
  • "is_campaign": false,
  • "compare_at_amount": {
    },
  • "min_quantity": 1,
  • "unit_of_measure": "EA",
  • "compute": { },
  • "priority": 0
}

Partially update one price row

Body is an arbitrary subset of ProductPrice fields. The service merges the patch into the existing row, re-validates the result as a ProductPriceInput, and re-runs the prices-array validators (scoping guard, currency consistency, scoping-tuple uniqueness). A body _id that disagrees with the URL is a 422.

Authorizations:
BearerAuth
path Parameters
product_id
required
string
Example: prd_a1b2c3d4e5f6
price_id
required
string
Example: prc_a1b2c3d4e5f6
Request Body schema: application/json
required
property name*
additional property
any

Responses

Response Schema: application/json
_id
required
string
_class
required
string
Value: "price"
required
object

self is the row's own resource URL, under the product that owns it. It survives an edit: a price id is the server's and is kept when a caller round-trips it.

required
object (MonetaryAmount)

A monetary value with an ISO 4217 currency code.

currency may be null to denote the identity ("zero") element{"amount": 0, "currency": null}. The identity absorbs into arithmetic (identity + x = x, x - x = identity, identity * n = identity); any non-zero amount must carry a currency. This is the wire form returned for an invoice draft whose totals haven't been computed yet.

Precision contract

amount is emitted as a JSON number carrying the server's Decimal with full precision — the server writes the Decimal's exact textual form as a raw JSON number token, bypassing any IEEE-754 round-trip. Sub-cent values such as 0.0003 USD (audiobook-streaming royalty rates, FX conversions, regulated reference prices) come over the wire exactly.

Client-side parsing

Standard JSON parsers convert JSON numbers to IEEE-754 floats, which lose precision for arithmetic on decimals that aren't power-of-10 fractions. Pick the right tool for the job:

  • Display only — the default float is fine. Both Python's repr(0.0003) and JavaScript's (0.0003).toString() return "0.0003", so user-visible numbers render correctly.
  • Exact arithmetic (summing line items, computing tax) — parse with a Decimal / BigDecimal type. Python: json.loads(text, parse_float=Decimal). JavaScript: a BigDecimal library such as big.js or decimal.js.

Client-side encoding for sub-cent values

A client that produces a sub-cent amount with json.dumps (Python) or JSON.stringify (JS) will also lose precision before the bytes leave the process — both encoders convert Decimal/BigDecimal to float first. Two paths preserve precision on the way in:

  1. Send the amount as a quoted decimal string. Request bodies schema this as MonetaryAmountInput, whose amount is a oneOf over number and string; the server's Pydantic coerces the string to an exact Decimal. (MonetaryAmount itself is the response shape and stays type: number.)
  2. Or use a JSON encoder on the client that supports raw number tokens (same trick as our server's encoder).

For everyday cent-precision values, sending a JSON number is fine.

tax_mode
required
string
Enum: "net" "gross"
vat_category
required
string or null
Enum: "H" "S" "R" "R2" "T" "Z" "EXEMPT" null

ONIX List 62 VAT category override for this row. Null inherits the Product-level vat_category. Settable only under by_country.

country_codes
required
Array of strings[ items = 2 characters ]

ISO 3166-1 alpha-2 codes. Empty = any. Gated by by_country.

required
Reference (object) or null

Reference to a tenant-defined Channel. Null = applies to any channel. Gated by by_channel.

valid_from
required
string or null <date>

ISO calendar date (YYYY-MM-DD), inclusive. Gated by time_validity.

valid_to
required
string or null <date>
is_campaign
required
boolean
Default: false

Gated by campaigns.

required
MonetaryAmount (object) or null
min_quantity
required
number
Default: 1

Volume-tier breakpoint. Gated by scale_prices.

unit_of_measure
required
string
Default: "EA"
compute
required
object or null

Reserved for formula_prices.

priority
required
integer
Default: 0

Tie-breaker; higher wins. Always available.

Request samples

Content type
application/json
{ }

Response samples

Content type
application/json
{
  • "_id": "prc_a1b2c3d4e5f6",
  • "_class": "price",
  • "_links": {
    },
  • "amount": {
    },
  • "tax_mode": "net",
  • "vat_category": "H",
  • "country_codes": [
    ],
  • "channel": {
    },
  • "valid_from": "2019-08-24",
  • "valid_to": "2019-08-24",
  • "is_campaign": false,
  • "compare_at_amount": {
    },
  • "min_quantity": 1,
  • "unit_of_measure": "EA",
  • "compute": { },
  • "priority": 0
}

Remove one price row from a product

Authorizations:
BearerAuth
path Parameters
product_id
required
string
Example: prd_a1b2c3d4e5f6
price_id
required
string
Example: prc_a1b2c3d4e5f6

Responses

Response samples

Content type
application/json
{
  • "error": "not_found",
  • "message": "Business partner not found",
  • "details": { }
}

Stock

List stock locations for the authenticated tenant

Authorizations:
BearerAuth
query Parameters
limit
integer [ 1 .. 200 ]
Default: 50
next_token
string

Base64-encoded pagination cursor from previous response

Responses

Response Schema: application/json
required
Array of objects (StockLocationResponse)
next_token
required
string or null

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_token": "string"
}

Create a stock location

Authorizations:
BearerAuth
Request Body schema: application/json
required
name
required
string [ 1 .. 140 ] characters
location_type
string
Default: "warehouse"
Enum: "shop" "store_room" "vehicle" "warehouse" "other"
description
string or null
is_default
boolean
Default: false

Mark this location as the tenant default. At most one location per tenant is the default; setting it here clears the flag on any previous default. The tenant's first location is made the default automatically.

is_active
boolean
Default: true

Responses

Response Schema: application/json
_id
required
string
_class
required
string
_name
required
string (EntityName)

Read-only, backend-composed display label of this entity (a person's full name, a product's name, a project's title, …). A backend-meta field (sibling of _id / _class) — never accepted on input.

name
required
string
location_type
required
string
Enum: "shop" "store_room" "vehicle" "warehouse" "other"
is_default
required
boolean
is_active
required
boolean
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

created_at
required
integer

Unix epoch seconds

updated_at
required
integer

Unix epoch seconds

required
Reference (object) or null
description
string or null

Request samples

Content type
application/json
{
  • "name": "Shop",
  • "location_type": "shop",
  • "description": "string",
  • "is_default": false,
  • "is_active": true
}

Response samples

Content type
application/json
{
  • "_id": "stl_a1b2c3d4e5f6",
  • "_class": "stock_location",
  • "_name": "Acme Corp",
  • "name": "string",
  • "location_type": "shop",
  • "description": "string",
  • "is_default": true,
  • "is_active": true,
  • "tenant": {
    },
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    }
}

Get a stock location

Authorizations:
BearerAuth
path Parameters
stock_location_id
required
string
Example: stl_a1b2c3d4e5f6

Responses

Response Schema: application/json
_id
required
string
_class
required
string
_name
required
string (EntityName)

Read-only, backend-composed display label of this entity (a person's full name, a product's name, a project's title, …). A backend-meta field (sibling of _id / _class) — never accepted on input.

name
required
string
location_type
required
string
Enum: "shop" "store_room" "vehicle" "warehouse" "other"
is_default
required
boolean
is_active
required
boolean
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

created_at
required
integer

Unix epoch seconds

updated_at
required
integer

Unix epoch seconds

required
Reference (object) or null
description
string or null

Response samples

Content type
application/json
{
  • "_id": "stl_a1b2c3d4e5f6",
  • "_class": "stock_location",
  • "_name": "Acme Corp",
  • "name": "string",
  • "location_type": "shop",
  • "description": "string",
  • "is_default": true,
  • "is_active": true,
  • "tenant": {
    },
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    }
}

Update a stock location (alias of PATCH — only supplied fields change)

Authorizations:
BearerAuth
path Parameters
stock_location_id
required
string
Example: stl_a1b2c3d4e5f6
Request Body schema: application/json
required
name
string [ 1 .. 140 ] characters
location_type
string
Enum: "shop" "store_room" "vehicle" "warehouse" "other"
description
string or null
is_default
boolean
is_active
boolean

Responses

Response Schema: application/json
_id
required
string
_class
required
string
_name
required
string (EntityName)

Read-only, backend-composed display label of this entity (a person's full name, a product's name, a project's title, …). A backend-meta field (sibling of _id / _class) — never accepted on input.

name
required
string
location_type
required
string
Enum: "shop" "store_room" "vehicle" "warehouse" "other"
is_default
required
boolean
is_active
required
boolean
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

created_at
required
integer

Unix epoch seconds

updated_at
required
integer

Unix epoch seconds

required
Reference (object) or null
description
string or null

Request samples

Content type
application/json
{
  • "name": "string",
  • "location_type": "shop",
  • "description": "string",
  • "is_default": true,
  • "is_active": true
}

Response samples

Content type
application/json
{
  • "_id": "stl_a1b2c3d4e5f6",
  • "_class": "stock_location",
  • "_name": "Acme Corp",
  • "name": "string",
  • "location_type": "shop",
  • "description": "string",
  • "is_default": true,
  • "is_active": true,
  • "tenant": {
    },
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    }
}

Partially update a stock location

Authorizations:
BearerAuth
path Parameters
stock_location_id
required
string
Example: stl_a1b2c3d4e5f6
Request Body schema: application/json
required
name
string [ 1 .. 140 ] characters
location_type
string
Enum: "shop" "store_room" "vehicle" "warehouse" "other"
description
string or null
is_default
boolean
is_active
boolean

Responses

Response Schema: application/json
_id
required
string
_class
required
string
_name
required
string (EntityName)

Read-only, backend-composed display label of this entity (a person's full name, a product's name, a project's title, …). A backend-meta field (sibling of _id / _class) — never accepted on input.

name
required
string
location_type
required
string
Enum: "shop" "store_room" "vehicle" "warehouse" "other"
is_default
required
boolean
is_active
required
boolean
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

created_at
required
integer

Unix epoch seconds

updated_at
required
integer

Unix epoch seconds

required
Reference (object) or null
description
string or null

Request samples

Content type
application/json
{
  • "name": "string",
  • "location_type": "shop",
  • "description": "string",
  • "is_default": true,
  • "is_active": true
}

Response samples

Content type
application/json
{
  • "_id": "stl_a1b2c3d4e5f6",
  • "_class": "stock_location",
  • "_name": "Acme Corp",
  • "name": "string",
  • "location_type": "shop",
  • "description": "string",
  • "is_default": true,
  • "is_active": true,
  • "tenant": {
    },
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    }
}

Delete a stock location

Rejected with 409 when the location is the tenant default (reassign first) or still holds stock.

Authorizations:
BearerAuth
path Parameters
stock_location_id
required
string
Example: stl_a1b2c3d4e5f6

Responses

Response samples

Content type
application/json
{
  • "error": "not_found",
  • "message": "Business partner not found",
  • "details": { }
}

List the movement ledger for one product at one location

Authorizations:
BearerAuth
query Parameters
product_id
required
string
stock_location_id
required
string
limit
integer [ 1 .. 200 ]
Default: 50
next_token
string

Base64-encoded pagination cursor from previous response

Responses

Response Schema: application/json
required
Array of objects (StockMovementResponse)
next_token
required
string or null

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_token": "string"
}

Record a stock movement (opening / receipt / issue / adjustment)

Authorizations:
BearerAuth
Request Body schema: application/json
required
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

movement_type
required
string
Enum: "opening" "receipt" "issue" "adjustment"

opening/receipt add stock (positive quantity_delta, unit_cost required); issue removes stock (negative); adjustment is a signed correction. count and transfer have their own endpoints.

quantity_delta
required
number

Signed quantity change; must be non-zero.

MonetaryAmountInput (object) or null

Required on opening/receipt so weighted-average cost recomputes.

reason
string or null
dedupe_key
string or null [ 1 .. 128 ] characters

Optional. A second write with the same key within the tenant does not create a second row. The existing movement is returned unchanged.

effective_at
integer or null

Business date (unix seconds); defaults to now.

Responses

Response Schema: application/json
_id
required
string
_class
required
string
_name
required
string

The movement type and its signed quantity, e.g. "receipt +20".

movement_type
required
string
Enum: "opening" "receipt" "issue" "transfer" "adjustment" "count"
quantity_delta
required
number
required
object (MonetaryAmount)

A monetary value with an ISO 4217 currency code.

currency may be null to denote the identity ("zero") element{"amount": 0, "currency": null}. The identity absorbs into arithmetic (identity + x = x, x - x = identity, identity * n = identity); any non-zero amount must carry a currency. This is the wire form returned for an invoice draft whose totals haven't been computed yet.

Precision contract

amount is emitted as a JSON number carrying the server's Decimal with full precision — the server writes the Decimal's exact textual form as a raw JSON number token, bypassing any IEEE-754 round-trip. Sub-cent values such as 0.0003 USD (audiobook-streaming royalty rates, FX conversions, regulated reference prices) come over the wire exactly.

Client-side parsing

Standard JSON parsers convert JSON numbers to IEEE-754 floats, which lose precision for arithmetic on decimals that aren't power-of-10 fractions. Pick the right tool for the job:

  • Display only — the default float is fine. Both Python's repr(0.0003) and JavaScript's (0.0003).toString() return "0.0003", so user-visible numbers render correctly.
  • Exact arithmetic (summing line items, computing tax) — parse with a Decimal / BigDecimal type. Python: json.loads(text, parse_float=Decimal). JavaScript: a BigDecimal library such as big.js or decimal.js.

Client-side encoding for sub-cent values

A client that produces a sub-cent amount with json.dumps (Python) or JSON.stringify (JS) will also lose precision before the bytes leave the process — both encoders convert Decimal/BigDecimal to float first. Two paths preserve precision on the way in:

  1. Send the amount as a quoted decimal string. Request bodies schema this as MonetaryAmountInput, whose amount is a oneOf over number and string; the server's Pydantic coerces the string to an exact Decimal. (MonetaryAmount itself is the response shape and stays type: number.)
  2. Or use a JSON encoder on the client that supports raw number tokens (same trick as our server's encoder).

For everyday cent-precision values, sending a JSON number is fine.

required
object (MonetaryAmount)

A monetary value with an ISO 4217 currency code.

currency may be null to denote the identity ("zero") element{"amount": 0, "currency": null}. The identity absorbs into arithmetic (identity + x = x, x - x = identity, identity * n = identity); any non-zero amount must carry a currency. This is the wire form returned for an invoice draft whose totals haven't been computed yet.

Precision contract

amount is emitted as a JSON number carrying the server's Decimal with full precision — the server writes the Decimal's exact textual form as a raw JSON number token, bypassing any IEEE-754 round-trip. Sub-cent values such as 0.0003 USD (audiobook-streaming royalty rates, FX conversions, regulated reference prices) come over the wire exactly.

Client-side parsing

Standard JSON parsers convert JSON numbers to IEEE-754 floats, which lose precision for arithmetic on decimals that aren't power-of-10 fractions. Pick the right tool for the job:

  • Display only — the default float is fine. Both Python's repr(0.0003) and JavaScript's (0.0003).toString() return "0.0003", so user-visible numbers render correctly.
  • Exact arithmetic (summing line items, computing tax) — parse with a Decimal / BigDecimal type. Python: json.loads(text, parse_float=Decimal). JavaScript: a BigDecimal library such as big.js or decimal.js.

Client-side encoding for sub-cent values

A client that produces a sub-cent amount with json.dumps (Python) or JSON.stringify (JS) will also lose precision before the bytes leave the process — both encoders convert Decimal/BigDecimal to float first. Two paths preserve precision on the way in:

  1. Send the amount as a quoted decimal string. Request bodies schema this as MonetaryAmountInput, whose amount is a oneOf over number and string; the server's Pydantic coerces the string to an exact Decimal. (MonetaryAmount itself is the response shape and stays type: number.)
  2. Or use a JSON encoder on the client that supports raw number tokens (same trick as our server's encoder).

For everyday cent-precision values, sending a JSON number is fine.

dedupe_key
required
string or null
effective_at
required
integer
required
Reference (object) or null

The offer or invoice this movement fulfils (a dispatch line).

document_line_item_id
required
string or null
required
Reference (object) or null

The receipt, stocktake or dispatch that posted this movement.

required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

created_at
required
integer

Unix epoch seconds

updated_at
required
integer

Unix epoch seconds

required
Reference (object) or null
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

Reference (object) or null
MonetaryAmount (object) or null
balance_after
number or null
reason
string or null

Request samples

Content type
application/json
{
  • "product": {
    },
  • "stock_location": {
    },
  • "movement_type": "opening",
  • "quantity_delta": 0,
  • "unit_cost": {
    },
  • "reason": "string",
  • "dedupe_key": "string",
  • "effective_at": 0
}

Response samples

Content type
application/json
{
  • "_id": "stm_a1b2c3d4e5f6",
  • "_class": "stock_movement",
  • "_name": "string",
  • "tenant": {
    },
  • "product": {
    },
  • "stock_location": {
    },
  • "counterparty_location": {
    },
  • "movement_type": "opening",
  • "quantity_delta": 0,
  • "unit_cost": {
    },
  • "value_delta": {
    },
  • "average_cost_after": {
    },
  • "balance_after": 0,
  • "reason": "string",
  • "dedupe_key": "string",
  • "effective_at": 0,
  • "source_document": {
    },
  • "document_line_item_id": "string",
  • "stock_document": {
    },
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    }
}

Post a physical-count variance

Supply the absolute counted quantity; the service posts a count movement for the difference from the current on-hand.

Authorizations:
BearerAuth
Request Body schema: application/json
required
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

counted_quantity
required
number >= 0

Absolute counted quantity; the service posts the variance.

reason
string or null
dedupe_key
string or null [ 1 .. 128 ] characters

Optional. A second write with the same key within the tenant does not create a second row. The existing movement is returned unchanged.

effective_at
integer or null

Responses

Response Schema: application/json
_id
required
string
_class
required
string
_name
required
string

The movement type and its signed quantity, e.g. "receipt +20".

movement_type
required
string
Enum: "opening" "receipt" "issue" "transfer" "adjustment" "count"
quantity_delta
required
number
required
object (MonetaryAmount)

A monetary value with an ISO 4217 currency code.

currency may be null to denote the identity ("zero") element{"amount": 0, "currency": null}. The identity absorbs into arithmetic (identity + x = x, x - x = identity, identity * n = identity); any non-zero amount must carry a currency. This is the wire form returned for an invoice draft whose totals haven't been computed yet.

Precision contract

amount is emitted as a JSON number carrying the server's Decimal with full precision — the server writes the Decimal's exact textual form as a raw JSON number token, bypassing any IEEE-754 round-trip. Sub-cent values such as 0.0003 USD (audiobook-streaming royalty rates, FX conversions, regulated reference prices) come over the wire exactly.

Client-side parsing

Standard JSON parsers convert JSON numbers to IEEE-754 floats, which lose precision for arithmetic on decimals that aren't power-of-10 fractions. Pick the right tool for the job:

  • Display only — the default float is fine. Both Python's repr(0.0003) and JavaScript's (0.0003).toString() return "0.0003", so user-visible numbers render correctly.
  • Exact arithmetic (summing line items, computing tax) — parse with a Decimal / BigDecimal type. Python: json.loads(text, parse_float=Decimal). JavaScript: a BigDecimal library such as big.js or decimal.js.

Client-side encoding for sub-cent values

A client that produces a sub-cent amount with json.dumps (Python) or JSON.stringify (JS) will also lose precision before the bytes leave the process — both encoders convert Decimal/BigDecimal to float first. Two paths preserve precision on the way in:

  1. Send the amount as a quoted decimal string. Request bodies schema this as MonetaryAmountInput, whose amount is a oneOf over number and string; the server's Pydantic coerces the string to an exact Decimal. (MonetaryAmount itself is the response shape and stays type: number.)
  2. Or use a JSON encoder on the client that supports raw number tokens (same trick as our server's encoder).

For everyday cent-precision values, sending a JSON number is fine.

required
object (MonetaryAmount)

A monetary value with an ISO 4217 currency code.

currency may be null to denote the identity ("zero") element{"amount": 0, "currency": null}. The identity absorbs into arithmetic (identity + x = x, x - x = identity, identity * n = identity); any non-zero amount must carry a currency. This is the wire form returned for an invoice draft whose totals haven't been computed yet.

Precision contract

amount is emitted as a JSON number carrying the server's Decimal with full precision — the server writes the Decimal's exact textual form as a raw JSON number token, bypassing any IEEE-754 round-trip. Sub-cent values such as 0.0003 USD (audiobook-streaming royalty rates, FX conversions, regulated reference prices) come over the wire exactly.

Client-side parsing

Standard JSON parsers convert JSON numbers to IEEE-754 floats, which lose precision for arithmetic on decimals that aren't power-of-10 fractions. Pick the right tool for the job:

  • Display only — the default float is fine. Both Python's repr(0.0003) and JavaScript's (0.0003).toString() return "0.0003", so user-visible numbers render correctly.
  • Exact arithmetic (summing line items, computing tax) — parse with a Decimal / BigDecimal type. Python: json.loads(text, parse_float=Decimal). JavaScript: a BigDecimal library such as big.js or decimal.js.

Client-side encoding for sub-cent values

A client that produces a sub-cent amount with json.dumps (Python) or JSON.stringify (JS) will also lose precision before the bytes leave the process — both encoders convert Decimal/BigDecimal to float first. Two paths preserve precision on the way in:

  1. Send the amount as a quoted decimal string. Request bodies schema this as MonetaryAmountInput, whose amount is a oneOf over number and string; the server's Pydantic coerces the string to an exact Decimal. (MonetaryAmount itself is the response shape and stays type: number.)
  2. Or use a JSON encoder on the client that supports raw number tokens (same trick as our server's encoder).

For everyday cent-precision values, sending a JSON number is fine.

dedupe_key
required
string or null
effective_at
required
integer
required
Reference (object) or null

The offer or invoice this movement fulfils (a dispatch line).

document_line_item_id
required
string or null
required
Reference (object) or null

The receipt, stocktake or dispatch that posted this movement.

required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

created_at
required
integer

Unix epoch seconds

updated_at
required
integer

Unix epoch seconds

required
Reference (object) or null
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

Reference (object) or null
MonetaryAmount (object) or null
balance_after
number or null
reason
string or null

Request samples

Content type
application/json
{
  • "product": {
    },
  • "stock_location": {
    },
  • "counted_quantity": 0,
  • "reason": "string",
  • "dedupe_key": "string",
  • "effective_at": 0
}

Response samples

Content type
application/json
{
  • "_id": "stm_a1b2c3d4e5f6",
  • "_class": "stock_movement",
  • "_name": "string",
  • "tenant": {
    },
  • "product": {
    },
  • "stock_location": {
    },
  • "counterparty_location": {
    },
  • "movement_type": "opening",
  • "quantity_delta": 0,
  • "unit_cost": {
    },
  • "value_delta": {
    },
  • "average_cost_after": {
    },
  • "balance_after": 0,
  • "reason": "string",
  • "dedupe_key": "string",
  • "effective_at": 0,
  • "source_document": {
    },
  • "document_line_item_id": "string",
  • "stock_document": {
    },
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    }
}

Transfer stock between two locations

Emits an issue on the source and a receipt on the destination in one transaction, carrying the source's average cost across.

Authorizations:
BearerAuth
Request Body schema: application/json
required
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

quantity
required
number > 0
reason
string or null
dedupe_key
string or null [ 1 .. 128 ] characters

Optional. A second write with the same key within the tenant does not create a second row. The existing movement is returned unchanged.

effective_at
integer or null

Responses

Response Schema: application/json
required
object (StockMovementResponse)
required
object (StockMovementResponse)

Request samples

Content type
application/json
{
  • "product": {
    },
  • "from_location": {
    },
  • "to_location": {
    },
  • "quantity": 0,
  • "reason": "string",
  • "dedupe_key": "string",
  • "effective_at": 0
}

Response samples

Content type
application/json
{
  • "source": {
    },
  • "destination": {
    }
}

Stock-development series (running on-hand balance over time)

Authorizations:
BearerAuth
query Parameters
product_id
required
string
stock_location_id
string

Omit to get the product's total on-hand across all locations; supply to scope the series to one location.

Responses

Response Schema: application/json
required
Array of objects

Response samples

Content type
application/json
{
  • "series": [
    ]
}

List stock levels for a product (optionally one location)

Authorizations:
BearerAuth
query Parameters
product_id
required
string
stock_location_id
string

Responses

Response Schema: application/json
required
Array of objects (StockLevelResponse)
next_token
required
string or null

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_token": "string"
}

Set or clear the reorder point on a level

Authorizations:
BearerAuth
Request Body schema: application/json
required
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

reorder_point
number or null >= 0

Responses

Response Schema: application/json
_id
required
string

Composite id "{product_id}.{stock_location_id}".

_class
required
string
on_hand
required
number
reserved
required
number
available
required
number

Computed on-hand minus reserved (never stored).

required
object (MonetaryAmount)

A monetary value with an ISO 4217 currency code.

currency may be null to denote the identity ("zero") element{"amount": 0, "currency": null}. The identity absorbs into arithmetic (identity + x = x, x - x = identity, identity * n = identity); any non-zero amount must carry a currency. This is the wire form returned for an invoice draft whose totals haven't been computed yet.

Precision contract

amount is emitted as a JSON number carrying the server's Decimal with full precision — the server writes the Decimal's exact textual form as a raw JSON number token, bypassing any IEEE-754 round-trip. Sub-cent values such as 0.0003 USD (audiobook-streaming royalty rates, FX conversions, regulated reference prices) come over the wire exactly.

Client-side parsing

Standard JSON parsers convert JSON numbers to IEEE-754 floats, which lose precision for arithmetic on decimals that aren't power-of-10 fractions. Pick the right tool for the job:

  • Display only — the default float is fine. Both Python's repr(0.0003) and JavaScript's (0.0003).toString() return "0.0003", so user-visible numbers render correctly.
  • Exact arithmetic (summing line items, computing tax) — parse with a Decimal / BigDecimal type. Python: json.loads(text, parse_float=Decimal). JavaScript: a BigDecimal library such as big.js or decimal.js.

Client-side encoding for sub-cent values

A client that produces a sub-cent amount with json.dumps (Python) or JSON.stringify (JS) will also lose precision before the bytes leave the process — both encoders convert Decimal/BigDecimal to float first. Two paths preserve precision on the way in:

  1. Send the amount as a quoted decimal string. Request bodies schema this as MonetaryAmountInput, whose amount is a oneOf over number and string; the server's Pydantic coerces the string to an exact Decimal. (MonetaryAmount itself is the response shape and stays type: number.)
  2. Or use a JSON encoder on the client that supports raw number tokens (same trick as our server's encoder).

For everyday cent-precision values, sending a JSON number is fine.

required
object

Backend-computed on_hand x average_cost.

stocking_unit_of_measure
required
string
last_counted_at
required
integer or null

The business date of the last stocktake that counted this level.

required
Reference (object) or null

The stocktake that last counted this level.

required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

created_at
required
integer

Unix epoch seconds

updated_at
required
integer

Unix epoch seconds

required
Reference (object) or null
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

reorder_point
number or null

Request samples

Content type
application/json
{
  • "product": {
    },
  • "stock_location": {
    },
  • "reorder_point": 0
}

Response samples

Content type
application/json
{
  • "_id": "prd_abc.stl_xyz",
  • "_class": "stock_level",
  • "tenant": {
    },
  • "product": {
    },
  • "stock_location": {
    },
  • "on_hand": 0,
  • "reserved": 0,
  • "available": 0,
  • "average_cost": {
    },
  • "stock_value": {
    },
  • "reorder_point": 0,
  • "stocking_unit_of_measure": "string",
  • "last_counted_at": 0,
  • "last_count": {
    },
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    }
}

List goods receipts, newest first

Authorizations:
BearerAuth
query Parameters
stock_location_id
string
limit
integer [ 1 .. 200 ]
Default: 50
next_token
string

Base64-encoded pagination cursor from previous response

Responses

Response Schema: application/json
required
Array of objects (StockReceiptResponse)
next_token
required
string or null

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_token": "string"
}

Post a goods receipt — one receipt movement per line, then the applied record

Lines post first, keyed {dedupe_key}:{line dedupe_key} so a retried sheet replays already-posted lines as no-ops; the header is written once every line landed. When a line fails the response is a 422 whose details.lines carries every line's outcome and no header is written.

Authorizations:
BearerAuth
Request Body schema: application/json
required
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

dedupe_key
required
string [ 1 .. 64 ] characters

Required. A second write with the same key within the tenant does not create a second row. The existing sheet is returned unchanged, every line replayed.

required
Array of objects (StockReceiptLineCreate) [ 1 .. 500 ] items
effective_at
integer or null

Business date (unix seconds); defaults to now.

reference
string or null

The supplier's delivery-note number, as given.

note
string or null

Responses

Response Schema: application/json
_id
required
string
_class
required
string
_name
required
string
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

effective_at
required
integer
reference
required
string or null
note
required
string or null
dedupe_key
required
string
required
Array of objects (StockReceiptLine)
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

created_at
required
integer
updated_at
required
integer
required
Reference (object) or null
required
Array of objects (StockReceiptLineResult)

The outcome per line of this post.

Request samples

Content type
application/json
{
  • "stock_location": {
    },
  • "dedupe_key": "string",
  • "lines": [
    ],
  • "effective_at": 0,
  • "reference": "string",
  • "note": "string"
}

Response samples

Content type
application/json
{
  • "_id": "srct_a1b2c3d4e5f6",
  • "_class": "stock_receipt",
  • "_name": "string",
  • "tenant": {
    },
  • "stock_location": {
    },
  • "effective_at": 0,
  • "reference": "string",
  • "note": "string",
  • "dedupe_key": "string",
  • "lines": [
    ],
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    },
  • "lines_result": [
    ]
}

Get a goods receipt

Authorizations:
BearerAuth
path Parameters
stock_receipt_id
required
string

Responses

Response Schema: application/json
_id
required
string
_class
required
string
_name
required
string
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

effective_at
required
integer
reference
required
string or null
note
required
string or null
dedupe_key
required
string
required
Array of objects (StockReceiptLine)
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

created_at
required
integer
updated_at
required
integer
required
Reference (object) or null

Response samples

Content type
application/json
{
  • "_id": "srct_a1b2c3d4e5f6",
  • "_class": "stock_receipt",
  • "_name": "string",
  • "tenant": {
    },
  • "stock_location": {
    },
  • "effective_at": 0,
  • "reference": "string",
  • "note": "string",
  • "dedupe_key": "string",
  • "lines": [
    ],
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    }
}

List stocktakes, newest first

Authorizations:
BearerAuth
query Parameters
stock_location_id
string
limit
integer [ 1 .. 200 ]
Default: 50
next_token
string

Base64-encoded pagination cursor from previous response

Responses

Response Schema: application/json
required
Array of objects (StockCountResponse)
next_token
required
string or null

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_token": "string"
}

Post a stocktake — a count movement per line with a variance, then the applied sheet

Every line's variance is measured against the ledger balance as of the count date, so a backdated count does not undo movements posted since. Lines whose counted figure matches post no movement but stay on the sheet. Lines post first, keyed {dedupe_key}:{line dedupe_key}, so a retried sheet replays; the sheet is written once every line landed, and every counted level is stamped last_counted_at.

Authorizations:
BearerAuth
Request Body schema: application/json
required
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

dedupe_key
required
string [ 1 .. 64 ] characters

Required. A second write with the same key within the tenant does not create a second row. The existing sheet is returned unchanged, every line replayed.

required
Array of objects (StockCountLineCreate) [ 1 .. 500 ] items
effective_at
integer or null

The count date (unix seconds); defaults to now. Variances are measured against the ledger as of this date.

note
string or null

Responses

Response Schema: application/json
_id
required
string
_class
required
string
_name
required
string
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

effective_at
required
integer
note
required
string or null
dedupe_key
required
string
required
Array of objects (StockCountLine)
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

created_at
required
integer
updated_at
required
integer
required
Reference (object) or null
required
Array of objects (StockCountLineResult)

Request samples

Content type
application/json
{
  • "stock_location": {
    },
  • "dedupe_key": "string",
  • "lines": [
    ],
  • "effective_at": 0,
  • "note": "string"
}

Response samples

Content type
application/json
{
  • "_id": "stc_a1b2c3d4e5f6",
  • "_class": "stock_count",
  • "_name": "string",
  • "tenant": {
    },
  • "stock_location": {
    },
  • "effective_at": 0,
  • "note": "string",
  • "dedupe_key": "string",
  • "lines": [
    ],
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    },
  • "lines_result": [
    ]
}

Get a stocktake

Authorizations:
BearerAuth
path Parameters
stock_count_id
required
string

Responses

Response Schema: application/json
_id
required
string
_class
required
string
_name
required
string
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

effective_at
required
integer
note
required
string or null
dedupe_key
required
string
required
Array of objects (StockCountLine)
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

created_at
required
integer
updated_at
required
integer
required
Reference (object) or null

Response samples

Content type
application/json
{
  • "_id": "stc_a1b2c3d4e5f6",
  • "_class": "stock_count",
  • "_name": "string",
  • "tenant": {
    },
  • "stock_location": {
    },
  • "effective_at": 0,
  • "note": "string",
  • "dedupe_key": "string",
  • "lines": [
    ],
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    }
}

List goods dispatches, newest first

Authorizations:
BearerAuth
query Parameters
document_id
string

Only dispatches shipped against this offer or invoice; the response then also carries shipped_by_line.

limit
integer [ 1 .. 200 ]
Default: 50
next_token
string

Base64-encoded pagination cursor from previous response

Responses

Response Schema: application/json
required
Array of objects (StockDispatchResponse)
next_token
required
string or null
object

Present when filtered by document_id — quantity already shipped per document line, across every dispatch.

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_token": "string",
  • "shipped_by_line": {
    }
}

Ship goods — an issue movement per tracked line, then the numbered dispatch record

From an accepted offer or an invoice (any status but cancelled), or on its own. Every tracked line posts an issue movement keyed {dedupe_key}:{line dedupe_key} naming the customer document and the dispatch; a draft invoice's reservation is lowered in the same write. Untracked lines are skipped and reported. The header is written once every line landed and numbered on the tenant's delivery-note series.

Authorizations:
BearerAuth
Request Body schema: application/json
required
dedupe_key
required
string [ 1 .. 64 ] characters

Required. A second write with the same key within the tenant does not create a second row. The existing sheet is returned unchanged, every line replayed.

required
Array of objects (StockDispatchLineCreate) [ 1 .. 500 ] items
Reference (object) or null

The accepted offer or the invoice being shipped. Omit for a standalone dispatch.

Reference (object) or null

Recipient of a standalone dispatch.

Reference (object) or null

The location the goods leave from; defaults to the tenant default.

effective_at
integer or null
reference
string or null

A customer order or waybill number.

note
string or null

Responses

Response Schema: application/json
_id
required
string
_class
required
string
_name
required
string

The dispatch number once posted.

dispatch_number
required
string
status
required
string
Enum: "draft" "posted"
required
Reference (object) or null
required
Reference (object) or null
recipient_name
required
string or null
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

effective_at
required
integer
reference
required
string or null
note
required
string or null
dedupe_key
required
string
required
Array of objects (StockDispatchLine)
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

created_at
required
integer
updated_at
required
integer
required
Reference (object) or null
required
Array of objects (StockDispatchLineResult)

Request samples

Content type
application/json
{
  • "source_document": {
    },
  • "business_partner": {
    },
  • "stock_location": {
    },
  • "dedupe_key": "string",
  • "lines": [
    ],
  • "effective_at": 0,
  • "reference": "string",
  • "note": "string"
}

Response samples

Content type
application/json
{
  • "_id": "sdp_a1b2c3d4e5f6",
  • "_class": "stock_dispatch",
  • "_name": "string",
  • "dispatch_number": "string",
  • "status": "draft",
  • "source_document": {
    },
  • "business_partner": {
    },
  • "recipient_name": "string",
  • "tenant": {
    },
  • "stock_location": {
    },
  • "effective_at": 0,
  • "reference": "string",
  • "note": "string",
  • "dedupe_key": "string",
  • "lines": [
    ],
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    },
  • "lines_result": [
    ]
}

Get a goods dispatch

Authorizations:
BearerAuth
path Parameters
stock_dispatch_id
required
string

Responses

Response Schema: application/json
_id
required
string
_class
required
string
_name
required
string

The dispatch number once posted.

dispatch_number
required
string
status
required
string
Enum: "draft" "posted"
required
Reference (object) or null
required
Reference (object) or null
recipient_name
required
string or null
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

effective_at
required
integer
reference
required
string or null
note
required
string or null
dedupe_key
required
string
required
Array of objects (StockDispatchLine)
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

created_at
required
integer
updated_at
required
integer
required
Reference (object) or null

Response samples

Content type
application/json
{
  • "_id": "sdp_a1b2c3d4e5f6",
  • "_class": "stock_dispatch",
  • "_name": "string",
  • "dispatch_number": "string",
  • "status": "draft",
  • "source_document": {
    },
  • "business_partner": {
    },
  • "recipient_name": "string",
  • "tenant": {
    },
  • "stock_location": {
    },
  • "effective_at": 0,
  • "reference": "string",
  • "note": "string",
  • "dedupe_key": "string",
  • "lines": [
    ],
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    }
}

List a dispatch's generated documents

Authorizations:
BearerAuth
path Parameters
stock_dispatch_id
required
string
Example: sdp_a1b2c3d4e5f6

Responses

Response Schema: application/json
required
Array of objects (StockDispatchDocumentResponse)
next_token
required
string or null

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_token": "string"
}

Request the delivery note PDF

Writes the document in pdf_pending and publishes the event the generator subscribes to. The dispatch must be posted; a posted dispatch already has one requested for it.

Authorizations:
BearerAuth
path Parameters
stock_dispatch_id
required
string
Example: sdp_a1b2c3d4e5f6
Request Body schema: application/json
optional
document_type
string (StockDispatchDocumentType)
Value: "delivery_note"

One artefact today, the rendered delivery note.

Responses

Response Schema: application/json
_id
required
string
_class
required
string
stock_dispatch_id
required
string
document_type
required
string (StockDispatchDocumentType)
Value: "delivery_note"

One artefact today, the rendered delivery note.

status
required
string (DocumentStatus)
Enum: "draft" "pdf_pending" "pdf_ready" "pdf_failed"

Lifecycle state. New documents start at pdf_pending; the async PDF worker promotes them to pdf_ready (or pdf_failed on permanent error). draft is reserved for future use.

required
Reference (object) or null

Reference to the rendered PDF; null until the worker attaches it.

template_version
required
string
created_at
required
integer
updated_at
required
integer
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

required
ReferenceValue (object) or null

Request samples

Content type
application/json
{
  • "document_type": "delivery_note"
}

Response samples

Content type
application/json
{
  • "_id": "sddoc_a1b2c3d4e5f6789012345",
  • "_class": "stock_dispatch_document",
  • "stock_dispatch_id": "sdp_a1b2c3d4e5f6",
  • "document_type": "delivery_note",
  • "status": "draft",
  • "file": {
    },
  • "template_version": "1.0.0",
  • "created_at": 0,
  • "updated_at": 0,
  • "tenant": {
    },
  • "created_by": {
    }
}

Get one of a dispatch's documents

Authorizations:
BearerAuth
path Parameters
stock_dispatch_id
required
string
Example: sdp_a1b2c3d4e5f6
stock_dispatch_document_id
required
string
Example: sddoc_a1b2c3d4e5f6789012345

Responses

Response Schema: application/json
_id
required
string
_class
required
string
stock_dispatch_id
required
string
document_type
required
string (StockDispatchDocumentType)
Value: "delivery_note"

One artefact today, the rendered delivery note.

status
required
string (DocumentStatus)
Enum: "draft" "pdf_pending" "pdf_ready" "pdf_failed"

Lifecycle state. New documents start at pdf_pending; the async PDF worker promotes them to pdf_ready (or pdf_failed on permanent error). draft is reserved for future use.

required
Reference (object) or null

Reference to the rendered PDF; null until the worker attaches it.

template_version
required
string
created_at
required
integer
updated_at
required
integer
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

required
ReferenceValue (object) or null

Response samples

Content type
application/json
{
  • "_id": "sddoc_a1b2c3d4e5f6789012345",
  • "_class": "stock_dispatch_document",
  • "stock_dispatch_id": "sdp_a1b2c3d4e5f6",
  • "document_type": "delivery_note",
  • "status": "draft",
  • "file": {
    },
  • "template_version": "1.0.0",
  • "created_at": 0,
  • "updated_at": 0,
  • "tenant": {
    },
  • "created_by": {
    }
}

Attach the rendered PDF, or retry a failed render

Setting file on a pdf_pending document moves it to pdf_ready. Idempotent: replaying on an already-attached document returns the PDF it already has. Setting status to pdf_pending re-requests the render of a failed document.

Authorizations:
BearerAuth
path Parameters
stock_dispatch_id
required
string
Example: sdp_a1b2c3d4e5f6
stock_dispatch_document_id
required
string
Example: sddoc_a1b2c3d4e5f6789012345
Request Body schema: application/json
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

status
string
Value: "pdf_pending"

Responses

Response Schema: application/json
_id
required
string
_class
required
string
stock_dispatch_id
required
string
document_type
required
string (StockDispatchDocumentType)
Value: "delivery_note"

One artefact today, the rendered delivery note.

status
required
string (DocumentStatus)
Enum: "draft" "pdf_pending" "pdf_ready" "pdf_failed"

Lifecycle state. New documents start at pdf_pending; the async PDF worker promotes them to pdf_ready (or pdf_failed on permanent error). draft is reserved for future use.

required
Reference (object) or null

Reference to the rendered PDF; null until the worker attaches it.

template_version
required
string
created_at
required
integer
updated_at
required
integer
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

required
ReferenceValue (object) or null

Request samples

Content type
application/json
{
  • "file": {
    },
  • "status": "pdf_pending"
}

Response samples

Content type
application/json
{
  • "_id": "sddoc_a1b2c3d4e5f6789012345",
  • "_class": "stock_dispatch_document",
  • "stock_dispatch_id": "sdp_a1b2c3d4e5f6",
  • "document_type": "delivery_note",
  • "status": "draft",
  • "file": {
    },
  • "template_version": "1.0.0",
  • "created_at": 0,
  • "updated_at": 0,
  • "tenant": {
    },
  • "created_by": {
    }
}

Attach the rendered PDF, or retry a failed render

Authorizations:
BearerAuth
path Parameters
stock_dispatch_id
required
string
Example: sdp_a1b2c3d4e5f6
stock_dispatch_document_id
required
string
Example: sddoc_a1b2c3d4e5f6789012345
Request Body schema: application/json
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

status
string
Value: "pdf_pending"

Responses

Response Schema: application/json
_id
required
string
_class
required
string
stock_dispatch_id
required
string
document_type
required
string (StockDispatchDocumentType)
Value: "delivery_note"

One artefact today, the rendered delivery note.

status
required
string (DocumentStatus)
Enum: "draft" "pdf_pending" "pdf_ready" "pdf_failed"

Lifecycle state. New documents start at pdf_pending; the async PDF worker promotes them to pdf_ready (or pdf_failed on permanent error). draft is reserved for future use.

required
Reference (object) or null

Reference to the rendered PDF; null until the worker attaches it.

template_version
required
string
created_at
required
integer
updated_at
required
integer
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

required
ReferenceValue (object) or null

Request samples

Content type
application/json
{
  • "file": {
    },
  • "status": "pdf_pending"
}

Response samples

Content type
application/json
{
  • "_id": "sddoc_a1b2c3d4e5f6789012345",
  • "_class": "stock_dispatch_document",
  • "stock_dispatch_id": "sdp_a1b2c3d4e5f6",
  • "document_type": "delivery_note",
  • "status": "draft",
  • "file": {
    },
  • "template_version": "1.0.0",
  • "created_at": 0,
  • "updated_at": 0,
  • "tenant": {
    },
  • "created_by": {
    }
}

Delete a document and soft-delete its PDF

Authorizations:
BearerAuth
path Parameters
stock_dispatch_id
required
string
Example: sdp_a1b2c3d4e5f6
stock_dispatch_document_id
required
string
Example: sddoc_a1b2c3d4e5f6789012345

Responses

Response samples

Content type
application/json
{
  • "error": "not_found",
  • "message": "Business partner not found",
  • "details": { }
}

List posted stock documents — receipts, stocktakes and dispatches — newest first

One timeline across the three applied stock documents. Capped at 200 rows; truncated says when the filters should narrow. counts gives the rows per kind after the location, date and search filters so a kind filter chip can show its count.

Authorizations:
BearerAuth
query Parameters
kind
string (StockDocumentKind)
Enum: "stock_receipt" "stock_count" "stock_dispatch"
stock_location_id
string
from
string <date>

ISO date, inclusive, on effective_at.

to
string <date>

ISO date, inclusive, on effective_at.

q
string

Matches the dispatch number, the receipt reference, the recipient and the customer document number.

Responses

Response Schema: application/json
required
Array of objects (StockDocumentRow)
required
object (StockDocumentCounts)

Rows per kind after the location, date and search filters, before the kind filter.

truncated
required
boolean

More than 200 rows matched; narrow the filters.

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "counts": {
    },
  • "truncated": true
}

The stock overview — every product x location, low rows first, with header totals

Sorted for the overview page: negative available first, then rows at or below their reorder point by shortfall, then the rest by product and location name. Capped at 2,000 rows (stats.truncated) — narrow by location past that. committed is what accepted offers still claim per product, attributed to the level at the default location.

Authorizations:
BearerAuth
query Parameters
stock_location_id
string
low_only
boolean

Responses

Response Schema: application/json
required
Array of objects (StockSummaryRow)
required
object (StockSummaryStats)

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "stats": {
    }
}

Inventory valuation with a total

Authorizations:
BearerAuth

Responses

Response Schema: application/json
Array of objects (StockLevelResponse)
object (MonetaryAmount)

A monetary value with an ISO 4217 currency code.

currency may be null to denote the identity ("zero") element{"amount": 0, "currency": null}. The identity absorbs into arithmetic (identity + x = x, x - x = identity, identity * n = identity); any non-zero amount must carry a currency. This is the wire form returned for an invoice draft whose totals haven't been computed yet.

Precision contract

amount is emitted as a JSON number carrying the server's Decimal with full precision — the server writes the Decimal's exact textual form as a raw JSON number token, bypassing any IEEE-754 round-trip. Sub-cent values such as 0.0003 USD (audiobook-streaming royalty rates, FX conversions, regulated reference prices) come over the wire exactly.

Client-side parsing

Standard JSON parsers convert JSON numbers to IEEE-754 floats, which lose precision for arithmetic on decimals that aren't power-of-10 fractions. Pick the right tool for the job:

  • Display only — the default float is fine. Both Python's repr(0.0003) and JavaScript's (0.0003).toString() return "0.0003", so user-visible numbers render correctly.
  • Exact arithmetic (summing line items, computing tax) — parse with a Decimal / BigDecimal type. Python: json.loads(text, parse_float=Decimal). JavaScript: a BigDecimal library such as big.js or decimal.js.

Client-side encoding for sub-cent values

A client that produces a sub-cent amount with json.dumps (Python) or JSON.stringify (JS) will also lose precision before the bytes leave the process — both encoders convert Decimal/BigDecimal to float first. Two paths preserve precision on the way in:

  1. Send the amount as a quoted decimal string. Request bodies schema this as MonetaryAmountInput, whose amount is a oneOf over number and string; the server's Pydantic coerces the string to an exact Decimal. (MonetaryAmount itself is the response shape and stays type: number.)
  2. Or use a JSON encoder on the client that supports raw number tokens (same trick as our server's encoder).

For everyday cent-precision values, sending a JSON number is fine.

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "total_value": {
    }
}

Levels whose available quantity is at or below their reorder point, largest shortfall first

Authorizations:
BearerAuth
query Parameters
stock_location_id
string

Responses

Response Schema: application/json
Array of objects (StockLevelResponse)

Response samples

Content type
application/json
{
  • "items": [
    ]
}

The tenant-wide movement ledger, newest first

Authorizations:
BearerAuth
query Parameters
limit
integer [ 1 .. 200 ]
Default: 50
next_token
string

Base64-encoded pagination cursor from previous response

Responses

Response Schema: application/json
required
Array of objects (StockMovementResponse)
next_token
required
string or null

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_token": "string"
}

Invoices

List the attempts to collect this invoice from the buyer's phone

Authorizations:
BearerAuth
path Parameters
invoice_id
required
string

Responses

Response Schema: application/json
required
Array of objects (PaymentCollection)
total
required
integer
page
required
integer
page_size
required
integer

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "total": 0,
  • "page": 0,
  • "page_size": 0
}

Ask this invoice's buyer to pay it from their phone

Starts a collection on one of the tenant's rails and returns whatever the buyer needs in order to pay. The money settles into the tenant's own merchant account — Raccoon never holds it — and is booked against this invoice as an ordinary payment when the rail confirms.

Rejected with 422 when the rail is switched off, not offered in the tenant's country, or has no credentials yet.

Authorizations:
BearerAuth
path Parameters
invoice_id
required
string
Request Body schema: application/json
required
rail_id
required
string
object (MonetaryAmount)

Optional — defaults to the invoice's open balance. Must be in the invoice's own currency, which makes a mismatch structurally unreachable rather than a 422 the buyer meets holding their phone.

phone_number
string <= 20 characters

Where to push the request, for a rail that asks a phone rather than showing a code — required for such a rail, ignored by the rest. A Kenyan number is accepted in any form a tenant types it (0712…, +254 712…, 254712…) and normalised before it reaches the provider.

dedupe_key
string [ 1 .. 64 ] characters

Optional. A second write with the same key within the tenant does not create a second row. The collection already started is returned unchanged; the rail is asked once.

stub_behaviour
string
Enum: "succeed" "pending" "fail"

Drives the stub adapter through a state a real rail would take minutes to reach. Ignored once a rail has a real adapter.

Responses

Response Schema: application/json
_id
required
string
_class
required
string
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

rail_id
required
string
required
object (MonetaryAmount)

A monetary value with an ISO 4217 currency code.

currency may be null to denote the identity ("zero") element{"amount": 0, "currency": null}. The identity absorbs into arithmetic (identity + x = x, x - x = identity, identity * n = identity); any non-zero amount must carry a currency. This is the wire form returned for an invoice draft whose totals haven't been computed yet.

Precision contract

amount is emitted as a JSON number carrying the server's Decimal with full precision — the server writes the Decimal's exact textual form as a raw JSON number token, bypassing any IEEE-754 round-trip. Sub-cent values such as 0.0003 USD (audiobook-streaming royalty rates, FX conversions, regulated reference prices) come over the wire exactly.

Client-side parsing

Standard JSON parsers convert JSON numbers to IEEE-754 floats, which lose precision for arithmetic on decimals that aren't power-of-10 fractions. Pick the right tool for the job:

  • Display only — the default float is fine. Both Python's repr(0.0003) and JavaScript's (0.0003).toString() return "0.0003", so user-visible numbers render correctly.
  • Exact arithmetic (summing line items, computing tax) — parse with a Decimal / BigDecimal type. Python: json.loads(text, parse_float=Decimal). JavaScript: a BigDecimal library such as big.js or decimal.js.

Client-side encoding for sub-cent values

A client that produces a sub-cent amount with json.dumps (Python) or JSON.stringify (JS) will also lose precision before the bytes leave the process — both encoders convert Decimal/BigDecimal to float first. Two paths preserve precision on the way in:

  1. Send the amount as a quoted decimal string. Request bodies schema this as MonetaryAmountInput, whose amount is a oneOf over number and string; the server's Pydantic coerces the string to an exact Decimal. (MonetaryAmount itself is the response shape and stays type: number.)
  2. Or use a JSON encoder on the client that supports raw number tokens (same trick as our server's encoder).

For everyday cent-precision values, sending a JSON number is fine.

reference
required
string

What the buyer's payment will actually carry back, and — on a rail that cannot confirm itself — the only thing tying the money to this invoice when it lands. A rail that mints a structured reference (an ISO 11649 RF…, a Swiss QRR) reports that here rather than the invoice number it was derived from, because the invoice number never appears on the transfer.

status
required
string
Enum: "pending" "succeeded" "failed" "expired" "cancelled"

pending is the honest resting state of every rail that confirms later; a collection reaches succeeded only once money is actually booked.

required
object

What the buyer needs in order to pay — payload carries the rail's own artefact (a QR string, a link), and the rest is what the screen shows beside it.

provider_reference
required
string or null

The rail's own receipt, once it has one.

stub_behaviour
required
string or null
expires_at
required
integer or null
settled_at
required
integer or null
failure_reason
required
string or null
required
Reference (object) or null

The payment this collection booked, once it settled.

dedupe_key
required
string or null
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

created_at
required
integer

Unix epoch seconds

updated_at
required
integer

Unix epoch seconds

required
Reference (object) or null
required
object

Request samples

Content type
application/json
{
  • "rail_id": "girocode",
  • "amount": {
    },
  • "phone_number": "0712345678",
  • "dedupe_key": "string",
  • "stub_behaviour": "succeed"
}

Response samples

Content type
application/json
{
  • "_id": "pcl_9f2c1a4b7e05",
  • "_class": "string",
  • "invoice": {
    },
  • "rail_id": "mpesa_push",
  • "amount": {
    },
  • "reference": "string",
  • "status": "pending",
  • "instruction": {
    },
  • "provider_reference": "string",
  • "stub_behaviour": "string",
  • "expires_at": 0,
  • "settled_at": 0,
  • "failure_reason": "string",
  • "payment": {
    },
  • "dedupe_key": "string",
  • "tenant": {
    },
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    },
  • "_links": {
    }
}

Read one collection

Authorizations:
BearerAuth
path Parameters
invoice_id
required
string
collection_id
required
string

Responses

Response Schema: application/json
_id
required
string
_class
required
string
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

rail_id
required
string
required
object (MonetaryAmount)

A monetary value with an ISO 4217 currency code.

currency may be null to denote the identity ("zero") element{"amount": 0, "currency": null}. The identity absorbs into arithmetic (identity + x = x, x - x = identity, identity * n = identity); any non-zero amount must carry a currency. This is the wire form returned for an invoice draft whose totals haven't been computed yet.

Precision contract

amount is emitted as a JSON number carrying the server's Decimal with full precision — the server writes the Decimal's exact textual form as a raw JSON number token, bypassing any IEEE-754 round-trip. Sub-cent values such as 0.0003 USD (audiobook-streaming royalty rates, FX conversions, regulated reference prices) come over the wire exactly.

Client-side parsing

Standard JSON parsers convert JSON numbers to IEEE-754 floats, which lose precision for arithmetic on decimals that aren't power-of-10 fractions. Pick the right tool for the job:

  • Display only — the default float is fine. Both Python's repr(0.0003) and JavaScript's (0.0003).toString() return "0.0003", so user-visible numbers render correctly.
  • Exact arithmetic (summing line items, computing tax) — parse with a Decimal / BigDecimal type. Python: json.loads(text, parse_float=Decimal). JavaScript: a BigDecimal library such as big.js or decimal.js.

Client-side encoding for sub-cent values

A client that produces a sub-cent amount with json.dumps (Python) or JSON.stringify (JS) will also lose precision before the bytes leave the process — both encoders convert Decimal/BigDecimal to float first. Two paths preserve precision on the way in:

  1. Send the amount as a quoted decimal string. Request bodies schema this as MonetaryAmountInput, whose amount is a oneOf over number and string; the server's Pydantic coerces the string to an exact Decimal. (MonetaryAmount itself is the response shape and stays type: number.)
  2. Or use a JSON encoder on the client that supports raw number tokens (same trick as our server's encoder).

For everyday cent-precision values, sending a JSON number is fine.

reference
required
string

What the buyer's payment will actually carry back, and — on a rail that cannot confirm itself — the only thing tying the money to this invoice when it lands. A rail that mints a structured reference (an ISO 11649 RF…, a Swiss QRR) reports that here rather than the invoice number it was derived from, because the invoice number never appears on the transfer.

status
required
string
Enum: "pending" "succeeded" "failed" "expired" "cancelled"

pending is the honest resting state of every rail that confirms later; a collection reaches succeeded only once money is actually booked.

required
object

What the buyer needs in order to pay — payload carries the rail's own artefact (a QR string, a link), and the rest is what the screen shows beside it.

provider_reference
required
string or null

The rail's own receipt, once it has one.

stub_behaviour
required
string or null
expires_at
required
integer or null
settled_at
required
integer or null
failure_reason
required
string or null
required
Reference (object) or null

The payment this collection booked, once it settled.

dedupe_key
required
string or null
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

created_at
required
integer

Unix epoch seconds

updated_at
required
integer

Unix epoch seconds

required
Reference (object) or null
required
object

Response samples

Content type
application/json
{
  • "_id": "pcl_9f2c1a4b7e05",
  • "_class": "string",
  • "invoice": {
    },
  • "rail_id": "mpesa_push",
  • "amount": {
    },
  • "reference": "string",
  • "status": "pending",
  • "instruction": {
    },
  • "provider_reference": "string",
  • "stub_behaviour": "string",
  • "expires_at": 0,
  • "settled_at": 0,
  • "failure_reason": "string",
  • "payment": {
    },
  • "dedupe_key": "string",
  • "tenant": {
    },
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    },
  • "_links": {
    }
}

Give up on a collection

The buyer walked away, or chose another rail. A settled collection cannot be cancelled — the money is already booked.

Authorizations:
BearerAuth
path Parameters
invoice_id
required
string
collection_id
required
string

Responses

Response Schema: application/json
_id
required
string
_class
required
string
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

rail_id
required
string
required
object (MonetaryAmount)

A monetary value with an ISO 4217 currency code.

currency may be null to denote the identity ("zero") element{"amount": 0, "currency": null}. The identity absorbs into arithmetic (identity + x = x, x - x = identity, identity * n = identity); any non-zero amount must carry a currency. This is the wire form returned for an invoice draft whose totals haven't been computed yet.

Precision contract

amount is emitted as a JSON number carrying the server's Decimal with full precision — the server writes the Decimal's exact textual form as a raw JSON number token, bypassing any IEEE-754 round-trip. Sub-cent values such as 0.0003 USD (audiobook-streaming royalty rates, FX conversions, regulated reference prices) come over the wire exactly.

Client-side parsing

Standard JSON parsers convert JSON numbers to IEEE-754 floats, which lose precision for arithmetic on decimals that aren't power-of-10 fractions. Pick the right tool for the job:

  • Display only — the default float is fine. Both Python's repr(0.0003) and JavaScript's (0.0003).toString() return "0.0003", so user-visible numbers render correctly.
  • Exact arithmetic (summing line items, computing tax) — parse with a Decimal / BigDecimal type. Python: json.loads(text, parse_float=Decimal). JavaScript: a BigDecimal library such as big.js or decimal.js.

Client-side encoding for sub-cent values

A client that produces a sub-cent amount with json.dumps (Python) or JSON.stringify (JS) will also lose precision before the bytes leave the process — both encoders convert Decimal/BigDecimal to float first. Two paths preserve precision on the way in:

  1. Send the amount as a quoted decimal string. Request bodies schema this as MonetaryAmountInput, whose amount is a oneOf over number and string; the server's Pydantic coerces the string to an exact Decimal. (MonetaryAmount itself is the response shape and stays type: number.)
  2. Or use a JSON encoder on the client that supports raw number tokens (same trick as our server's encoder).

For everyday cent-precision values, sending a JSON number is fine.

reference
required
string

What the buyer's payment will actually carry back, and — on a rail that cannot confirm itself — the only thing tying the money to this invoice when it lands. A rail that mints a structured reference (an ISO 11649 RF…, a Swiss QRR) reports that here rather than the invoice number it was derived from, because the invoice number never appears on the transfer.

status
required
string
Enum: "pending" "succeeded" "failed" "expired" "cancelled"

pending is the honest resting state of every rail that confirms later; a collection reaches succeeded only once money is actually booked.

required
object

What the buyer needs in order to pay — payload carries the rail's own artefact (a QR string, a link), and the rest is what the screen shows beside it.

provider_reference
required
string or null

The rail's own receipt, once it has one.

stub_behaviour
required
string or null
expires_at
required
integer or null
settled_at
required
integer or null
failure_reason
required
string or null
required
Reference (object) or null

The payment this collection booked, once it settled.

dedupe_key
required
string or null
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

created_at
required
integer

Unix epoch seconds

updated_at
required
integer

Unix epoch seconds

required
Reference (object) or null
required
object

Response samples

Content type
application/json
{
  • "_id": "pcl_9f2c1a4b7e05",
  • "_class": "string",
  • "invoice": {
    },
  • "rail_id": "mpesa_push",
  • "amount": {
    },
  • "reference": "string",
  • "status": "pending",
  • "instruction": {
    },
  • "provider_reference": "string",
  • "stub_behaviour": "string",
  • "expires_at": 0,
  • "settled_at": 0,
  • "failure_reason": "string",
  • "payment": {
    },
  • "dedupe_key": "string",
  • "tenant": {
    },
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    },
  • "_links": {
    }
}

Report that a collection was paid (service-to-service)

Books the money through the ordinary allocation path, so the invoice gets a payment exactly as if a human had recorded it.

Gated to service identities and superusers only — deliberately stricter than its sibling routes, because a forged settlement is money rather than provenance. Repeated delivery is expected on every rail researched and books once.

Authorizations:
BearerAuth
path Parameters
invoice_id
required
string
collection_id
required
string
Request Body schema: application/json
required
provider_reference
required
string

The rail's own receipt (an M-Pesa code, a Pix txid).

object (MonetaryAmount)

Optional — defaults to what the collection asked for.

Responses

Response Schema: application/json
_id
required
string
_class
required
string
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

rail_id
required
string
required
object (MonetaryAmount)

A monetary value with an ISO 4217 currency code.

currency may be null to denote the identity ("zero") element{"amount": 0, "currency": null}. The identity absorbs into arithmetic (identity + x = x, x - x = identity, identity * n = identity); any non-zero amount must carry a currency. This is the wire form returned for an invoice draft whose totals haven't been computed yet.

Precision contract

amount is emitted as a JSON number carrying the server's Decimal with full precision — the server writes the Decimal's exact textual form as a raw JSON number token, bypassing any IEEE-754 round-trip. Sub-cent values such as 0.0003 USD (audiobook-streaming royalty rates, FX conversions, regulated reference prices) come over the wire exactly.

Client-side parsing

Standard JSON parsers convert JSON numbers to IEEE-754 floats, which lose precision for arithmetic on decimals that aren't power-of-10 fractions. Pick the right tool for the job:

  • Display only — the default float is fine. Both Python's repr(0.0003) and JavaScript's (0.0003).toString() return "0.0003", so user-visible numbers render correctly.
  • Exact arithmetic (summing line items, computing tax) — parse with a Decimal / BigDecimal type. Python: json.loads(text, parse_float=Decimal). JavaScript: a BigDecimal library such as big.js or decimal.js.

Client-side encoding for sub-cent values

A client that produces a sub-cent amount with json.dumps (Python) or JSON.stringify (JS) will also lose precision before the bytes leave the process — both encoders convert Decimal/BigDecimal to float first. Two paths preserve precision on the way in:

  1. Send the amount as a quoted decimal string. Request bodies schema this as MonetaryAmountInput, whose amount is a oneOf over number and string; the server's Pydantic coerces the string to an exact Decimal. (MonetaryAmount itself is the response shape and stays type: number.)
  2. Or use a JSON encoder on the client that supports raw number tokens (same trick as our server's encoder).

For everyday cent-precision values, sending a JSON number is fine.

reference
required
string

What the buyer's payment will actually carry back, and — on a rail that cannot confirm itself — the only thing tying the money to this invoice when it lands. A rail that mints a structured reference (an ISO 11649 RF…, a Swiss QRR) reports that here rather than the invoice number it was derived from, because the invoice number never appears on the transfer.

status
required
string
Enum: "pending" "succeeded" "failed" "expired" "cancelled"

pending is the honest resting state of every rail that confirms later; a collection reaches succeeded only once money is actually booked.

required
object

What the buyer needs in order to pay — payload carries the rail's own artefact (a QR string, a link), and the rest is what the screen shows beside it.

provider_reference
required
string or null

The rail's own receipt, once it has one.

stub_behaviour
required
string or null
expires_at
required
integer or null
settled_at
required
integer or null
failure_reason
required
string or null
required
Reference (object) or null

The payment this collection booked, once it settled.

dedupe_key
required
string or null
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

created_at
required
integer

Unix epoch seconds

updated_at
required
integer

Unix epoch seconds

required
Reference (object) or null
required
object

Request samples

Content type
application/json
{
  • "provider_reference": "string",
  • "amount": {
    }
}

Response samples

Content type
application/json
{
  • "_id": "pcl_9f2c1a4b7e05",
  • "_class": "string",
  • "invoice": {
    },
  • "rail_id": "mpesa_push",
  • "amount": {
    },
  • "reference": "string",
  • "status": "pending",
  • "instruction": {
    },
  • "provider_reference": "string",
  • "stub_behaviour": "string",
  • "expires_at": 0,
  • "settled_at": 0,
  • "failure_reason": "string",
  • "payment": {
    },
  • "dedupe_key": "string",
  • "tenant": {
    },
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    },
  • "_links": {
    }
}

Report what the buyer must do to pay (service-to-service)

Completes the instruction on a collection whose rail had to ask a provider for it. A rail whose adapter runs in process fills this in before the start request returns and is refused here; a remote one leaves the collection at state: requesting until its worker reports back through this route.

Send failure_reason instead of a payload when the provider refused the request, so the collection closes rather than waiting for a code that is never coming. One of the two is required: a body carrying neither is a 422, because it would leave the collection waiting with an empty artefact and no reason given.

Gated to service identities and superusers only, for the same reason as its sibling: this writes what a buyer is shown, so a forged instruction is a code pointing at somebody else's account.

Authorizations:
BearerAuth
path Parameters
invoice_id
required
string
collection_id
required
string
Request Body schema: application/json
required
payload
string <= 4096 characters

The rail's own artefact — for PayPal, the approval link the QR encodes.

object

What the screen shows beside the code, rendered as given.

expires_in_seconds
integer or null [ 1 .. 86400 ]

How long the artefact is good for, when the provider says.

failure_reason
string or null <= 500 characters

Sent instead of a payload when the provider refused. Closes the collection rather than leaving it waiting for a code.

Responses

Response Schema: application/json
_id
required
string
_class
required
string
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

rail_id
required
string
required
object (MonetaryAmount)

A monetary value with an ISO 4217 currency code.

currency may be null to denote the identity ("zero") element{"amount": 0, "currency": null}. The identity absorbs into arithmetic (identity + x = x, x - x = identity, identity * n = identity); any non-zero amount must carry a currency. This is the wire form returned for an invoice draft whose totals haven't been computed yet.

Precision contract

amount is emitted as a JSON number carrying the server's Decimal with full precision — the server writes the Decimal's exact textual form as a raw JSON number token, bypassing any IEEE-754 round-trip. Sub-cent values such as 0.0003 USD (audiobook-streaming royalty rates, FX conversions, regulated reference prices) come over the wire exactly.

Client-side parsing

Standard JSON parsers convert JSON numbers to IEEE-754 floats, which lose precision for arithmetic on decimals that aren't power-of-10 fractions. Pick the right tool for the job:

  • Display only — the default float is fine. Both Python's repr(0.0003) and JavaScript's (0.0003).toString() return "0.0003", so user-visible numbers render correctly.
  • Exact arithmetic (summing line items, computing tax) — parse with a Decimal / BigDecimal type. Python: json.loads(text, parse_float=Decimal). JavaScript: a BigDecimal library such as big.js or decimal.js.

Client-side encoding for sub-cent values

A client that produces a sub-cent amount with json.dumps (Python) or JSON.stringify (JS) will also lose precision before the bytes leave the process — both encoders convert Decimal/BigDecimal to float first. Two paths preserve precision on the way in:

  1. Send the amount as a quoted decimal string. Request bodies schema this as MonetaryAmountInput, whose amount is a oneOf over number and string; the server's Pydantic coerces the string to an exact Decimal. (MonetaryAmount itself is the response shape and stays type: number.)
  2. Or use a JSON encoder on the client that supports raw number tokens (same trick as our server's encoder).

For everyday cent-precision values, sending a JSON number is fine.

reference
required
string

What the buyer's payment will actually carry back, and — on a rail that cannot confirm itself — the only thing tying the money to this invoice when it lands. A rail that mints a structured reference (an ISO 11649 RF…, a Swiss QRR) reports that here rather than the invoice number it was derived from, because the invoice number never appears on the transfer.

status
required
string
Enum: "pending" "succeeded" "failed" "expired" "cancelled"

pending is the honest resting state of every rail that confirms later; a collection reaches succeeded only once money is actually booked.

required
object

What the buyer needs in order to pay — payload carries the rail's own artefact (a QR string, a link), and the rest is what the screen shows beside it.

provider_reference
required
string or null

The rail's own receipt, once it has one.

stub_behaviour
required
string or null
expires_at
required
integer or null
settled_at
required
integer or null
failure_reason
required
string or null
required
Reference (object) or null

The payment this collection booked, once it settled.

dedupe_key
required
string or null
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

created_at
required
integer

Unix epoch seconds

updated_at
required
integer

Unix epoch seconds

required
Reference (object) or null
required
object

Request samples

Content type
application/json
{
  • "payload": "string",
  • "display": {
    },
  • "expires_in_seconds": 1,
  • "failure_reason": "string"
}

Response samples

Content type
application/json
{
  • "_id": "pcl_9f2c1a4b7e05",
  • "_class": "string",
  • "invoice": {
    },
  • "rail_id": "mpesa_push",
  • "amount": {
    },
  • "reference": "string",
  • "status": "pending",
  • "instruction": {
    },
  • "provider_reference": "string",
  • "stub_behaviour": "string",
  • "expires_at": 0,
  • "settled_at": 0,
  • "failure_reason": "string",
  • "payment": {
    },
  • "dedupe_key": "string",
  • "tenant": {
    },
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    },
  • "_links": {
    }
}

List invoices (newest first)

Authorizations:
BearerAuth
query Parameters
limit
integer [ 1 .. 200 ]
Default: 50
next_token
string

Base64-encoded pagination cursor from previous response

status
string
Enum: "draft" "finalized" "paid" "overdue" "cancelled"
payment_status
string
Enum: "unpaid" "partially_paid" "paid"

Filter on the settlement axis. Independent of status: an invoice can be overdue and partially_paid at the same time. An unrecognised value is rejected with 422 rather than matching nothing.

business_partner_id
string
type_id
string
offer_id
string

Only the invoices raised from this offer. One offer can produce several invoices, so this is how they are listed — the offer keeps no list of its own.

q
string non-empty

Full-text search term. When provided, the endpoint delegates to the shared search index (matches across name/title/email/identifier fields, tenant-scoped) and returns the matching entities in the same response shape as the unfiltered list. Pagination (limit, next_token) is not honoured in this mode; results are capped at the search index size limit (20 items in MVP).

Responses

Response Schema: application/json
required
Array of objects (InvoiceResponse)
next_token
required
string or null

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_token": "string"
}

Create a new invoice (starts as draft)

Authorizations:
BearerAuth
Request Body schema: application/json
required
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

ReferenceValue (object) or null

Issuing Company (EN 16931 BG-7 Seller). Auto-resolved from the tenant's invoice-company config when omitted on create. Per the no-bare-id rule (architecture.md#Embedded References) the wire shape is the full Reference object.

Reference (object) or null

Optional tenant-defined sales channel selected for this invoice. Snapshotted on the invoice.

issue_date
string or null <date>
due_date
string or null <date>
tax_point_date
string or null <date>

EN 16931 BT-7 — value-added-tax point date.

actual_delivery_date
string or null <date>

EN 16931 BT-72 — actual delivery date.

document_type_code
string or null
Enum: "380" "381" "384" "386" "326" null

EN 16931 BT-3 (UNCL 1001). 380=commercial invoice, 381=credit note, 384=corrected invoice, 386=prepayment invoice, 326=partial invoice.

currency
string or null = 3 characters
buyer_reference
string or null

EN 16931 BT-10. Often the buyer's PO number or Leitweg-ID.

purchase_order_reference
string or null

EN 16931 BT-13.

contract_reference
string or null

EN 16931 BT-12.

project_reference
string or null

EN 16931 BT-11.

subject
string or null

What the invoice is about, printed as its subject line.

InvoicePeriod (object) or null
InvoiceBillingReference (object) or null
buyer_name
string or null

EN 16931 BT-44 — buyer name. Defaults to the partner's company/full name.

buyer_legal_registration_id
string or null

EN 16931 BT-47.

buyer_identifier
string or null

EN 16931 BT-46 — party identifier (GLN, DUNS, etc.).

buyer_customer_number
string or null

The buyer's customer number with the seller (DIN 5008 Kundennummer). Defaults to the business partner's.

buyer_vat_id
string or null

EN 16931 BT-48.

buyer_vat_id_type
string or null

EN 16931 VAT scheme of buyer_vat_id — the identifier registry schema_key (e.g. vat_eu, vat_gb). Defaulted from the partner's VAT-class identifier when omitted.

InvoicePartyContact (object) or null
InvoiceElectronicAddress (object) or null
InvoicePayee (object) or null
payment_means_code
string or null

EN 16931 BT-81 (UNCL 4461). Defaults from tenant config.

payment_terms_text
string or null

EN 16931 BT-20.

InvoicePaymentTerms (object) or null

Structured skonto / late-payment block. Additive to BT-20 payment_terms_text — both render when supplied.

remittance_information
string or null

EN 16931 BT-83 — payment reference / Verwendungszweck.

Array of objects (InvoiceLineItemCreate)
notes
Array of strings
Default: []

EN 16931 BT-22 — invoice notes (0..n). Each entry is rendered as its own paragraph below the totals. The author handles translation; the renderer prints what is supplied.

vat_aggregation
string
Enum: "horizontal" "vertical"

How per-line VAT rolls up into the invoice totals. vertical computes VAT once per rate band on the aggregated net (the B2B norm); horizontal sums the VAT shown on each line. The two can differ by a rounding cent. Optional: omitted, the invoice takes the default_vat_aggregation of its invoice type. Frozen with the rest of the content once the invoice leaves draft.

type_id
string [ 1 .. 100 ] characters

The invoice-class tenant type this invoice is created under. Every invoice has one. It decides the letterhead, the numbering and the payment defaults. Omitted, the invoice is created under the tenant's default invoice type; that is a 422 when the tenant has several and marks none of them default.

schema_version
string or null
object
Default: {}
object

Optional. When omitted, the server snapshots the partner's preferred addresses for each slot declared by the invoice type. When supplied, the slot keys must be a subset of the invoice type's address_slots.

Responses

Response Schema: application/json
_id
required
string
_class
required
string
_name
required
string (EntityName)

Read-only, backend-composed display label (the invoice number, or id while still a draft).

required
object

self is the invoice's own resource URL; documents and line_items are its sub-collections. Further rels may be present; a client follows the ones it knows.

invoice_number
required
string

Empty ("") while the invoice is in draft. Stamped by the per-tenant InvoiceNumberingSchema on the draft → finalized transition (resolution order: invoice type → company → tenant-default schema). Once assigned, the value is immutable. Cancellation from draft does NOT consume a number — the row keeps the empty string and relies on status="cancelled" as the audit signal.

required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

status
required
string
Enum: "draft" "finalized" "paid" "overdue" "cancelled"
issue_date
required
string <date>
due_date
required
string <date>
document_type_code
required
string
Enum: "380" "381" "384" "386" "326"
currency
required
string
required
Reference (object) or null

The offer this invoice was raised from, null if none. One offer can produce several invoices — a deposit and a final, or one per milestone — so the link lives here and never as a list on the offer.

payment_means_code
required
string
required
Array of objects (InvoiceLineItemResponse)
required
object (MonetaryAmount)

EN 16931 BT-109 — sum of line nets, as a MonetaryAmount carrying the invoice's currency (BT-5). A draft whose totals haven't been computed yet returns the identity element ({"amount": 0, "currency": null}).

required
object (MonetaryAmount)

EN 16931 BT-110.

required
object (MonetaryAmount)

EN 16931 BT-112. BT-115 (payable_amount) equals this until prepaid / rounding ships.

payment_status
required
string
Enum: "unpaid" "partially_paid" "paid"

How much of the invoice has been settled — a second axis, derived from the recorded allocations and never accepted on input. It is deliberately separate from status: an invoice can be overdue and partially_paid at once, and folding the two together would give status a second writer alongside the dunning worker. Record a payment to change it; PATCH {status: "paid"} is rejected with 422.

required
object (MonetaryAmount)

Cash received against this invoice — the sum of its non-voided allocations. Backend-computed. Always carries the invoice currency, even at zero.

required
object (MonetaryAmount)

The part of the invoice its payments deliberately did not cover — withholding tax the customer remitted on the seller's behalf, an early-payment discount, bank charges. Backend-computed from the allocations' typed deductions.

required
object (MonetaryAmount)

tax_inclusiveamount_paidamount_written_off: what the invoice still owes. Computed on the wire, never stored. Withheld tax counts as settled because it is money the seller is no longer owed, even though it never reached the bank.

allocation_version
required
integer

Optimistic-lock counter for the allocation transaction. Read-only; exposed so a client can tell two allocation states apart.

clearance_status
required
string
Enum: "not_required" "pending" "cleared" "rejected"

Whether the tax authority has cleared this invoice — the third axis, beside status (lifecycle) and payment_status (settlement).

In a clearance market (Kenya, Nigeria, India, Brazil) the document is not a valid invoice until the authority mints its artefact, so pending and rejected both block sending. not_required is the default and covers every tenant not in such a market; whether a seller is in scope derives from the seller country, never from a field a user sets.

Deliberately not a sixth status value: that would give status a second unlocked writer racing the dunning worker.

clearance_scheme
required
string or null
Enum: "ke_kra_etims" "ng_firs_mbs" "in_gstn_irp" "br_sefaz_nfe" null

Which fiscal-clearance regime engaged, as a code — <country>_<authority>_<regime> in lower snake case.

Null whenever clearance_status is not_required, which is every invoice outside a clearance market. Set alongside pending, cleared and rejected: a rejected invoice still names the regime that refused it, because that is what makes the error actionable.

Read-only, and written only by a clearance adapter through POST /resource/v1/invoices/{id}/clearance. Never derived from the seller's country — clearance is a property of the clearing act, and a seller trading into two clearance markets would resolve to the wrong authority on an immutable document.

clearance_reference
required
string or null

The identity the authority minted — Nigeria's IRN, Kenya's Control Unit Invoice Number, India's IRN. Null until cleared.

clearance_stamp
required
string or null

Cryptographic signature where the authority issues one (Nigeria's CSID). Null where the regime issues none, and until cleared.

clearance_qr
required
string or null

QR payload the human-readable document must carry. Null until cleared.

cleared_at
required
integer or null

Unix seconds at which clearance was recorded. Null until cleared.

clearance_error
required
string or null

Why the authority refused, when clearance_status is rejected. Null otherwise.

required
Array of objects (InvoiceVatBreakdownLine)

Per-VAT-rate breakdown (UStG §14). Server-computed by recompute_totals(); read-only on the API. The renderer prefers this persisted list over recomputation.

vat_aggregation
required
string
Enum: "horizontal" "vertical"

How per-line VAT rolls up into the invoice totals. vertical computes VAT once per rate band on the aggregated net (the B2B norm); horizontal sums the VAT shown on each line. The two can differ by a rounding cent. Chosen by the caller on create or on a draft update; omitted, it is the default_vat_aggregation of the invoice type. Frozen once the invoice leaves draft.

language
required
string

The language the invoice is written in. Snapshotted from the business partner when the draft is created, refreshed on every draft save and frozen once the invoice leaves draft; falls back to the tenant's default language when the partner has none. Always present. Read-only: it is not accepted on input.

notes
required
Array of strings

EN 16931 BT-22 invoice notes (0..n).

type_id
required
string

The tenant type this invoice was created under.

required
object
required
object

Snapshotted address per slot the invoice type declared. Slot keys are a subset of the type's address_slots (invoice | shipping | service). Empty object when no slot is configured for the invoice type or the partner has no eligible address.

created_at
required
integer
updated_at
required
integer
required
object (ReferenceValue)

Stored value for a field of type "reference"

required
object (ReferenceValue)

Stored value for a field of type "reference"

required
Array of objects (IdentifierResponse)

The numbers this record carries (ISBN, GTIN, VAT ID, ...). Stored on the record itself; the identifiers sub-resource addresses one entry.

ReferenceValue (object) or null

Issuing Company (BG-7 Seller). Snapshotted onto the immutable seller_* columns at draft → finalized. Null until the auto-default resolves (Phase D seed guarantees a value for new tenants).

Reference (object) or null

Sales channel selected for this invoice, null if none.

dunning_level
integer

Dunning escalation level (0 = none, 1..N = reminder ladder step).

InvoicePaymentTerms (object) or null

Structured BT-20 sub-block. null when unset.

Request samples

Content type
application/json
{
  • "business_partner": {
    },
  • "company": {
    },
  • "channel": {
    },
  • "issue_date": "2026-05-01",
  • "due_date": "2026-05-31",
  • "tax_point_date": "2019-08-24",
  • "actual_delivery_date": "2019-08-24",
  • "document_type_code": "380",
  • "currency": "EUR",
  • "buyer_reference": "string",
  • "purchase_order_reference": "string",
  • "contract_reference": "string",
  • "project_reference": "string",
  • "subject": "string",
  • "invoicing_period": {
    },
  • "preceding_invoice": {
    },
  • "buyer_name": "string",
  • "buyer_legal_registration_id": "string",
  • "buyer_identifier": "string",
  • "buyer_customer_number": "string",
  • "buyer_vat_id": "string",
  • "buyer_vat_id_type": "string",
  • "buyer_contact": {
    },
  • "buyer_electronic_address": {
    },
  • "payee": {
    },
  • "payment_means_code": "string",
  • "payment_terms_text": "string",
  • "payment_terms": {
    },
  • "remittance_information": "string",
  • "line_items": [
    ],
  • "notes": [ ],
  • "vat_aggregation": "horizontal",
  • "type_id": "standard_invoice",
  • "schema_version": "string",
  • "data": { },
  • "addresses": {
    }
}

Response samples

Content type
application/json
{
  • "identifiers": [
    ],
  • "_id": "inv_a1b2c3d4e5f6",
  • "_class": "invoice",
  • "_name": "Acme Corp",
  • "_links": {
    },
  • "invoice_number": "2026-R-0042",
  • "business_partner": {
    },
  • "company": {
    },
  • "channel": {
    },
  • "offer": {
    },
  • "status": "draft",
  • "issue_date": "2026-05-01",
  • "due_date": "2026-05-31",
  • "dunning_level": 0,
  • "document_type_code": "380",
  • "currency": "EUR",
  • "payment_means_code": "30",
  • "line_items": [
    ],
  • "tax_exclusive": {
    },
  • "tax_total": {
    },
  • "tax_inclusive": {
    },
  • "payment_status": "unpaid",
  • "amount_paid": {
    },
  • "amount_written_off": {
    },
  • "amount_due": {
    },
  • "allocation_version": 0,
  • "clearance_status": "not_required",
  • "language": "de",
  • "clearance_scheme": "ke_kra_etims",
  • "clearance_reference": "IRN-2026-000123",
  • "clearance_stamp": "string",
  • "clearance_qr": "string",
  • "cleared_at": 0,
  • "clearance_error": "string",
  • "vat_breakdown": [
    ],
  • "vat_aggregation": "horizontal",
  • "notes": [
    ],
  • "payment_terms": {
    },
  • "type_id": "string",
  • "data": { },
  • "addresses": {
    },
  • "created_at": 1746144000,
  • "updated_at": 1746144000,
  • "created_by": {
    },
  • "tenant": {
    }
}

Get invoice by ID

Authorizations:
BearerAuth
path Parameters
invoice_id
required
string
Example: inv_a1b2c3d4e5f6

Responses

Response Schema: application/json
_id
required
string
_class
required
string
_name
required
string (EntityName)

Read-only, backend-composed display label (the invoice number, or id while still a draft).

required
object

self is the invoice's own resource URL; documents and line_items are its sub-collections. Further rels may be present; a client follows the ones it knows.

invoice_number
required
string

Empty ("") while the invoice is in draft. Stamped by the per-tenant InvoiceNumberingSchema on the draft → finalized transition (resolution order: invoice type → company → tenant-default schema). Once assigned, the value is immutable. Cancellation from draft does NOT consume a number — the row keeps the empty string and relies on status="cancelled" as the audit signal.

required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

status
required
string
Enum: "draft" "finalized" "paid" "overdue" "cancelled"
issue_date
required
string <date>
due_date
required
string <date>
document_type_code
required
string
Enum: "380" "381" "384" "386" "326"
currency
required
string
required
Reference (object) or null

The offer this invoice was raised from, null if none. One offer can produce several invoices — a deposit and a final, or one per milestone — so the link lives here and never as a list on the offer.

payment_means_code
required
string
required
Array of objects (InvoiceLineItemResponse)
required
object (MonetaryAmount)

EN 16931 BT-109 — sum of line nets, as a MonetaryAmount carrying the invoice's currency (BT-5). A draft whose totals haven't been computed yet returns the identity element ({"amount": 0, "currency": null}).

required
object (MonetaryAmount)

EN 16931 BT-110.

required
object (MonetaryAmount)

EN 16931 BT-112. BT-115 (payable_amount) equals this until prepaid / rounding ships.

payment_status
required
string
Enum: "unpaid" "partially_paid" "paid"

How much of the invoice has been settled — a second axis, derived from the recorded allocations and never accepted on input. It is deliberately separate from status: an invoice can be overdue and partially_paid at once, and folding the two together would give status a second writer alongside the dunning worker. Record a payment to change it; PATCH {status: "paid"} is rejected with 422.

required
object (MonetaryAmount)

Cash received against this invoice — the sum of its non-voided allocations. Backend-computed. Always carries the invoice currency, even at zero.

required
object (MonetaryAmount)

The part of the invoice its payments deliberately did not cover — withholding tax the customer remitted on the seller's behalf, an early-payment discount, bank charges. Backend-computed from the allocations' typed deductions.

required
object (MonetaryAmount)

tax_inclusiveamount_paidamount_written_off: what the invoice still owes. Computed on the wire, never stored. Withheld tax counts as settled because it is money the seller is no longer owed, even though it never reached the bank.

allocation_version
required
integer

Optimistic-lock counter for the allocation transaction. Read-only; exposed so a client can tell two allocation states apart.

clearance_status
required
string
Enum: "not_required" "pending" "cleared" "rejected"

Whether the tax authority has cleared this invoice — the third axis, beside status (lifecycle) and payment_status (settlement).

In a clearance market (Kenya, Nigeria, India, Brazil) the document is not a valid invoice until the authority mints its artefact, so pending and rejected both block sending. not_required is the default and covers every tenant not in such a market; whether a seller is in scope derives from the seller country, never from a field a user sets.

Deliberately not a sixth status value: that would give status a second unlocked writer racing the dunning worker.

clearance_scheme
required
string or null
Enum: "ke_kra_etims" "ng_firs_mbs" "in_gstn_irp" "br_sefaz_nfe" null

Which fiscal-clearance regime engaged, as a code — <country>_<authority>_<regime> in lower snake case.

Null whenever clearance_status is not_required, which is every invoice outside a clearance market. Set alongside pending, cleared and rejected: a rejected invoice still names the regime that refused it, because that is what makes the error actionable.

Read-only, and written only by a clearance adapter through POST /resource/v1/invoices/{id}/clearance. Never derived from the seller's country — clearance is a property of the clearing act, and a seller trading into two clearance markets would resolve to the wrong authority on an immutable document.

clearance_reference
required
string or null

The identity the authority minted — Nigeria's IRN, Kenya's Control Unit Invoice Number, India's IRN. Null until cleared.

clearance_stamp
required
string or null

Cryptographic signature where the authority issues one (Nigeria's CSID). Null where the regime issues none, and until cleared.

clearance_qr
required
string or null

QR payload the human-readable document must carry. Null until cleared.

cleared_at
required
integer or null

Unix seconds at which clearance was recorded. Null until cleared.

clearance_error
required
string or null

Why the authority refused, when clearance_status is rejected. Null otherwise.

required
Array of objects (InvoiceVatBreakdownLine)

Per-VAT-rate breakdown (UStG §14). Server-computed by recompute_totals(); read-only on the API. The renderer prefers this persisted list over recomputation.

vat_aggregation
required
string
Enum: "horizontal" "vertical"

How per-line VAT rolls up into the invoice totals. vertical computes VAT once per rate band on the aggregated net (the B2B norm); horizontal sums the VAT shown on each line. The two can differ by a rounding cent. Chosen by the caller on create or on a draft update; omitted, it is the default_vat_aggregation of the invoice type. Frozen once the invoice leaves draft.

language
required
string

The language the invoice is written in. Snapshotted from the business partner when the draft is created, refreshed on every draft save and frozen once the invoice leaves draft; falls back to the tenant's default language when the partner has none. Always present. Read-only: it is not accepted on input.

notes
required
Array of strings

EN 16931 BT-22 invoice notes (0..n).

type_id
required
string

The tenant type this invoice was created under.

required
object
required
object

Snapshotted address per slot the invoice type declared. Slot keys are a subset of the type's address_slots (invoice | shipping | service). Empty object when no slot is configured for the invoice type or the partner has no eligible address.

created_at
required
integer
updated_at
required
integer
required
object (ReferenceValue)

Stored value for a field of type "reference"

required
object (ReferenceValue)

Stored value for a field of type "reference"

required
Array of objects (IdentifierResponse)

The numbers this record carries (ISBN, GTIN, VAT ID, ...). Stored on the record itself; the identifiers sub-resource addresses one entry.

ReferenceValue (object) or null

Issuing Company (BG-7 Seller). Snapshotted onto the immutable seller_* columns at draft → finalized. Null until the auto-default resolves (Phase D seed guarantees a value for new tenants).

Reference (object) or null

Sales channel selected for this invoice, null if none.

dunning_level
integer

Dunning escalation level (0 = none, 1..N = reminder ladder step).

InvoicePaymentTerms (object) or null

Structured BT-20 sub-block. null when unset.

Response samples

Content type
application/json
{
  • "identifiers": [
    ],
  • "_id": "inv_a1b2c3d4e5f6",
  • "_class": "invoice",
  • "_name": "Acme Corp",
  • "_links": {
    },
  • "invoice_number": "2026-R-0042",
  • "business_partner": {
    },
  • "company": {
    },
  • "channel": {
    },
  • "offer": {
    },
  • "status": "draft",
  • "issue_date": "2026-05-01",
  • "due_date": "2026-05-31",
  • "dunning_level": 0,
  • "document_type_code": "380",
  • "currency": "EUR",
  • "payment_means_code": "30",
  • "line_items": [
    ],
  • "tax_exclusive": {
    },
  • "tax_total": {
    },
  • "tax_inclusive": {
    },
  • "payment_status": "unpaid",
  • "amount_paid": {
    },
  • "amount_written_off": {
    },
  • "amount_due": {
    },
  • "allocation_version": 0,
  • "clearance_status": "not_required",
  • "language": "de",
  • "clearance_scheme": "ke_kra_etims",
  • "clearance_reference": "IRN-2026-000123",
  • "clearance_stamp": "string",
  • "clearance_qr": "string",
  • "cleared_at": 0,
  • "clearance_error": "string",
  • "vat_breakdown": [
    ],
  • "vat_aggregation": "horizontal",
  • "notes": [
    ],
  • "payment_terms": {
    },
  • "type_id": "string",
  • "data": { },
  • "addresses": {
    },
  • "created_at": 1746144000,
  • "updated_at": 1746144000,
  • "created_by": {
    },
  • "tenant": {
    }
}

Update invoice fields and/or status

{"status": "finalized"} issues the invoice: it refreshes the seller snapshot, re-rates VAT at the time of supply, stamps the number from the tenant's numbering schema in the same atomic write, and hands the PDF off to the document generator. A finalize carries no other field changes — save the edits first — and only a draft can be finalized.

paid is refused: an invoice becomes paid by recording a payment against it.

Authorizations:
BearerAuth
path Parameters
invoice_id
required
string
Example: inv_a1b2c3d4e5f6
Request Body schema: application/json
required
Reference (object) or null
ReferenceValue (object) or null

Re-pick the issuing Company. Validated against the tenant's per-doc-type allowed list. Only editable while the invoice is in draft. Per the no-bare-id rule the wire shape is the full Reference object.

Reference (object) or null

Re-pick the sales channel, or null to clear. Only editable while the invoice is in draft.

issue_date
string <date>
due_date
string <date>
dunning_level
integer >= 0

Dunning escalation level (0 = none, 1..N = reminder ladder step).

tax_point_date
string <date>
actual_delivery_date
string <date>
document_type_code
string
Enum: "380" "381" "384" "386" "326"
currency
string = 3 characters
buyer_reference
string
purchase_order_reference
string
contract_reference
string
project_reference
string
subject
string or null
object (InvoicePeriod)

Invoice-level invoicing period (EN 16931 BG-14).

object (InvoiceBillingReference)

Preceding-invoice reference (EN 16931 BG-3 / BT-25 / BT-26).

buyer_name
string
buyer_legal_registration_id
string
buyer_identifier
string
buyer_customer_number
string or null
buyer_vat_id
string
buyer_vat_id_type
string

EN 16931 VAT scheme of buyer_vat_id — the identifier registry schema_key (e.g. vat_eu, vat_gb).

object (InvoicePartyContact)

Contact group (EN 16931 BG-6 / BG-9).

object (InvoiceElectronicAddress)

Electronic address (EN 16931 BT-34 / BT-49). scheme_id follows the EAS code list: EM = email, 9930 = German VAT, 0088 = GLN, etc.

object (InvoicePayee)

Third-party payee (EN 16931 BG-10). Populated only when the party receiving payment differs from the seller (e.g. factoring).

payment_means_code
string
payment_terms_text
string
InvoicePaymentTerms (object) or null
remittance_information
string
Array of objects (InvoiceLineItemCreate)
notes
Array of strings or null

Replaces the persisted list. null / omitted = no change; [] clears the field.

vat_aggregation
string
Enum: "horizontal" "vertical"

How per-line VAT rolls up into the invoice totals. vertical computes VAT once per rate band on the aggregated net (the B2B norm); horizontal sums the VAT shown on each line. The two can differ by a rounding cent. Optional: omitted, the invoice takes the default_vat_aggregation of its invoice type. Frozen with the rest of the content once the invoice leaves draft.

status
string
Enum: "draft" "finalized" "paid" "overdue" "cancelled"
type_id
string or null
schema_version
string or null
object
object

Responses

Response Schema: application/json
_id
required
string
_class
required
string
_name
required
string (EntityName)

Read-only, backend-composed display label (the invoice number, or id while still a draft).

required
object

self is the invoice's own resource URL; documents and line_items are its sub-collections. Further rels may be present; a client follows the ones it knows.

invoice_number
required
string

Empty ("") while the invoice is in draft. Stamped by the per-tenant InvoiceNumberingSchema on the draft → finalized transition (resolution order: invoice type → company → tenant-default schema). Once assigned, the value is immutable. Cancellation from draft does NOT consume a number — the row keeps the empty string and relies on status="cancelled" as the audit signal.

required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

status
required
string
Enum: "draft" "finalized" "paid" "overdue" "cancelled"
issue_date
required
string <date>
due_date
required
string <date>
document_type_code
required
string
Enum: "380" "381" "384" "386" "326"
currency
required
string
required
Reference (object) or null

The offer this invoice was raised from, null if none. One offer can produce several invoices — a deposit and a final, or one per milestone — so the link lives here and never as a list on the offer.

payment_means_code
required
string
required
Array of objects (InvoiceLineItemResponse)
required
object (MonetaryAmount)

EN 16931 BT-109 — sum of line nets, as a MonetaryAmount carrying the invoice's currency (BT-5). A draft whose totals haven't been computed yet returns the identity element ({"amount": 0, "currency": null}).

required
object (MonetaryAmount)

EN 16931 BT-110.

required
object (MonetaryAmount)

EN 16931 BT-112. BT-115 (payable_amount) equals this until prepaid / rounding ships.

payment_status
required
string
Enum: "unpaid" "partially_paid" "paid"

How much of the invoice has been settled — a second axis, derived from the recorded allocations and never accepted on input. It is deliberately separate from status: an invoice can be overdue and partially_paid at once, and folding the two together would give status a second writer alongside the dunning worker. Record a payment to change it; PATCH {status: "paid"} is rejected with 422.

required
object (MonetaryAmount)

Cash received against this invoice — the sum of its non-voided allocations. Backend-computed. Always carries the invoice currency, even at zero.

required
object (MonetaryAmount)

The part of the invoice its payments deliberately did not cover — withholding tax the customer remitted on the seller's behalf, an early-payment discount, bank charges. Backend-computed from the allocations' typed deductions.

required
object (MonetaryAmount)

tax_inclusiveamount_paidamount_written_off: what the invoice still owes. Computed on the wire, never stored. Withheld tax counts as settled because it is money the seller is no longer owed, even though it never reached the bank.

allocation_version
required
integer

Optimistic-lock counter for the allocation transaction. Read-only; exposed so a client can tell two allocation states apart.

clearance_status
required
string
Enum: "not_required" "pending" "cleared" "rejected"

Whether the tax authority has cleared this invoice — the third axis, beside status (lifecycle) and payment_status (settlement).

In a clearance market (Kenya, Nigeria, India, Brazil) the document is not a valid invoice until the authority mints its artefact, so pending and rejected both block sending. not_required is the default and covers every tenant not in such a market; whether a seller is in scope derives from the seller country, never from a field a user sets.

Deliberately not a sixth status value: that would give status a second unlocked writer racing the dunning worker.

clearance_scheme
required
string or null
Enum: "ke_kra_etims" "ng_firs_mbs" "in_gstn_irp" "br_sefaz_nfe" null

Which fiscal-clearance regime engaged, as a code — <country>_<authority>_<regime> in lower snake case.

Null whenever clearance_status is not_required, which is every invoice outside a clearance market. Set alongside pending, cleared and rejected: a rejected invoice still names the regime that refused it, because that is what makes the error actionable.

Read-only, and written only by a clearance adapter through POST /resource/v1/invoices/{id}/clearance. Never derived from the seller's country — clearance is a property of the clearing act, and a seller trading into two clearance markets would resolve to the wrong authority on an immutable document.

clearance_reference
required
string or null

The identity the authority minted — Nigeria's IRN, Kenya's Control Unit Invoice Number, India's IRN. Null until cleared.

clearance_stamp
required
string or null

Cryptographic signature where the authority issues one (Nigeria's CSID). Null where the regime issues none, and until cleared.

clearance_qr
required
string or null

QR payload the human-readable document must carry. Null until cleared.

cleared_at
required
integer or null

Unix seconds at which clearance was recorded. Null until cleared.

clearance_error
required
string or null

Why the authority refused, when clearance_status is rejected. Null otherwise.

required
Array of objects (InvoiceVatBreakdownLine)

Per-VAT-rate breakdown (UStG §14). Server-computed by recompute_totals(); read-only on the API. The renderer prefers this persisted list over recomputation.

vat_aggregation
required
string
Enum: "horizontal" "vertical"

How per-line VAT rolls up into the invoice totals. vertical computes VAT once per rate band on the aggregated net (the B2B norm); horizontal sums the VAT shown on each line. The two can differ by a rounding cent. Chosen by the caller on create or on a draft update; omitted, it is the default_vat_aggregation of the invoice type. Frozen once the invoice leaves draft.

language
required
string

The language the invoice is written in. Snapshotted from the business partner when the draft is created, refreshed on every draft save and frozen once the invoice leaves draft; falls back to the tenant's default language when the partner has none. Always present. Read-only: it is not accepted on input.

notes
required
Array of strings

EN 16931 BT-22 invoice notes (0..n).

type_id
required
string

The tenant type this invoice was created under.

required
object
required
object

Snapshotted address per slot the invoice type declared. Slot keys are a subset of the type's address_slots (invoice | shipping | service). Empty object when no slot is configured for the invoice type or the partner has no eligible address.

created_at
required
integer
updated_at
required
integer
required
object (ReferenceValue)

Stored value for a field of type "reference"

required
object (ReferenceValue)

Stored value for a field of type "reference"

required
Array of objects (IdentifierResponse)

The numbers this record carries (ISBN, GTIN, VAT ID, ...). Stored on the record itself; the identifiers sub-resource addresses one entry.

ReferenceValue (object) or null

Issuing Company (BG-7 Seller). Snapshotted onto the immutable seller_* columns at draft → finalized. Null until the auto-default resolves (Phase D seed guarantees a value for new tenants).

Reference (object) or null

Sales channel selected for this invoice, null if none.

dunning_level
integer

Dunning escalation level (0 = none, 1..N = reminder ladder step).

InvoicePaymentTerms (object) or null

Structured BT-20 sub-block. null when unset.

Request samples

Content type
application/json
{
  • "business_partner": {
    },
  • "company": {
    },
  • "channel": {
    },
  • "issue_date": "2019-08-24",
  • "due_date": "2019-08-24",
  • "dunning_level": 0,
  • "tax_point_date": "2019-08-24",
  • "actual_delivery_date": "2019-08-24",
  • "document_type_code": "380",
  • "currency": "str",
  • "buyer_reference": "string",
  • "purchase_order_reference": "string",
  • "contract_reference": "string",
  • "project_reference": "string",
  • "subject": "string",
  • "invoicing_period": {
    },
  • "preceding_invoice": {
    },
  • "buyer_name": "string",
  • "buyer_legal_registration_id": "string",
  • "buyer_identifier": "string",
  • "buyer_customer_number": "string",
  • "buyer_vat_id": "string",
  • "buyer_vat_id_type": "string",
  • "buyer_contact": {
    },
  • "buyer_electronic_address": {
    },
  • "payee": {
    },
  • "payment_means_code": "string",
  • "payment_terms_text": "string",
  • "payment_terms": {
    },
  • "remittance_information": "string",
  • "line_items": [
    ],
  • "notes": [
    ],
  • "vat_aggregation": "horizontal",
  • "status": "draft",
  • "type_id": "string",
  • "schema_version": "string",
  • "data": { },
  • "addresses": {
    }
}

Response samples

Content type
application/json
{
  • "identifiers": [
    ],
  • "_id": "inv_a1b2c3d4e5f6",
  • "_class": "invoice",
  • "_name": "Acme Corp",
  • "_links": {
    },
  • "invoice_number": "2026-R-0042",
  • "business_partner": {
    },
  • "company": {
    },
  • "channel": {
    },
  • "offer": {
    },
  • "status": "draft",
  • "issue_date": "2026-05-01",
  • "due_date": "2026-05-31",
  • "dunning_level": 0,
  • "document_type_code": "380",
  • "currency": "EUR",
  • "payment_means_code": "30",
  • "line_items": [
    ],
  • "tax_exclusive": {
    },
  • "tax_total": {
    },
  • "tax_inclusive": {
    },
  • "payment_status": "unpaid",
  • "amount_paid": {
    },
  • "amount_written_off": {
    },
  • "amount_due": {
    },
  • "allocation_version": 0,
  • "clearance_status": "not_required",
  • "language": "de",
  • "clearance_scheme": "ke_kra_etims",
  • "clearance_reference": "IRN-2026-000123",
  • "clearance_stamp": "string",
  • "clearance_qr": "string",
  • "cleared_at": 0,
  • "clearance_error": "string",
  • "vat_breakdown": [
    ],
  • "vat_aggregation": "horizontal",
  • "notes": [
    ],
  • "payment_terms": {
    },
  • "type_id": "string",
  • "data": { },
  • "addresses": {
    },
  • "created_at": 1746144000,
  • "updated_at": 1746144000,
  • "created_by": {
    },
  • "tenant": {
    }
}

Partially update invoice fields and/or status

{"status": "finalized"} issues the invoice: it refreshes the seller snapshot, re-rates VAT at the time of supply, stamps the number from the tenant's numbering schema in the same atomic write, and hands the PDF off to the document generator. A finalize carries no other field changes — save the edits first — and only a draft can be finalized.

paid is refused: an invoice becomes paid by recording a payment against it.

Authorizations:
BearerAuth
path Parameters
invoice_id
required
string
Example: inv_a1b2c3d4e5f6
Request Body schema: application/json
required
Reference (object) or null
ReferenceValue (object) or null

Re-pick the issuing Company. Validated against the tenant's per-doc-type allowed list. Only editable while the invoice is in draft. Per the no-bare-id rule the wire shape is the full Reference object.

Reference (object) or null

Re-pick the sales channel, or null to clear. Only editable while the invoice is in draft.

issue_date
string <date>
due_date
string <date>
dunning_level
integer >= 0

Dunning escalation level (0 = none, 1..N = reminder ladder step).

tax_point_date
string <date>
actual_delivery_date
string <date>
document_type_code
string
Enum: "380" "381" "384" "386" "326"
currency
string = 3 characters
buyer_reference
string
purchase_order_reference
string
contract_reference
string
project_reference
string
subject
string or null
object (InvoicePeriod)

Invoice-level invoicing period (EN 16931 BG-14).

object (InvoiceBillingReference)

Preceding-invoice reference (EN 16931 BG-3 / BT-25 / BT-26).

buyer_name
string
buyer_legal_registration_id
string
buyer_identifier
string
buyer_customer_number
string or null
buyer_vat_id
string
buyer_vat_id_type
string

EN 16931 VAT scheme of buyer_vat_id — the identifier registry schema_key (e.g. vat_eu, vat_gb).

object (InvoicePartyContact)

Contact group (EN 16931 BG-6 / BG-9).

object (InvoiceElectronicAddress)

Electronic address (EN 16931 BT-34 / BT-49). scheme_id follows the EAS code list: EM = email, 9930 = German VAT, 0088 = GLN, etc.

object (InvoicePayee)

Third-party payee (EN 16931 BG-10). Populated only when the party receiving payment differs from the seller (e.g. factoring).

payment_means_code
string
payment_terms_text
string
InvoicePaymentTerms (object) or null
remittance_information
string
Array of objects (InvoiceLineItemCreate)
notes
Array of strings or null

Replaces the persisted list. null / omitted = no change; [] clears the field.

vat_aggregation
string
Enum: "horizontal" "vertical"

How per-line VAT rolls up into the invoice totals. vertical computes VAT once per rate band on the aggregated net (the B2B norm); horizontal sums the VAT shown on each line. The two can differ by a rounding cent. Optional: omitted, the invoice takes the default_vat_aggregation of its invoice type. Frozen with the rest of the content once the invoice leaves draft.

status
string
Enum: "draft" "finalized" "paid" "overdue" "cancelled"
type_id
string or null
schema_version
string or null
object
object

Responses

Response Schema: application/json
_id
required
string
_class
required
string
_name
required
string (EntityName)

Read-only, backend-composed display label (the invoice number, or id while still a draft).

required
object

self is the invoice's own resource URL; documents and line_items are its sub-collections. Further rels may be present; a client follows the ones it knows.

invoice_number
required
string

Empty ("") while the invoice is in draft. Stamped by the per-tenant InvoiceNumberingSchema on the draft → finalized transition (resolution order: invoice type → company → tenant-default schema). Once assigned, the value is immutable. Cancellation from draft does NOT consume a number — the row keeps the empty string and relies on status="cancelled" as the audit signal.

required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

status
required
string
Enum: "draft" "finalized" "paid" "overdue" "cancelled"
issue_date
required
string <date>
due_date
required
string <date>
document_type_code
required
string
Enum: "380" "381" "384" "386" "326"
currency
required
string
required
Reference (object) or null

The offer this invoice was raised from, null if none. One offer can produce several invoices — a deposit and a final, or one per milestone — so the link lives here and never as a list on the offer.

payment_means_code
required
string
required
Array of objects (InvoiceLineItemResponse)
required
object (MonetaryAmount)

EN 16931 BT-109 — sum of line nets, as a MonetaryAmount carrying the invoice's currency (BT-5). A draft whose totals haven't been computed yet returns the identity element ({"amount": 0, "currency": null}).

required
object (MonetaryAmount)

EN 16931 BT-110.

required
object (MonetaryAmount)

EN 16931 BT-112. BT-115 (payable_amount) equals this until prepaid / rounding ships.

payment_status
required
string
Enum: "unpaid" "partially_paid" "paid"

How much of the invoice has been settled — a second axis, derived from the recorded allocations and never accepted on input. It is deliberately separate from status: an invoice can be overdue and partially_paid at once, and folding the two together would give status a second writer alongside the dunning worker. Record a payment to change it; PATCH {status: "paid"} is rejected with 422.

required
object (MonetaryAmount)

Cash received against this invoice — the sum of its non-voided allocations. Backend-computed. Always carries the invoice currency, even at zero.

required
object (MonetaryAmount)

The part of the invoice its payments deliberately did not cover — withholding tax the customer remitted on the seller's behalf, an early-payment discount, bank charges. Backend-computed from the allocations' typed deductions.

required
object (MonetaryAmount)

tax_inclusiveamount_paidamount_written_off: what the invoice still owes. Computed on the wire, never stored. Withheld tax counts as settled because it is money the seller is no longer owed, even though it never reached the bank.

allocation_version
required
integer

Optimistic-lock counter for the allocation transaction. Read-only; exposed so a client can tell two allocation states apart.

clearance_status
required
string
Enum: "not_required" "pending" "cleared" "rejected"

Whether the tax authority has cleared this invoice — the third axis, beside status (lifecycle) and payment_status (settlement).

In a clearance market (Kenya, Nigeria, India, Brazil) the document is not a valid invoice until the authority mints its artefact, so pending and rejected both block sending. not_required is the default and covers every tenant not in such a market; whether a seller is in scope derives from the seller country, never from a field a user sets.

Deliberately not a sixth status value: that would give status a second unlocked writer racing the dunning worker.

clearance_scheme
required
string or null
Enum: "ke_kra_etims" "ng_firs_mbs" "in_gstn_irp" "br_sefaz_nfe" null

Which fiscal-clearance regime engaged, as a code — <country>_<authority>_<regime> in lower snake case.

Null whenever clearance_status is not_required, which is every invoice outside a clearance market. Set alongside pending, cleared and rejected: a rejected invoice still names the regime that refused it, because that is what makes the error actionable.

Read-only, and written only by a clearance adapter through POST /resource/v1/invoices/{id}/clearance. Never derived from the seller's country — clearance is a property of the clearing act, and a seller trading into two clearance markets would resolve to the wrong authority on an immutable document.

clearance_reference
required
string or null

The identity the authority minted — Nigeria's IRN, Kenya's Control Unit Invoice Number, India's IRN. Null until cleared.

clearance_stamp
required
string or null

Cryptographic signature where the authority issues one (Nigeria's CSID). Null where the regime issues none, and until cleared.

clearance_qr
required
string or null

QR payload the human-readable document must carry. Null until cleared.

cleared_at
required
integer or null

Unix seconds at which clearance was recorded. Null until cleared.

clearance_error
required
string or null

Why the authority refused, when clearance_status is rejected. Null otherwise.

required
Array of objects (InvoiceVatBreakdownLine)

Per-VAT-rate breakdown (UStG §14). Server-computed by recompute_totals(); read-only on the API. The renderer prefers this persisted list over recomputation.

vat_aggregation
required
string
Enum: "horizontal" "vertical"

How per-line VAT rolls up into the invoice totals. vertical computes VAT once per rate band on the aggregated net (the B2B norm); horizontal sums the VAT shown on each line. The two can differ by a rounding cent. Chosen by the caller on create or on a draft update; omitted, it is the default_vat_aggregation of the invoice type. Frozen once the invoice leaves draft.

language
required
string

The language the invoice is written in. Snapshotted from the business partner when the draft is created, refreshed on every draft save and frozen once the invoice leaves draft; falls back to the tenant's default language when the partner has none. Always present. Read-only: it is not accepted on input.

notes
required
Array of strings

EN 16931 BT-22 invoice notes (0..n).

type_id
required
string

The tenant type this invoice was created under.

required
object
required
object

Snapshotted address per slot the invoice type declared. Slot keys are a subset of the type's address_slots (invoice | shipping | service). Empty object when no slot is configured for the invoice type or the partner has no eligible address.

created_at
required
integer
updated_at
required
integer
required
object (ReferenceValue)

Stored value for a field of type "reference"

required
object (ReferenceValue)

Stored value for a field of type "reference"

required
Array of objects (IdentifierResponse)

The numbers this record carries (ISBN, GTIN, VAT ID, ...). Stored on the record itself; the identifiers sub-resource addresses one entry.

ReferenceValue (object) or null

Issuing Company (BG-7 Seller). Snapshotted onto the immutable seller_* columns at draft → finalized. Null until the auto-default resolves (Phase D seed guarantees a value for new tenants).

Reference (object) or null

Sales channel selected for this invoice, null if none.

dunning_level
integer

Dunning escalation level (0 = none, 1..N = reminder ladder step).

InvoicePaymentTerms (object) or null

Structured BT-20 sub-block. null when unset.

Request samples

Content type
application/json
{
  • "business_partner": {
    },
  • "company": {
    },
  • "channel": {
    },
  • "issue_date": "2019-08-24",
  • "due_date": "2019-08-24",
  • "dunning_level": 0,
  • "tax_point_date": "2019-08-24",
  • "actual_delivery_date": "2019-08-24",
  • "document_type_code": "380",
  • "currency": "str",
  • "buyer_reference": "string",
  • "purchase_order_reference": "string",
  • "contract_reference": "string",
  • "project_reference": "string",
  • "subject": "string",
  • "invoicing_period": {
    },
  • "preceding_invoice": {
    },
  • "buyer_name": "string",
  • "buyer_legal_registration_id": "string",
  • "buyer_identifier": "string",
  • "buyer_customer_number": "string",
  • "buyer_vat_id": "string",
  • "buyer_vat_id_type": "string",
  • "buyer_contact": {
    },
  • "buyer_electronic_address": {
    },
  • "payee": {
    },
  • "payment_means_code": "string",
  • "payment_terms_text": "string",
  • "payment_terms": {
    },
  • "remittance_information": "string",
  • "line_items": [
    ],
  • "notes": [
    ],
  • "vat_aggregation": "horizontal",
  • "status": "draft",
  • "type_id": "string",
  • "schema_version": "string",
  • "data": { },
  • "addresses": {
    }
}

Response samples

Content type
application/json
{
  • "identifiers": [
    ],
  • "_id": "inv_a1b2c3d4e5f6",
  • "_class": "invoice",
  • "_name": "Acme Corp",
  • "_links": {
    },
  • "invoice_number": "2026-R-0042",
  • "business_partner": {
    },
  • "company": {
    },
  • "channel": {
    },
  • "offer": {
    },
  • "status": "draft",
  • "issue_date": "2026-05-01",
  • "due_date": "2026-05-31",
  • "dunning_level": 0,
  • "document_type_code": "380",
  • "currency": "EUR",
  • "payment_means_code": "30",
  • "line_items": [
    ],
  • "tax_exclusive": {
    },
  • "tax_total": {
    },
  • "tax_inclusive": {
    },
  • "payment_status": "unpaid",
  • "amount_paid": {
    },
  • "amount_written_off": {
    },
  • "amount_due": {
    },
  • "allocation_version": 0,
  • "clearance_status": "not_required",
  • "language": "de",
  • "clearance_scheme": "ke_kra_etims",
  • "clearance_reference": "IRN-2026-000123",
  • "clearance_stamp": "string",
  • "clearance_qr": "string",
  • "cleared_at": 0,
  • "clearance_error": "string",
  • "vat_breakdown": [
    ],
  • "vat_aggregation": "horizontal",
  • "notes": [
    ],
  • "payment_terms": {
    },
  • "type_id": "string",
  • "data": { },
  • "addresses": {
    },
  • "created_at": 1746144000,
  • "updated_at": 1746144000,
  • "created_by": {
    },
  • "tenant": {
    }
}

Cancel an invoice (soft-delete; status → cancelled)

A finalized invoice carrying live payment allocations returns 409. Void the allocations first, then cancel it.

Authorizations:
BearerAuth
path Parameters
invoice_id
required
string
Example: inv_a1b2c3d4e5f6

Responses

Response samples

Content type
application/json
{
  • "error": "not_found",
  • "message": "Business partner not found",
  • "details": { }
}

Record a tax authority's clearance outcome for an invoice

The callback half of clearance. In Kenya, Nigeria, India and Brazil the tax authority must mint an artefact — an IRN, control number, cryptographic stamp or QR — before the document is a valid invoice at all, and Nigeria's turnaround is measured in hours. So the call to the authority never happens in a request: finalize hands off, a per-country worker service does the talking, and the answer arrives here, exactly as the VAT-ID validator reports an identifier validation.

Called by a worker holding a service identity, not by a user.

status is never touched by this endpoint. Clearance lives on its own axis so a clearance worker and the dunning worker are not two writers on one field.

Cleared is terminal. A repeated cleared returns the unchanged invoice; a rejected after a cleared is a 409. At-least-once delivery must not let message ordering decide whether an invoice is valid.

Authorizations:
BearerAuth
path Parameters
invoice_id
required
string
Example: inv_a1b2c3d4e5f6
Request Body schema: application/json
required
status
required
string
Enum: "cleared" "rejected"
scheme
string or null
Enum: "ke_kra_etims" "ng_firs_mbs" "in_gstn_irp" "br_sefaz_nfe" null

Which regime is reporting, as a code — never an authority name and never a display label.

Optional on the wire so an adapter written against the original callback contract keeps working, and immutable once recorded: a result naming a different regime than the one already on the invoice is refused with 409 rather than applied, because which regime cleared a document is a fact about the document and two regimes claiming one invoice is a routing bug upstream.

reference
string or null
stamp
string or null
qr
string or null
error
string or null

Why the authority refused. Expected whenever status is rejected.

Responses

Response Schema: application/json
_id
required
string
_class
required
string
_name
required
string (EntityName)

Read-only, backend-composed display label (the invoice number, or id while still a draft).

required
object

self is the invoice's own resource URL; documents and line_items are its sub-collections. Further rels may be present; a client follows the ones it knows.

invoice_number
required
string

Empty ("") while the invoice is in draft. Stamped by the per-tenant InvoiceNumberingSchema on the draft → finalized transition (resolution order: invoice type → company → tenant-default schema). Once assigned, the value is immutable. Cancellation from draft does NOT consume a number — the row keeps the empty string and relies on status="cancelled" as the audit signal.

required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

status
required
string
Enum: "draft" "finalized" "paid" "overdue" "cancelled"
issue_date
required
string <date>
due_date
required
string <date>
document_type_code
required
string
Enum: "380" "381" "384" "386" "326"
currency
required
string
required
Reference (object) or null

The offer this invoice was raised from, null if none. One offer can produce several invoices — a deposit and a final, or one per milestone — so the link lives here and never as a list on the offer.

payment_means_code
required
string
required
Array of objects (InvoiceLineItemResponse)
required
object (MonetaryAmount)

EN 16931 BT-109 — sum of line nets, as a MonetaryAmount carrying the invoice's currency (BT-5). A draft whose totals haven't been computed yet returns the identity element ({"amount": 0, "currency": null}).

required
object (MonetaryAmount)

EN 16931 BT-110.

required
object (MonetaryAmount)

EN 16931 BT-112. BT-115 (payable_amount) equals this until prepaid / rounding ships.

payment_status
required
string
Enum: "unpaid" "partially_paid" "paid"

How much of the invoice has been settled — a second axis, derived from the recorded allocations and never accepted on input. It is deliberately separate from status: an invoice can be overdue and partially_paid at once, and folding the two together would give status a second writer alongside the dunning worker. Record a payment to change it; PATCH {status: "paid"} is rejected with 422.

required
object (MonetaryAmount)

Cash received against this invoice — the sum of its non-voided allocations. Backend-computed. Always carries the invoice currency, even at zero.

required
object (MonetaryAmount)

The part of the invoice its payments deliberately did not cover — withholding tax the customer remitted on the seller's behalf, an early-payment discount, bank charges. Backend-computed from the allocations' typed deductions.

required
object (MonetaryAmount)

tax_inclusiveamount_paidamount_written_off: what the invoice still owes. Computed on the wire, never stored. Withheld tax counts as settled because it is money the seller is no longer owed, even though it never reached the bank.

allocation_version
required
integer

Optimistic-lock counter for the allocation transaction. Read-only; exposed so a client can tell two allocation states apart.

clearance_status
required
string
Enum: "not_required" "pending" "cleared" "rejected"

Whether the tax authority has cleared this invoice — the third axis, beside status (lifecycle) and payment_status (settlement).

In a clearance market (Kenya, Nigeria, India, Brazil) the document is not a valid invoice until the authority mints its artefact, so pending and rejected both block sending. not_required is the default and covers every tenant not in such a market; whether a seller is in scope derives from the seller country, never from a field a user sets.

Deliberately not a sixth status value: that would give status a second unlocked writer racing the dunning worker.

clearance_scheme
required
string or null
Enum: "ke_kra_etims" "ng_firs_mbs" "in_gstn_irp" "br_sefaz_nfe" null

Which fiscal-clearance regime engaged, as a code — <country>_<authority>_<regime> in lower snake case.

Null whenever clearance_status is not_required, which is every invoice outside a clearance market. Set alongside pending, cleared and rejected: a rejected invoice still names the regime that refused it, because that is what makes the error actionable.

Read-only, and written only by a clearance adapter through POST /resource/v1/invoices/{id}/clearance. Never derived from the seller's country — clearance is a property of the clearing act, and a seller trading into two clearance markets would resolve to the wrong authority on an immutable document.

clearance_reference
required
string or null

The identity the authority minted — Nigeria's IRN, Kenya's Control Unit Invoice Number, India's IRN. Null until cleared.

clearance_stamp
required
string or null

Cryptographic signature where the authority issues one (Nigeria's CSID). Null where the regime issues none, and until cleared.

clearance_qr
required
string or null

QR payload the human-readable document must carry. Null until cleared.

cleared_at
required
integer or null

Unix seconds at which clearance was recorded. Null until cleared.

clearance_error
required
string or null

Why the authority refused, when clearance_status is rejected. Null otherwise.

required
Array of objects (InvoiceVatBreakdownLine)

Per-VAT-rate breakdown (UStG §14). Server-computed by recompute_totals(); read-only on the API. The renderer prefers this persisted list over recomputation.

vat_aggregation
required
string
Enum: "horizontal" "vertical"

How per-line VAT rolls up into the invoice totals. vertical computes VAT once per rate band on the aggregated net (the B2B norm); horizontal sums the VAT shown on each line. The two can differ by a rounding cent. Chosen by the caller on create or on a draft update; omitted, it is the default_vat_aggregation of the invoice type. Frozen once the invoice leaves draft.

language
required
string

The language the invoice is written in. Snapshotted from the business partner when the draft is created, refreshed on every draft save and frozen once the invoice leaves draft; falls back to the tenant's default language when the partner has none. Always present. Read-only: it is not accepted on input.

notes
required
Array of strings

EN 16931 BT-22 invoice notes (0..n).

type_id
required
string

The tenant type this invoice was created under.

required
object
required
object

Snapshotted address per slot the invoice type declared. Slot keys are a subset of the type's address_slots (invoice | shipping | service). Empty object when no slot is configured for the invoice type or the partner has no eligible address.

created_at
required
integer
updated_at
required
integer
required
object (ReferenceValue)

Stored value for a field of type "reference"

required
object (ReferenceValue)

Stored value for a field of type "reference"

required
Array of objects (IdentifierResponse)

The numbers this record carries (ISBN, GTIN, VAT ID, ...). Stored on the record itself; the identifiers sub-resource addresses one entry.

ReferenceValue (object) or null

Issuing Company (BG-7 Seller). Snapshotted onto the immutable seller_* columns at draft → finalized. Null until the auto-default resolves (Phase D seed guarantees a value for new tenants).

Reference (object) or null

Sales channel selected for this invoice, null if none.

dunning_level
integer

Dunning escalation level (0 = none, 1..N = reminder ladder step).

InvoicePaymentTerms (object) or null

Structured BT-20 sub-block. null when unset.

Request samples

Content type
application/json
{
  • "status": "cleared",
  • "scheme": "ke_kra_etims",
  • "reference": "IRN-2026-000123",
  • "stamp": "string",
  • "qr": "string",
  • "error": "string"
}

Response samples

Content type
application/json
{
  • "identifiers": [
    ],
  • "_id": "inv_a1b2c3d4e5f6",
  • "_class": "invoice",
  • "_name": "Acme Corp",
  • "_links": {
    },
  • "invoice_number": "2026-R-0042",
  • "business_partner": {
    },
  • "company": {
    },
  • "channel": {
    },
  • "offer": {
    },
  • "status": "draft",
  • "issue_date": "2026-05-01",
  • "due_date": "2026-05-31",
  • "dunning_level": 0,
  • "document_type_code": "380",
  • "currency": "EUR",
  • "payment_means_code": "30",
  • "line_items": [
    ],
  • "tax_exclusive": {
    },
  • "tax_total": {
    },
  • "tax_inclusive": {
    },
  • "payment_status": "unpaid",
  • "amount_paid": {
    },
  • "amount_written_off": {
    },
  • "amount_due": {
    },
  • "allocation_version": 0,
  • "clearance_status": "not_required",
  • "language": "de",
  • "clearance_scheme": "ke_kra_etims",
  • "clearance_reference": "IRN-2026-000123",
  • "clearance_stamp": "string",
  • "clearance_qr": "string",
  • "cleared_at": 0,
  • "clearance_error": "string",
  • "vat_breakdown": [
    ],
  • "vat_aggregation": "horizontal",
  • "notes": [
    ],
  • "payment_terms": {
    },
  • "type_id": "string",
  • "data": { },
  • "addresses": {
    },
  • "created_at": 1746144000,
  • "updated_at": 1746144000,
  • "created_by": {
    },
  • "tenant": {
    }
}

List the messages sent about an invoice

One row per send attempt per channel, newest first — the answer to "has this invoice gone out, where to, and when".

Authorizations:
BearerAuth
path Parameters
invoice_id
required
string
Example: inv_a1b2c3d4e5f6

Responses

Response Schema: application/json
required
Array of objects (CorrespondenceResponse)
next_token
required
string or null

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_token": "string"
}

Send an invoice to the business partner over a channel

Sending creates a correspondence row: two sends are two rows, which is why this is a creation and not a status change on the invoice.

The send itself is performed by an event-driven worker, so a 202 means "accepted for sending", not "sent" — poll GET /resource/v1/invoices/{invoice_id}/correspondence (or the row's _links.self) until status leaves queued.

The invoice must have left draft (it needs its number and frozen seller snapshot), and the business partner must have an email address — the one flagged for invoicing, else the primary one, else the only one. The invoice's own status is unchanged by a send; correspondence rows are the record of what went out.

Sending an invoice that already went out sends it again and creates a second row. The only 409 is a send still in flight: while a row on the same channel is queued, a repeat is the same send twice, not a second one.

Authorizations:
BearerAuth
path Parameters
invoice_id
required
string
Example: inv_a1b2c3d4e5f6
Request Body schema: application/json
required
channel
required
string (CorrespondenceChannel)
Value: "email"

Channel a document is sent over. email is the only value at MVP; the enum exists so adding a channel (e.g. WhatsApp) is an added member rather than a new endpoint.

acknowledge_uncleared
boolean
Default: false

Send even though the invoice has not been cleared by the tax authority. Staying compliant is the tenant's own legal responsibility, so an uncleared send is warned about (409 with invoice_not_cleared) rather than blocked outright; set this to acknowledge that responsibility and send the invoice anyway.

Responses

Request samples

Content type
application/json
{
  • "channel": "email",
  • "acknowledge_uncleared": false
}

Response samples

Content type
application/json
{
  • "_id": "cor_a1b2c3d4e5f6789012345",
  • "_class": "correspondence",
  • "document": {
    },
  • "channel": "email",
  • "purpose": "invoice",
  • "status": "queued",
  • "recipient": "billing@example.com",
  • "attachment": {
    },
  • "email_message": {
    },
  • "sent_at": 0,
  • "failure_reason": "string",
  • "message": "string",
  • "created_at": 0,
  • "updated_at": 0,
  • "tenant": {
    },
  • "_links": {
    },
  • "created_by": {
    }
}

Read one message sent about an invoice

The row's own _links.self. Poll it while status is queued to learn whether the send succeeded.

Authorizations:
BearerAuth
path Parameters
invoice_id
required
string
Example: inv_a1b2c3d4e5f6
correspondence_id
required
string
Example: cor_a1b2c3d4e5f6789012345

Responses

Response Schema: application/json
_id
required
string
_class
required
string
required
object (Reference)

The document this message is about — an invoice or an offer.

channel
required
string (CorrespondenceChannel)
Value: "email"

Channel a document is sent over. email is the only value at MVP; the enum exists so adding a channel (e.g. WhatsApp) is an added member rather than a new endpoint.

purpose
required
string (CorrespondencePurpose)
Enum: "invoice" "reminder" "offer"

What went out. invoice is the invoice itself and offer the offer itself, each carrying its own PDF; reminder is a dunning payment reminder chasing an invoice, carrying the approved message in the body and the same invoice PDF as its attachment. A document may only have one send in flight per channel; reminders repeat as the dunning ladder escalates.

status
required
string (CorrespondenceStatus)
Enum: "queued" "sent" "failed"

Lifecycle state. A send starts at queued; the correspondence worker promotes it to sent once the channel accepted the message, or to failed with a failure_reason when the channel rejected it permanently. A failed row does not block a fresh send attempt.

recipient
required
string

Resolved destination for this channel — for email, the business partner's invoicing address at the moment the send was requested.

message
required
string or null

The approved reminder text sent in the body, for a reminder row; null for an invoice or offer row, whose body is generated from the document.

required
Reference (object) or null

The PDF file that actually went out. null until the row is sent — which document is attached is decided at send time, so a regenerated PDF is the one the customer receives.

required
Reference (object) or null

The stored outbound message. Null until the worker links it, including the interval after message creation but before linking. Once linked, it remains available whether sending succeeds or fails.

sent_at
required
integer or null

Unix seconds the channel accepted the message; null until then.

failure_reason
required
string or null

Channel error code for a failed row; null otherwise.

created_at
required
integer
updated_at
required
integer
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

required
object

self is the row's own URL — the one to poll while status is queued.

required
ReferenceValue (object) or null

Response samples

Content type
application/json
{
  • "_id": "cor_a1b2c3d4e5f6789012345",
  • "_class": "correspondence",
  • "document": {
    },
  • "channel": "email",
  • "purpose": "invoice",
  • "status": "queued",
  • "recipient": "billing@example.com",
  • "attachment": {
    },
  • "email_message": {
    },
  • "sent_at": 0,
  • "failure_reason": "string",
  • "message": "string",
  • "created_at": 0,
  • "updated_at": 0,
  • "tenant": {
    },
  • "_links": {
    },
  • "created_by": {
    }
}

Refresh invoice address snapshots from the partner's preferred addresses

Replaces the invoice's address snapshots with the current preferred (primary) addresses from the linked business partner, for every slot declared on the invoice type. The frontend calls this after the user confirms a "Update addresses to {new partner}'s defaults?" prompt triggered by a business-partner change. Only allowed while the invoice is in draft.

Authorizations:
BearerAuth
path Parameters
invoice_id
required
string
Example: inv_a1b2c3d4e5f6

Responses

Response Schema: application/json
_id
required
string
_class
required
string
_name
required
string (EntityName)

Read-only, backend-composed display label (the invoice number, or id while still a draft).

required
object

self is the invoice's own resource URL; documents and line_items are its sub-collections. Further rels may be present; a client follows the ones it knows.

invoice_number
required
string

Empty ("") while the invoice is in draft. Stamped by the per-tenant InvoiceNumberingSchema on the draft → finalized transition (resolution order: invoice type → company → tenant-default schema). Once assigned, the value is immutable. Cancellation from draft does NOT consume a number — the row keeps the empty string and relies on status="cancelled" as the audit signal.

required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

status
required
string
Enum: "draft" "finalized" "paid" "overdue" "cancelled"
issue_date
required
string <date>
due_date
required
string <date>
document_type_code
required
string
Enum: "380" "381" "384" "386" "326"
currency
required
string
required
Reference (object) or null

The offer this invoice was raised from, null if none. One offer can produce several invoices — a deposit and a final, or one per milestone — so the link lives here and never as a list on the offer.

payment_means_code
required
string
required
Array of objects (InvoiceLineItemResponse)
required
object (MonetaryAmount)

EN 16931 BT-109 — sum of line nets, as a MonetaryAmount carrying the invoice's currency (BT-5). A draft whose totals haven't been computed yet returns the identity element ({"amount": 0, "currency": null}).

required
object (MonetaryAmount)

EN 16931 BT-110.

required
object (MonetaryAmount)

EN 16931 BT-112. BT-115 (payable_amount) equals this until prepaid / rounding ships.

payment_status
required
string
Enum: "unpaid" "partially_paid" "paid"

How much of the invoice has been settled — a second axis, derived from the recorded allocations and never accepted on input. It is deliberately separate from status: an invoice can be overdue and partially_paid at once, and folding the two together would give status a second writer alongside the dunning worker. Record a payment to change it; PATCH {status: "paid"} is rejected with 422.

required
object (MonetaryAmount)

Cash received against this invoice — the sum of its non-voided allocations. Backend-computed. Always carries the invoice currency, even at zero.

required
object (MonetaryAmount)

The part of the invoice its payments deliberately did not cover — withholding tax the customer remitted on the seller's behalf, an early-payment discount, bank charges. Backend-computed from the allocations' typed deductions.

required
object (MonetaryAmount)

tax_inclusiveamount_paidamount_written_off: what the invoice still owes. Computed on the wire, never stored. Withheld tax counts as settled because it is money the seller is no longer owed, even though it never reached the bank.

allocation_version
required
integer

Optimistic-lock counter for the allocation transaction. Read-only; exposed so a client can tell two allocation states apart.

clearance_status
required
string
Enum: "not_required" "pending" "cleared" "rejected"

Whether the tax authority has cleared this invoice — the third axis, beside status (lifecycle) and payment_status (settlement).

In a clearance market (Kenya, Nigeria, India, Brazil) the document is not a valid invoice until the authority mints its artefact, so pending and rejected both block sending. not_required is the default and covers every tenant not in such a market; whether a seller is in scope derives from the seller country, never from a field a user sets.

Deliberately not a sixth status value: that would give status a second unlocked writer racing the dunning worker.

clearance_scheme
required
string or null
Enum: "ke_kra_etims" "ng_firs_mbs" "in_gstn_irp" "br_sefaz_nfe" null

Which fiscal-clearance regime engaged, as a code — <country>_<authority>_<regime> in lower snake case.

Null whenever clearance_status is not_required, which is every invoice outside a clearance market. Set alongside pending, cleared and rejected: a rejected invoice still names the regime that refused it, because that is what makes the error actionable.

Read-only, and written only by a clearance adapter through POST /resource/v1/invoices/{id}/clearance. Never derived from the seller's country — clearance is a property of the clearing act, and a seller trading into two clearance markets would resolve to the wrong authority on an immutable document.

clearance_reference
required
string or null

The identity the authority minted — Nigeria's IRN, Kenya's Control Unit Invoice Number, India's IRN. Null until cleared.

clearance_stamp
required
string or null

Cryptographic signature where the authority issues one (Nigeria's CSID). Null where the regime issues none, and until cleared.

clearance_qr
required
string or null

QR payload the human-readable document must carry. Null until cleared.

cleared_at
required
integer or null

Unix seconds at which clearance was recorded. Null until cleared.

clearance_error
required
string or null

Why the authority refused, when clearance_status is rejected. Null otherwise.

required
Array of objects (InvoiceVatBreakdownLine)

Per-VAT-rate breakdown (UStG §14). Server-computed by recompute_totals(); read-only on the API. The renderer prefers this persisted list over recomputation.

vat_aggregation
required
string
Enum: "horizontal" "vertical"

How per-line VAT rolls up into the invoice totals. vertical computes VAT once per rate band on the aggregated net (the B2B norm); horizontal sums the VAT shown on each line. The two can differ by a rounding cent. Chosen by the caller on create or on a draft update; omitted, it is the default_vat_aggregation of the invoice type. Frozen once the invoice leaves draft.

language
required
string

The language the invoice is written in. Snapshotted from the business partner when the draft is created, refreshed on every draft save and frozen once the invoice leaves draft; falls back to the tenant's default language when the partner has none. Always present. Read-only: it is not accepted on input.

notes
required
Array of strings

EN 16931 BT-22 invoice notes (0..n).

type_id
required
string

The tenant type this invoice was created under.

required
object
required
object

Snapshotted address per slot the invoice type declared. Slot keys are a subset of the type's address_slots (invoice | shipping | service). Empty object when no slot is configured for the invoice type or the partner has no eligible address.

created_at
required
integer
updated_at
required
integer
required
object (ReferenceValue)

Stored value for a field of type "reference"

required
object (ReferenceValue)

Stored value for a field of type "reference"

required
Array of objects (IdentifierResponse)

The numbers this record carries (ISBN, GTIN, VAT ID, ...). Stored on the record itself; the identifiers sub-resource addresses one entry.

ReferenceValue (object) or null

Issuing Company (BG-7 Seller). Snapshotted onto the immutable seller_* columns at draft → finalized. Null until the auto-default resolves (Phase D seed guarantees a value for new tenants).

Reference (object) or null

Sales channel selected for this invoice, null if none.

dunning_level
integer

Dunning escalation level (0 = none, 1..N = reminder ladder step).

InvoicePaymentTerms (object) or null

Structured BT-20 sub-block. null when unset.

Response samples

Content type
application/json
{
  • "identifiers": [
    ],
  • "_id": "inv_a1b2c3d4e5f6",
  • "_class": "invoice",
  • "_name": "Acme Corp",
  • "_links": {
    },
  • "invoice_number": "2026-R-0042",
  • "business_partner": {
    },
  • "company": {
    },
  • "channel": {
    },
  • "offer": {
    },
  • "status": "draft",
  • "issue_date": "2026-05-01",
  • "due_date": "2026-05-31",
  • "dunning_level": 0,
  • "document_type_code": "380",
  • "currency": "EUR",
  • "payment_means_code": "30",
  • "line_items": [
    ],
  • "tax_exclusive": {
    },
  • "tax_total": {
    },
  • "tax_inclusive": {
    },
  • "payment_status": "unpaid",
  • "amount_paid": {
    },
  • "amount_written_off": {
    },
  • "amount_due": {
    },
  • "allocation_version": 0,
  • "clearance_status": "not_required",
  • "language": "de",
  • "clearance_scheme": "ke_kra_etims",
  • "clearance_reference": "IRN-2026-000123",
  • "clearance_stamp": "string",
  • "clearance_qr": "string",
  • "cleared_at": 0,
  • "clearance_error": "string",
  • "vat_breakdown": [
    ],
  • "vat_aggregation": "horizontal",
  • "notes": [
    ],
  • "payment_terms": {
    },
  • "type_id": "string",
  • "data": { },
  • "addresses": {
    },
  • "created_at": 1746144000,
  • "updated_at": 1746144000,
  • "created_by": {
    },
  • "tenant": {
    }
}

List the invoice's line items

The same rows the invoice itself returns in line_items, at the URL the invoice advertises as _links.line_items. Unpaginated — next_token is always null — because a line array is bounded by what fits on one invoice.

Authorizations:
BearerAuth
path Parameters
invoice_id
required
string
Example: inv_a1b2c3d4e5f6

Responses

Response Schema: application/json
required
Array of objects (InvoiceLineItemResponse)
next_token
required
string or null

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_token": "string"
}

Append a line item to an invoice

Sub-resource sugar over the parent invoice update: the invoice's totals and VAT breakdown are recomputed and one DynamoDB item is written, as if the whole line_items array had been sent.

Draft only. An invoice that has been finalized or cancelled answers 422 — an issued invoice is the legal record and its content is frozen.

The line is new by definition, so a body carrying _id is a 422 — the server assigns line ids. To change an existing line, address its own URL.

Authorizations:
BearerAuth
path Parameters
invoice_id
required
string
Example: inv_a1b2c3d4e5f6
Request Body schema: application/json
required
description
required
string non-empty
required
number or string (Decimal)
_id
string

The id of a line already on this invoice, when the intent is to edit that line rather than add one. Omit it to add a line — the server assigns the id. An _id naming no line on the invoice is a 422; ids are the server's and a client may not invent one.

(Decimal (Decimal (number) or Decimal (string))) or null

Optional. Filled by the resolver when product is supplied alone.

vat_category
string or null
Enum: "H" "S" "R" "R2" "T" "Z" "EXEMPT" null

Which tax band this line falls in. Omit it — the default — to inherit the product's category, then the tenant's vat.default_vat_category. Setting it overrides that inheritance for this line only; the rate is still resolved from it against the supply and target countries and the date, so an overridden line keeps a real regime and legal note. EXEMPT means the supply is out of scope of VAT rather than a 0% band, and routes to the exempt regime instead of a rate lookup.

The rate itself is never accepted on input — it is always derived from the band, the countries involved and the date. Which bands a country has varies, so a client offering them as a choice should read the effective set for the buyer's country rather than assume a fixed list.

Reference (object) or null

Reference to the catalog product whose price should be applied to this line. When set without an explicit unit_price, the invoice service auto-fills unit_price and tax_mode from the resolver result.

unit_code
string or null [ 1 .. 3 ] characters ^[A-Za-z0-9]{1,3}$

UN/ECE Recommendation 20 unit of measure (EN 16931 BT-130). Omit it to get C62 ("one"), which is what every line issued before this field existed reads back as. Any syntactically valid Rec 20 code is accepted, not just the shortlist the UI offers — a trade we did not anticipate must not be blocked by our list. Lower case is upper-cased on write.

InvoiceItemClassification (object) or null

Responses

Response Schema: application/json
_id
required
string
_class
required
string
Value: "line_item"
required
object

self is the line's own resource URL, under the invoice that owns it. It survives an edit: a line id is the server's and is kept when a caller round-trips it.

description
required
string
required
number or string (Decimal)
required
number or string (Decimal)
tax_mode
required
string
Enum: "net" "gross"

Whether unit_price is quoted gross or net of VAT.

vat_regime
required
string
Enum: "domestic_vat" "intra_eu_reverse_charge" "intra_eu_oss" "intra_eu_origin_b2c" "third_country_export" "destination_sales_tax" "exempt"

The VAT regime the resolver picked at issue time. Snapshotted — a historical invoice keeps the treatment that applied when it was issued, even after the countries involved change bloc membership.

vat_country
required
string or null = 2 characters

ISO 3166-1 alpha-2 country whose VAT regime applied.

vat_category
required
string
Enum: "H" "S" "R" "R2" "T" "Z" "EXEMPT"

ONIX List 62 VAT category resolved for this line.

vat_category_source
required
string
Enum: "line" "product" "tenant_default"

Where the category came from — a deliberate per-line choice, the product's default, or the tenant's. Recorded so an audit can tell a chosen band from an inherited one without re-deriving anything.

required
number or string (Decimal)
vat_legal_note
required
string or null

Localised note the invoice must print for this regime (e.g. the reverse-charge or export wording), in the tenant's language with English as the fallback. Null when the regime needs no note.

required
number or string (Decimal)
required
number or string (Decimal)
required
number or string (Decimal)
required
Reference (object) or null

Back-link to the catalog product the line was priced from, when applicable.

resolved_price_id
required
string or null

Id of the ProductPrice row the resolver chose, when the line was priced by the multi-dimension resolver. Always null in single-price mode and when the caller supplied an explicit unit_price.

unit_code
required
string [ 1 .. 3 ] characters ^[A-Z0-9]{1,3}$

UN/ECE Rec 20 unit of measure (BT-130). Never null — a line with no unit is not exportable, so the default C62 is written instead.

unit_label
required
string

unit_code written out for a reader, in the invoice's language and matched to this line's quantity (1 day vs 2 days) — the same word the PDF prints, so a screen and the document it represents never disagree. Read-only and backend-composed; rendered on read rather than stored, so it is not accepted on input and carries no history.

Empty for C62 ("one"): "12 × Sourdough" reads better than "12 Piece × Sourdough". A code outside the shortlist renders as the code itself rather than as nothing — showing it tells the reader something, dropping it would misstate the line.

required
InvoiceItemClassification (object) or null
seller_item_identifier
required
string or null

The seller's own article number (BT-155), snapshotted from the product's sku identifier at issue time. Null when the line carries no product, or the product has no SKU.

required
InvoiceStandardItemIdentifier (object) or null

Snapshotted from the product's gtin at issue time (BT-157). GTIN is the only source: BT-157 identifies the article, and the other product-applicable schemes are not article identifiers in a scheme a receiver can resolve.

required
Array of objects (PrintedIdentifier)

The numbers this line prints beside its description, snapshotted from the product's primary identifiers when the line was written. Which ones print is the product type's decision (primary on its identifier declarations); the pair is stored rather than a joined string so it can be re-ordered and exported. Distinct from BT-155 / BT-157 above, which are the EN 16931 export slots and take only sku / gtin.

Request samples

Content type
application/json
{
  • "_id": "li_a1b2c3d4e5f6",
  • "description": "Consulting services",
  • "quantity": 0,
  • "unit_price": 0,
  • "vat_category": "H",
  • "product": {
    },
  • "unit_code": "HUR",
  • "item_classification": {
    }
}

Response samples

Content type
application/json
{
  • "_id": "li_a1b2c3d4e5f6",
  • "_class": "line_item",
  • "_links": {
    },
  • "description": "Consulting services",
  • "quantity": 0,
  • "unit_price": 0,
  • "tax_mode": "net",
  • "vat_regime": "domestic_vat",
  • "vat_country": "st",
  • "vat_category": "H",
  • "vat_category_source": "line",
  • "vat_rate_percent": 0,
  • "vat_legal_note": "string",
  • "unit_price_net": 0,
  • "tax_amount": 0,
  • "line_total": 0,
  • "unit_code": "HUR",
  • "unit_label": "h",
  • "item_classification": {
    },
  • "seller_item_identifier": "SD-100",
  • "standard_item_identifier": {
    },
  • "item_identifiers": [
    ],
  • "product": {
    },
  • "resolved_price_id": "string"
}

Get one line item

A line is an object with an id, so it answers at its own URL — the one every line carries as _links.self. 404 when this invoice does not hold that line, including when it belongs to another invoice.

Authorizations:
BearerAuth
path Parameters
invoice_id
required
string
Example: inv_a1b2c3d4e5f6
line_item_id
required
string
Example: li_a1b2c3d4e5f6

Responses

Response Schema: application/json
_id
required
string
_class
required
string
Value: "line_item"
required
object

self is the line's own resource URL, under the invoice that owns it. It survives an edit: a line id is the server's and is kept when a caller round-trips it.

description
required
string
required
number or string (Decimal)
required
number or string (Decimal)
tax_mode
required
string
Enum: "net" "gross"

Whether unit_price is quoted gross or net of VAT.

vat_regime
required
string
Enum: "domestic_vat" "intra_eu_reverse_charge" "intra_eu_oss" "intra_eu_origin_b2c" "third_country_export" "destination_sales_tax" "exempt"

The VAT regime the resolver picked at issue time. Snapshotted — a historical invoice keeps the treatment that applied when it was issued, even after the countries involved change bloc membership.

vat_country
required
string or null = 2 characters

ISO 3166-1 alpha-2 country whose VAT regime applied.

vat_category
required
string
Enum: "H" "S" "R" "R2" "T" "Z" "EXEMPT"

ONIX List 62 VAT category resolved for this line.

vat_category_source
required
string
Enum: "line" "product" "tenant_default"

Where the category came from — a deliberate per-line choice, the product's default, or the tenant's. Recorded so an audit can tell a chosen band from an inherited one without re-deriving anything.

required
number or string (Decimal)
vat_legal_note
required
string or null

Localised note the invoice must print for this regime (e.g. the reverse-charge or export wording), in the tenant's language with English as the fallback. Null when the regime needs no note.

required
number or string (Decimal)
required
number or string (Decimal)
required
number or string (Decimal)
required
Reference (object) or null

Back-link to the catalog product the line was priced from, when applicable.

resolved_price_id
required
string or null

Id of the ProductPrice row the resolver chose, when the line was priced by the multi-dimension resolver. Always null in single-price mode and when the caller supplied an explicit unit_price.

unit_code
required
string [ 1 .. 3 ] characters ^[A-Z0-9]{1,3}$

UN/ECE Rec 20 unit of measure (BT-130). Never null — a line with no unit is not exportable, so the default C62 is written instead.

unit_label
required
string

unit_code written out for a reader, in the invoice's language and matched to this line's quantity (1 day vs 2 days) — the same word the PDF prints, so a screen and the document it represents never disagree. Read-only and backend-composed; rendered on read rather than stored, so it is not accepted on input and carries no history.

Empty for C62 ("one"): "12 × Sourdough" reads better than "12 Piece × Sourdough". A code outside the shortlist renders as the code itself rather than as nothing — showing it tells the reader something, dropping it would misstate the line.

required
InvoiceItemClassification (object) or null
seller_item_identifier
required
string or null

The seller's own article number (BT-155), snapshotted from the product's sku identifier at issue time. Null when the line carries no product, or the product has no SKU.

required
InvoiceStandardItemIdentifier (object) or null

Snapshotted from the product's gtin at issue time (BT-157). GTIN is the only source: BT-157 identifies the article, and the other product-applicable schemes are not article identifiers in a scheme a receiver can resolve.

required
Array of objects (PrintedIdentifier)

The numbers this line prints beside its description, snapshotted from the product's primary identifiers when the line was written. Which ones print is the product type's decision (primary on its identifier declarations); the pair is stored rather than a joined string so it can be re-ordered and exported. Distinct from BT-155 / BT-157 above, which are the EN 16931 export slots and take only sku / gtin.

Response samples

Content type
application/json
{
  • "_id": "li_a1b2c3d4e5f6",
  • "_class": "line_item",
  • "_links": {
    },
  • "description": "Consulting services",
  • "quantity": 0,
  • "unit_price": 0,
  • "tax_mode": "net",
  • "vat_regime": "domestic_vat",
  • "vat_country": "st",
  • "vat_category": "H",
  • "vat_category_source": "line",
  • "vat_rate_percent": 0,
  • "vat_legal_note": "string",
  • "unit_price_net": 0,
  • "tax_amount": 0,
  • "line_total": 0,
  • "unit_code": "HUR",
  • "unit_label": "h",
  • "item_classification": {
    },
  • "seller_item_identifier": "SD-100",
  • "standard_item_identifier": {
    },
  • "item_identifiers": [
    ],
  • "product": {
    },
  • "resolved_price_id": "string"
}

Replace one line item

Full replace: the body is the new content of the line, and the line id stays with it across the swap so the URL survives. A body _id that disagrees with the URL is a 422 rather than being silently overridden.

A PUT restates the line, so an explicit unit_price is read as net unless the line names a product — the same reading a create gets. Use PATCH to edit a gross-quoted line without restating its basis.

Draft only; see POST.

Authorizations:
BearerAuth
path Parameters
invoice_id
required
string
Example: inv_a1b2c3d4e5f6
line_item_id
required
string
Example: li_a1b2c3d4e5f6
Request Body schema: application/json
required
description
required
string non-empty
required
number or string (Decimal)
_id
string

The id of a line already on this invoice, when the intent is to edit that line rather than add one. Omit it to add a line — the server assigns the id. An _id naming no line on the invoice is a 422; ids are the server's and a client may not invent one.

(Decimal (Decimal (number) or Decimal (string))) or null

Optional. Filled by the resolver when product is supplied alone.

vat_category
string or null
Enum: "H" "S" "R" "R2" "T" "Z" "EXEMPT" null

Which tax band this line falls in. Omit it — the default — to inherit the product's category, then the tenant's vat.default_vat_category. Setting it overrides that inheritance for this line only; the rate is still resolved from it against the supply and target countries and the date, so an overridden line keeps a real regime and legal note. EXEMPT means the supply is out of scope of VAT rather than a 0% band, and routes to the exempt regime instead of a rate lookup.

The rate itself is never accepted on input — it is always derived from the band, the countries involved and the date. Which bands a country has varies, so a client offering them as a choice should read the effective set for the buyer's country rather than assume a fixed list.

Reference (object) or null

Reference to the catalog product whose price should be applied to this line. When set without an explicit unit_price, the invoice service auto-fills unit_price and tax_mode from the resolver result.

unit_code
string or null [ 1 .. 3 ] characters ^[A-Za-z0-9]{1,3}$

UN/ECE Recommendation 20 unit of measure (EN 16931 BT-130). Omit it to get C62 ("one"), which is what every line issued before this field existed reads back as. Any syntactically valid Rec 20 code is accepted, not just the shortlist the UI offers — a trade we did not anticipate must not be blocked by our list. Lower case is upper-cased on write.

InvoiceItemClassification (object) or null

Responses

Response Schema: application/json
_id
required
string
_class
required
string
Value: "line_item"
required
object

self is the line's own resource URL, under the invoice that owns it. It survives an edit: a line id is the server's and is kept when a caller round-trips it.

description
required
string
required
number or string (Decimal)
required
number or string (Decimal)
tax_mode
required
string
Enum: "net" "gross"

Whether unit_price is quoted gross or net of VAT.

vat_regime
required
string
Enum: "domestic_vat" "intra_eu_reverse_charge" "intra_eu_oss" "intra_eu_origin_b2c" "third_country_export" "destination_sales_tax" "exempt"

The VAT regime the resolver picked at issue time. Snapshotted — a historical invoice keeps the treatment that applied when it was issued, even after the countries involved change bloc membership.

vat_country
required
string or null = 2 characters

ISO 3166-1 alpha-2 country whose VAT regime applied.

vat_category
required
string
Enum: "H" "S" "R" "R2" "T" "Z" "EXEMPT"

ONIX List 62 VAT category resolved for this line.

vat_category_source
required
string
Enum: "line" "product" "tenant_default"

Where the category came from — a deliberate per-line choice, the product's default, or the tenant's. Recorded so an audit can tell a chosen band from an inherited one without re-deriving anything.

required
number or string (Decimal)
vat_legal_note
required
string or null

Localised note the invoice must print for this regime (e.g. the reverse-charge or export wording), in the tenant's language with English as the fallback. Null when the regime needs no note.

required
number or string (Decimal)
required
number or string (Decimal)
required
number or string (Decimal)
required
Reference (object) or null

Back-link to the catalog product the line was priced from, when applicable.

resolved_price_id
required
string or null

Id of the ProductPrice row the resolver chose, when the line was priced by the multi-dimension resolver. Always null in single-price mode and when the caller supplied an explicit unit_price.

unit_code
required
string [ 1 .. 3 ] characters ^[A-Z0-9]{1,3}$

UN/ECE Rec 20 unit of measure (BT-130). Never null — a line with no unit is not exportable, so the default C62 is written instead.

unit_label
required
string

unit_code written out for a reader, in the invoice's language and matched to this line's quantity (1 day vs 2 days) — the same word the PDF prints, so a screen and the document it represents never disagree. Read-only and backend-composed; rendered on read rather than stored, so it is not accepted on input and carries no history.

Empty for C62 ("one"): "12 × Sourdough" reads better than "12 Piece × Sourdough". A code outside the shortlist renders as the code itself rather than as nothing — showing it tells the reader something, dropping it would misstate the line.

required
InvoiceItemClassification (object) or null
seller_item_identifier
required
string or null

The seller's own article number (BT-155), snapshotted from the product's sku identifier at issue time. Null when the line carries no product, or the product has no SKU.

required
InvoiceStandardItemIdentifier (object) or null

Snapshotted from the product's gtin at issue time (BT-157). GTIN is the only source: BT-157 identifies the article, and the other product-applicable schemes are not article identifiers in a scheme a receiver can resolve.

required
Array of objects (PrintedIdentifier)

The numbers this line prints beside its description, snapshotted from the product's primary identifiers when the line was written. Which ones print is the product type's decision (primary on its identifier declarations); the pair is stored rather than a joined string so it can be re-ordered and exported. Distinct from BT-155 / BT-157 above, which are the EN 16931 export slots and take only sku / gtin.

Request samples

Content type
application/json
{
  • "_id": "li_a1b2c3d4e5f6",
  • "description": "Consulting services",
  • "quantity": 0,
  • "unit_price": 0,
  • "vat_category": "H",
  • "product": {
    },
  • "unit_code": "HUR",
  • "item_classification": {
    }
}

Response samples

Content type
application/json
{
  • "_id": "li_a1b2c3d4e5f6",
  • "_class": "line_item",
  • "_links": {
    },
  • "description": "Consulting services",
  • "quantity": 0,
  • "unit_price": 0,
  • "tax_mode": "net",
  • "vat_regime": "domestic_vat",
  • "vat_country": "st",
  • "vat_category": "H",
  • "vat_category_source": "line",
  • "vat_rate_percent": 0,
  • "vat_legal_note": "string",
  • "unit_price_net": 0,
  • "tax_amount": 0,
  • "line_total": 0,
  • "unit_code": "HUR",
  • "unit_label": "h",
  • "item_classification": {
    },
  • "seller_item_identifier": "SD-100",
  • "standard_item_identifier": {
    },
  • "item_identifiers": [
    ],
  • "product": {
    },
  • "resolved_price_id": "string"
}

Partially update one line item

Body is an arbitrary subset of InvoiceLineItemCreate fields. The service overlays the patch onto the stored line, re-validates the result and rebuilds the line, then recomputes the invoice's totals and VAT breakdown. A body _id that disagrees with the URL is a 422.

The line keeps the tax basis it was quoted in: patching the quantity of a gross-quoted line does not re-read its price as net.

Draft only; see POST.

Authorizations:
BearerAuth
path Parameters
invoice_id
required
string
Example: inv_a1b2c3d4e5f6
line_item_id
required
string
Example: li_a1b2c3d4e5f6
Request Body schema: application/json
required
property name*
additional property
any

Responses

Response Schema: application/json
_id
required
string
_class
required
string
Value: "line_item"
required
object

self is the line's own resource URL, under the invoice that owns it. It survives an edit: a line id is the server's and is kept when a caller round-trips it.

description
required
string
required
number or string (Decimal)
required
number or string (Decimal)
tax_mode
required
string
Enum: "net" "gross"

Whether unit_price is quoted gross or net of VAT.

vat_regime
required
string
Enum: "domestic_vat" "intra_eu_reverse_charge" "intra_eu_oss" "intra_eu_origin_b2c" "third_country_export" "destination_sales_tax" "exempt"

The VAT regime the resolver picked at issue time. Snapshotted — a historical invoice keeps the treatment that applied when it was issued, even after the countries involved change bloc membership.

vat_country
required
string or null = 2 characters

ISO 3166-1 alpha-2 country whose VAT regime applied.

vat_category
required
string
Enum: "H" "S" "R" "R2" "T" "Z" "EXEMPT"

ONIX List 62 VAT category resolved for this line.

vat_category_source
required
string
Enum: "line" "product" "tenant_default"

Where the category came from — a deliberate per-line choice, the product's default, or the tenant's. Recorded so an audit can tell a chosen band from an inherited one without re-deriving anything.

required
number or string (Decimal)
vat_legal_note
required
string or null

Localised note the invoice must print for this regime (e.g. the reverse-charge or export wording), in the tenant's language with English as the fallback. Null when the regime needs no note.

required
number or string (Decimal)
required
number or string (Decimal)
required
number or string (Decimal)
required
Reference (object) or null

Back-link to the catalog product the line was priced from, when applicable.

resolved_price_id
required
string or null

Id of the ProductPrice row the resolver chose, when the line was priced by the multi-dimension resolver. Always null in single-price mode and when the caller supplied an explicit unit_price.

unit_code
required
string [ 1 .. 3 ] characters ^[A-Z0-9]{1,3}$

UN/ECE Rec 20 unit of measure (BT-130). Never null — a line with no unit is not exportable, so the default C62 is written instead.

unit_label
required
string

unit_code written out for a reader, in the invoice's language and matched to this line's quantity (1 day vs 2 days) — the same word the PDF prints, so a screen and the document it represents never disagree. Read-only and backend-composed; rendered on read rather than stored, so it is not accepted on input and carries no history.

Empty for C62 ("one"): "12 × Sourdough" reads better than "12 Piece × Sourdough". A code outside the shortlist renders as the code itself rather than as nothing — showing it tells the reader something, dropping it would misstate the line.

required
InvoiceItemClassification (object) or null
seller_item_identifier
required
string or null

The seller's own article number (BT-155), snapshotted from the product's sku identifier at issue time. Null when the line carries no product, or the product has no SKU.

required
InvoiceStandardItemIdentifier (object) or null

Snapshotted from the product's gtin at issue time (BT-157). GTIN is the only source: BT-157 identifies the article, and the other product-applicable schemes are not article identifiers in a scheme a receiver can resolve.

required
Array of objects (PrintedIdentifier)

The numbers this line prints beside its description, snapshotted from the product's primary identifiers when the line was written. Which ones print is the product type's decision (primary on its identifier declarations); the pair is stored rather than a joined string so it can be re-ordered and exported. Distinct from BT-155 / BT-157 above, which are the EN 16931 export slots and take only sku / gtin.

Request samples

Content type
application/json
{ }

Response samples

Content type
application/json
{
  • "_id": "li_a1b2c3d4e5f6",
  • "_class": "line_item",
  • "_links": {
    },
  • "description": "Consulting services",
  • "quantity": 0,
  • "unit_price": 0,
  • "tax_mode": "net",
  • "vat_regime": "domestic_vat",
  • "vat_country": "st",
  • "vat_category": "H",
  • "vat_category_source": "line",
  • "vat_rate_percent": 0,
  • "vat_legal_note": "string",
  • "unit_price_net": 0,
  • "tax_amount": 0,
  • "line_total": 0,
  • "unit_code": "HUR",
  • "unit_label": "h",
  • "item_classification": {
    },
  • "seller_item_identifier": "SD-100",
  • "standard_item_identifier": {
    },
  • "item_identifiers": [
    ],
  • "product": {
    },
  • "resolved_price_id": "string"
}

Remove one line item from an invoice

Recomputes the invoice's totals and VAT breakdown. Draft only; see POST.

Authorizations:
BearerAuth
path Parameters
invoice_id
required
string
Example: inv_a1b2c3d4e5f6
line_item_id
required
string
Example: li_a1b2c3d4e5f6

Responses

Response samples

Content type
application/json
{
  • "error": "not_found",
  • "message": "Business partner not found",
  • "details": { }
}

Offers

List offers (newest first)

Authorizations:
BearerAuth
query Parameters
limit
integer [ 1 .. 200 ]
Default: 50
next_token
string

Base64-encoded pagination cursor from previous response

status
string
Enum: "draft" "finalized" "sent" "accepted" "rejected" "expired" "superseded"
business_partner_id
string
type_id
string

Responses

Response Schema: application/json
required
Array of objects (OfferResponse)
next_token
required
string or null

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_token": "string"
}

Create a new offer (starts as draft)

Authorizations:
BearerAuth
Request Body schema: application/json
required
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

ReferenceValue (object) or null

Issuing Company (EN 16931 BG-7 Seller). Auto-resolved from the tenant's invoice-company config when omitted on create. Per the no-bare-id rule (architecture.md#Embedded References) the wire shape is the full Reference object.

Reference (object) or null

Optional tenant-defined sales channel selected for this offer. Snapshotted on the offer.

issue_date
string or null <date>
currency
string or null = 3 characters
buyer_reference
string or null

EN 16931 BT-10. Often the buyer's PO number or Leitweg-ID.

purchase_order_reference
string or null

EN 16931 BT-13.

contract_reference
string or null

EN 16931 BT-12.

project_reference
string or null

EN 16931 BT-11.

subject
string or null

What the offer is about, printed as its subject line.

InvoicePeriod (object) or null
buyer_name
string or null

EN 16931 BT-44 — buyer name. Defaults to the partner's company/full name.

buyer_legal_registration_id
string or null

EN 16931 BT-47.

buyer_identifier
string or null

EN 16931 BT-46 — party identifier (GLN, DUNS, etc.).

buyer_customer_number
string or null

The buyer's customer number with the seller (DIN 5008 Kundennummer). Defaults to the business partner's.

buyer_vat_id
string or null

EN 16931 BT-48.

buyer_vat_id_type
string or null

EN 16931 VAT scheme of buyer_vat_id — the identifier registry schema_key (e.g. vat_eu, vat_gb). Defaulted from the partner's VAT-class identifier when omitted.

InvoicePartyContact (object) or null
InvoiceElectronicAddress (object) or null
InvoicePayee (object) or null
payment_terms_text
string or null

EN 16931 BT-20.

InvoicePaymentTerms (object) or null

Structured skonto / late-payment block. Additive to BT-20 payment_terms_text — both render when supplied.

remittance_information
string or null

EN 16931 BT-83 — payment reference / Verwendungszweck.

Array of objects (InvoiceLineItemCreate)
notes
Array of strings
Default: []

EN 16931 BT-22 — invoice notes (0..n). Each entry is rendered as its own paragraph below the totals. The author handles translation; the renderer prints what is supplied.

vat_aggregation
string
Enum: "horizontal" "vertical"

How per-line VAT rolls up into the offer totals. vertical computes VAT once per rate band on the aggregated net (the B2B norm); horizontal sums the VAT shown on each line. Optional: omitted, the offer takes the default_vat_aggregation of its offer type.

type_id
string [ 1 .. 100 ] characters

The offer-class tenant type this offer is created under. Every offer has one. It decides the letterhead and the payment defaults. Omitted, the offer is created under the tenant's default offer type; that is a 422 when the tenant has several and marks none of them default.

schema_version
string or null
object
Default: {}
object

Optional. When omitted, the server snapshots the partner's preferred addresses for each slot declared by the offer type. When supplied, the slot keys must be a subset of the offer type's address_slots.

valid_until
string <date>

Last day the quoted price stands. Defaults to issue_date + the tenant's offer.default_validity_days.

Responses

Response Schema: application/json
_id
required
string
_class
required
string
_name
required
string (EntityName)

Read-only, backend-composed display label (the offer number, or id while still a draft).

required
object

self is the offer's own resource URL. Its documents live under /resource/v1/offers/{id}/documents. Further rels may be present; a client follows the ones it knows.

required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

status
required
string
Enum: "draft" "finalized" "sent" "accepted" "rejected" "expired" "superseded"
issue_date
required
string <date>
currency
required
string
required
Array of objects (OfferLineItemResponse)
required
object (MonetaryAmount)

EN 16931 BT-109 — sum of line nets, as a MonetaryAmount carrying the offer's currency (BT-5). A draft whose totals haven't been computed yet returns the identity element ({"amount": 0, "currency": null}).

required
object (MonetaryAmount)

EN 16931 BT-110.

required
object (MonetaryAmount)

EN 16931 BT-112. BT-115 (payable_amount) equals this until prepaid / rounding ships.

required
Array of objects (InvoiceVatBreakdownLine)

Per-VAT-rate breakdown (UStG §14). Server-computed by recompute_totals(); read-only on the API. The renderer prefers this persisted list over recomputation.

vat_aggregation
required
string
Enum: "horizontal" "vertical"

How per-line VAT rolls up into the offer totals. vertical computes VAT once per rate band on the aggregated net (the B2B norm); horizontal sums the VAT shown on each line. The two can differ by a rounding cent. Chosen by the caller on create or on a draft update; omitted, it is the default_vat_aggregation of the offer type.

language
required
string

The language the offer is written in. Snapshotted from the business partner when the draft is created, refreshed on every draft save and frozen once the offer leaves draft; falls back to the tenant's default language when the partner has none. Always present. Read-only: it is not accepted on input.

notes
required
Array of strings

EN 16931 BT-22 notes (0..n), printed on the offer.

type_id
required
string

The tenant type this offer was created under.

required
object
required
object

Snapshotted address per slot the offer type declared. Slot keys are a subset of the type's address_slots (invoice | shipping | service). Empty object when no slot is configured for the type or the partner has no eligible address.

created_at
required
integer
updated_at
required
integer
required
object (ReferenceValue)

Stored value for a field of type "reference"

required
object (ReferenceValue)

Stored value for a field of type "reference"

offer_number
required
string

Empty ("") until the offer is finalized. Offer numbers are not gap-free.

valid_until
required
string or null <date>
required
Reference (object) or null

The offer this one replaces, when it is a revision.

accepted_at
required
integer or null

Unix seconds.

rejected_at
required
integer or null

Unix seconds.

required
Array of objects (IdentifierResponse)

The numbers this record carries (ISBN, GTIN, VAT ID, ...), stored on the record itself.

ReferenceValue (object) or null

Issuing Company (BG-7 Seller). Snapshotted onto the immutable seller_* columns at draft → finalized. Null until the auto-default resolves (Phase D seed guarantees a value for new tenants).

Reference (object) or null

Sales channel selected for this offer, null if none.

InvoicePaymentTerms (object) or null

Structured BT-20 sub-block. null when unset.

Request samples

Content type
application/json
{
  • "business_partner": {
    },
  • "company": {
    },
  • "channel": {
    },
  • "issue_date": "2026-05-01",
  • "currency": "EUR",
  • "buyer_reference": "string",
  • "purchase_order_reference": "string",
  • "contract_reference": "string",
  • "project_reference": "string",
  • "subject": "string",
  • "invoicing_period": {
    },
  • "buyer_name": "string",
  • "buyer_legal_registration_id": "string",
  • "buyer_identifier": "string",
  • "buyer_customer_number": "string",
  • "buyer_vat_id": "string",
  • "buyer_vat_id_type": "string",
  • "buyer_contact": {
    },
  • "buyer_electronic_address": {
    },
  • "payee": {
    },
  • "payment_terms_text": "string",
  • "payment_terms": {
    },
  • "remittance_information": "string",
  • "line_items": [
    ],
  • "notes": [ ],
  • "vat_aggregation": "horizontal",
  • "type_id": "standard_offer",
  • "schema_version": "string",
  • "data": { },
  • "addresses": {
    },
  • "valid_until": "2019-08-24"
}

Response samples

Content type
application/json
{
  • "identifiers": [
    ],
  • "_id": "off_a1b2c3d4e5f6",
  • "_class": "offer",
  • "_name": "Acme Corp",
  • "_links": {
    },
  • "business_partner": {
    },
  • "company": {
    },
  • "channel": {
    },
  • "status": "draft",
  • "issue_date": "2026-05-01",
  • "currency": "EUR",
  • "line_items": [
    ],
  • "tax_exclusive": {
    },
  • "tax_total": {
    },
  • "tax_inclusive": {
    },
  • "language": "de",
  • "vat_breakdown": [
    ],
  • "vat_aggregation": "horizontal",
  • "notes": [
    ],
  • "payment_terms": {
    },
  • "type_id": "string",
  • "data": { },
  • "addresses": {
    },
  • "created_at": 1746144000,
  • "updated_at": 1746144000,
  • "created_by": {
    },
  • "tenant": {
    },
  • "offer_number": "AN-2026-0001",
  • "valid_until": "2019-08-24",
  • "supersedes": {
    },
  • "accepted_at": 0,
  • "rejected_at": 0
}

List an offer's generated documents

Authorizations:
BearerAuth
path Parameters
offer_id
required
string
Example: off_a1b2c3d4e5f6

Responses

Response Schema: application/json
required
Array of objects (OfferDocumentResponse)
next_token
required
string or null

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_token": "string"
}

Request the offer PDF

Writes the document in pdf_pending and publishes the event the generator subscribes to. The offer must have been finalized: a draft has no number, and a document with no number on it is not one to put in front of a customer.

Authorizations:
BearerAuth
path Parameters
offer_id
required
string
Example: off_a1b2c3d4e5f6
Request Body schema: application/json
optional
document_type
string (OfferDocumentType)
Value: "offer"

The kind of document. One today: the rendered offer PDF. It is an enum rather than a free string so a second artefact arrives as a deliberate change rather than by a caller inventing a value.

Responses

Response Schema: application/json
_id
required
string
_class
required
string
offer_id
required
string
document_type
required
string (OfferDocumentType)
Value: "offer"

The kind of document. One today: the rendered offer PDF. It is an enum rather than a free string so a second artefact arrives as a deliberate change rather than by a caller inventing a value.

status
required
string (DocumentStatus)
Enum: "draft" "pdf_pending" "pdf_ready" "pdf_failed"

Lifecycle state. New documents start at pdf_pending; the async PDF worker promotes them to pdf_ready (or pdf_failed on permanent error). draft is reserved for future use.

required
Reference (object) or null

Reference to the rendered PDF. null until the document-generator worker reports the output via PATCH .../documents/{id}.

template_version
required
string
created_at
required
integer
updated_at
required
integer
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

required
ReferenceValue (object) or null

Request samples

Content type
application/json
{
  • "document_type": "offer"
}

Response samples

Content type
application/json
{
  • "_id": "odoc_a1b2c3d4e5f6789012345",
  • "_class": "offer_document",
  • "offer_id": "off_a1b2c3d4e5f6",
  • "document_type": "offer",
  • "status": "draft",
  • "file": {
    },
  • "template_version": "1.0.0",
  • "created_at": 0,
  • "updated_at": 0,
  • "tenant": {
    },
  • "created_by": {
    }
}

Get one of an offer's documents

Authorizations:
BearerAuth
path Parameters
offer_id
required
string
Example: off_a1b2c3d4e5f6
offer_document_id
required
string
Example: odoc_a1b2c3d4e5f6789012345

Responses

Response Schema: application/json
_id
required
string
_class
required
string
offer_id
required
string
document_type
required
string (OfferDocumentType)
Value: "offer"

The kind of document. One today: the rendered offer PDF. It is an enum rather than a free string so a second artefact arrives as a deliberate change rather than by a caller inventing a value.

status
required
string (DocumentStatus)
Enum: "draft" "pdf_pending" "pdf_ready" "pdf_failed"

Lifecycle state. New documents start at pdf_pending; the async PDF worker promotes them to pdf_ready (or pdf_failed on permanent error). draft is reserved for future use.

required
Reference (object) or null

Reference to the rendered PDF. null until the document-generator worker reports the output via PATCH .../documents/{id}.

template_version
required
string
created_at
required
integer
updated_at
required
integer
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

required
ReferenceValue (object) or null

Response samples

Content type
application/json
{
  • "_id": "odoc_a1b2c3d4e5f6789012345",
  • "_class": "offer_document",
  • "offer_id": "off_a1b2c3d4e5f6",
  • "document_type": "offer",
  • "status": "draft",
  • "file": {
    },
  • "template_version": "1.0.0",
  • "created_at": 0,
  • "updated_at": 0,
  • "tenant": {
    },
  • "created_by": {
    }
}

Attach the rendered PDF, or retry a failed render

Setting file on a pdf_pending document moves it to pdf_ready. Idempotent: replaying on an already-attached document returns the PDF it already has. Setting status to pdf_pending re-requests the render of a failed document.

Authorizations:
BearerAuth
path Parameters
offer_id
required
string
Example: off_a1b2c3d4e5f6
offer_document_id
required
string
Example: odoc_a1b2c3d4e5f6789012345
Request Body schema: application/json
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

status
string
Value: "pdf_pending"

Set to pdf_pending to re-request a failed render. Idempotent for an already-pending row; 422 from any other status, and 422 when sent together with file — those are two different writes.

Responses

Response Schema: application/json
_id
required
string
_class
required
string
offer_id
required
string
document_type
required
string (OfferDocumentType)
Value: "offer"

The kind of document. One today: the rendered offer PDF. It is an enum rather than a free string so a second artefact arrives as a deliberate change rather than by a caller inventing a value.

status
required
string (DocumentStatus)
Enum: "draft" "pdf_pending" "pdf_ready" "pdf_failed"

Lifecycle state. New documents start at pdf_pending; the async PDF worker promotes them to pdf_ready (or pdf_failed on permanent error). draft is reserved for future use.

required
Reference (object) or null

Reference to the rendered PDF. null until the document-generator worker reports the output via PATCH .../documents/{id}.

template_version
required
string
created_at
required
integer
updated_at
required
integer
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

required
ReferenceValue (object) or null

Request samples

Content type
application/json
{
  • "file": {
    },
  • "status": "pdf_pending"
}

Response samples

Content type
application/json
{
  • "_id": "odoc_a1b2c3d4e5f6789012345",
  • "_class": "offer_document",
  • "offer_id": "off_a1b2c3d4e5f6",
  • "document_type": "offer",
  • "status": "draft",
  • "file": {
    },
  • "template_version": "1.0.0",
  • "created_at": 0,
  • "updated_at": 0,
  • "tenant": {
    },
  • "created_by": {
    }
}

Attach the rendered PDF, or retry a failed render

Authorizations:
BearerAuth
path Parameters
offer_id
required
string
Example: off_a1b2c3d4e5f6
offer_document_id
required
string
Example: odoc_a1b2c3d4e5f6789012345
Request Body schema: application/json
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

status
string
Value: "pdf_pending"

Set to pdf_pending to re-request a failed render. Idempotent for an already-pending row; 422 from any other status, and 422 when sent together with file — those are two different writes.

Responses

Response Schema: application/json
_id
required
string
_class
required
string
offer_id
required
string
document_type
required
string (OfferDocumentType)
Value: "offer"

The kind of document. One today: the rendered offer PDF. It is an enum rather than a free string so a second artefact arrives as a deliberate change rather than by a caller inventing a value.

status
required
string (DocumentStatus)
Enum: "draft" "pdf_pending" "pdf_ready" "pdf_failed"

Lifecycle state. New documents start at pdf_pending; the async PDF worker promotes them to pdf_ready (or pdf_failed on permanent error). draft is reserved for future use.

required
Reference (object) or null

Reference to the rendered PDF. null until the document-generator worker reports the output via PATCH .../documents/{id}.

template_version
required
string
created_at
required
integer
updated_at
required
integer
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

required
ReferenceValue (object) or null

Request samples

Content type
application/json
{
  • "file": {
    },
  • "status": "pdf_pending"
}

Response samples

Content type
application/json
{
  • "_id": "odoc_a1b2c3d4e5f6789012345",
  • "_class": "offer_document",
  • "offer_id": "off_a1b2c3d4e5f6",
  • "document_type": "offer",
  • "status": "draft",
  • "file": {
    },
  • "template_version": "1.0.0",
  • "created_at": 0,
  • "updated_at": 0,
  • "tenant": {
    },
  • "created_by": {
    }
}

Delete a document and soft-delete its PDF

Authorizations:
BearerAuth
path Parameters
offer_id
required
string
Example: off_a1b2c3d4e5f6
offer_document_id
required
string
Example: odoc_a1b2c3d4e5f6789012345

Responses

Response samples

Content type
application/json
{
  • "error": "not_found",
  • "message": "Business partner not found",
  • "details": { }
}

List the messages sent about an offer

One row per send attempt per channel, newest first — the answer to "has this offer gone out, where to, and when".

Authorizations:
BearerAuth
path Parameters
offer_id
required
string
Example: off_a1b2c3d4e5f6

Responses

Response Schema: application/json
required
Array of objects (CorrespondenceResponse)
next_token
required
string or null

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_token": "string"
}

Send an offer to the business partner over a channel

The same sub-resource the invoice has, mounted on the offer: sending creates a correspondence row, so two sends are two rows.

The send itself is performed by an event-driven worker, so a 202 means "accepted for sending", not "sent" — poll GET /resource/v1/offers/{offer_id}/correspondence (or the row's _links.self) until status leaves queued.

The offer must have left draft (it needs its number and frozen seller snapshot), and the business partner must have an email address. The offer's own status is unchanged by a send — the rows are the record of what went out, which is also what tells a reopened offer whether it was ever sent.

The only 409 is a send still in flight: while a row on the same channel is queued, a repeat is the same send twice, not a second one.

Authorizations:
BearerAuth
path Parameters
offer_id
required
string
Example: off_a1b2c3d4e5f6
Request Body schema: application/json
required
channel
required
string (CorrespondenceChannel)
Value: "email"

Channel a document is sent over. email is the only value at MVP; the enum exists so adding a channel (e.g. WhatsApp) is an added member rather than a new endpoint.

acknowledge_uncleared
boolean
Default: false

Send even though the invoice has not been cleared by the tax authority. Staying compliant is the tenant's own legal responsibility, so an uncleared send is warned about (409 with invoice_not_cleared) rather than blocked outright; set this to acknowledge that responsibility and send the invoice anyway.

Responses

Request samples

Content type
application/json
{
  • "channel": "email",
  • "acknowledge_uncleared": false
}

Response samples

Content type
application/json
{
  • "_id": "cor_a1b2c3d4e5f6789012345",
  • "_class": "correspondence",
  • "document": {
    },
  • "channel": "email",
  • "purpose": "invoice",
  • "status": "queued",
  • "recipient": "billing@example.com",
  • "attachment": {
    },
  • "email_message": {
    },
  • "sent_at": 0,
  • "failure_reason": "string",
  • "message": "string",
  • "created_at": 0,
  • "updated_at": 0,
  • "tenant": {
    },
  • "_links": {
    },
  • "created_by": {
    }
}

Read one message sent about an offer

The row's own _links.self. Poll it while status is queued to learn whether the send succeeded.

Authorizations:
BearerAuth
path Parameters
offer_id
required
string
Example: off_a1b2c3d4e5f6
correspondence_id
required
string
Example: cor_a1b2c3d4e5f6789012345

Responses

Response Schema: application/json
_id
required
string
_class
required
string
required
object (Reference)

The document this message is about — an invoice or an offer.

channel
required
string (CorrespondenceChannel)
Value: "email"

Channel a document is sent over. email is the only value at MVP; the enum exists so adding a channel (e.g. WhatsApp) is an added member rather than a new endpoint.

purpose
required
string (CorrespondencePurpose)
Enum: "invoice" "reminder" "offer"

What went out. invoice is the invoice itself and offer the offer itself, each carrying its own PDF; reminder is a dunning payment reminder chasing an invoice, carrying the approved message in the body and the same invoice PDF as its attachment. A document may only have one send in flight per channel; reminders repeat as the dunning ladder escalates.

status
required
string (CorrespondenceStatus)
Enum: "queued" "sent" "failed"

Lifecycle state. A send starts at queued; the correspondence worker promotes it to sent once the channel accepted the message, or to failed with a failure_reason when the channel rejected it permanently. A failed row does not block a fresh send attempt.

recipient
required
string

Resolved destination for this channel — for email, the business partner's invoicing address at the moment the send was requested.

message
required
string or null

The approved reminder text sent in the body, for a reminder row; null for an invoice or offer row, whose body is generated from the document.

required
Reference (object) or null

The PDF file that actually went out. null until the row is sent — which document is attached is decided at send time, so a regenerated PDF is the one the customer receives.

required
Reference (object) or null

The stored outbound message. Null until the worker links it, including the interval after message creation but before linking. Once linked, it remains available whether sending succeeds or fails.

sent_at
required
integer or null

Unix seconds the channel accepted the message; null until then.

failure_reason
required
string or null

Channel error code for a failed row; null otherwise.

created_at
required
integer
updated_at
required
integer
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

required
object

self is the row's own URL — the one to poll while status is queued.

required
ReferenceValue (object) or null

Response samples

Content type
application/json
{
  • "_id": "cor_a1b2c3d4e5f6789012345",
  • "_class": "correspondence",
  • "document": {
    },
  • "channel": "email",
  • "purpose": "invoice",
  • "status": "queued",
  • "recipient": "billing@example.com",
  • "attachment": {
    },
  • "email_message": {
    },
  • "sent_at": 0,
  • "failure_reason": "string",
  • "message": "string",
  • "created_at": 0,
  • "updated_at": 0,
  • "tenant": {
    },
  • "_links": {
    },
  • "created_by": {
    }
}

Raise a draft invoice from an accepted offer

Accepting and invoicing are two acts: accepting is a status change and creates nothing, and the production or fulfilment flow sits between the two. One offer routinely becomes several invoices — a deposit and a final, or one per milestone — or none at all, so this may be called more than once and the offer is never mutated. The prices and terms the customer accepted are copied over exactly. VAT is re-rated at creation against the invoice date and the buyer, so a rate that has moved or a VAT-ID validated since the offer was written is reflected on the invoice.

Authorizations:
BearerAuth
path Parameters
offer_id
required
string
Example: off_a1b2c3d4e5f6
Request Body schema: application/json
optional
line_item_ids
Array of strings

Bill only these positions. Omitted, the whole offer is billed.

type_id
string

Raise the invoice under this invoice type. Omitted, the tenant's default invoice type is used. Must be a type of entity class invoice.

Responses

Response Schema: application/json
_id
required
string
_class
required
string
_name
required
string (EntityName)

Read-only, backend-composed display label (the invoice number, or id while still a draft).

required
object

self is the invoice's own resource URL; documents and line_items are its sub-collections. Further rels may be present; a client follows the ones it knows.

invoice_number
required
string

Empty ("") while the invoice is in draft. Stamped by the per-tenant InvoiceNumberingSchema on the draft → finalized transition (resolution order: invoice type → company → tenant-default schema). Once assigned, the value is immutable. Cancellation from draft does NOT consume a number — the row keeps the empty string and relies on status="cancelled" as the audit signal.

required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

status
required
string
Enum: "draft" "finalized" "paid" "overdue" "cancelled"
issue_date
required
string <date>
due_date
required
string <date>
document_type_code
required
string
Enum: "380" "381" "384" "386" "326"
currency
required
string
required
Reference (object) or null

The offer this invoice was raised from, null if none. One offer can produce several invoices — a deposit and a final, or one per milestone — so the link lives here and never as a list on the offer.

payment_means_code
required
string
required
Array of objects (InvoiceLineItemResponse)
required
object (MonetaryAmount)

EN 16931 BT-109 — sum of line nets, as a MonetaryAmount carrying the invoice's currency (BT-5). A draft whose totals haven't been computed yet returns the identity element ({"amount": 0, "currency": null}).

required
object (MonetaryAmount)

EN 16931 BT-110.

required
object (MonetaryAmount)

EN 16931 BT-112. BT-115 (payable_amount) equals this until prepaid / rounding ships.

payment_status
required
string
Enum: "unpaid" "partially_paid" "paid"

How much of the invoice has been settled — a second axis, derived from the recorded allocations and never accepted on input. It is deliberately separate from status: an invoice can be overdue and partially_paid at once, and folding the two together would give status a second writer alongside the dunning worker. Record a payment to change it; PATCH {status: "paid"} is rejected with 422.

required
object (MonetaryAmount)

Cash received against this invoice — the sum of its non-voided allocations. Backend-computed. Always carries the invoice currency, even at zero.

required
object (MonetaryAmount)

The part of the invoice its payments deliberately did not cover — withholding tax the customer remitted on the seller's behalf, an early-payment discount, bank charges. Backend-computed from the allocations' typed deductions.

required
object (MonetaryAmount)

tax_inclusiveamount_paidamount_written_off: what the invoice still owes. Computed on the wire, never stored. Withheld tax counts as settled because it is money the seller is no longer owed, even though it never reached the bank.

allocation_version
required
integer

Optimistic-lock counter for the allocation transaction. Read-only; exposed so a client can tell two allocation states apart.

clearance_status
required
string
Enum: "not_required" "pending" "cleared" "rejected"

Whether the tax authority has cleared this invoice — the third axis, beside status (lifecycle) and payment_status (settlement).

In a clearance market (Kenya, Nigeria, India, Brazil) the document is not a valid invoice until the authority mints its artefact, so pending and rejected both block sending. not_required is the default and covers every tenant not in such a market; whether a seller is in scope derives from the seller country, never from a field a user sets.

Deliberately not a sixth status value: that would give status a second unlocked writer racing the dunning worker.

clearance_scheme
required
string or null
Enum: "ke_kra_etims" "ng_firs_mbs" "in_gstn_irp" "br_sefaz_nfe" null

Which fiscal-clearance regime engaged, as a code — <country>_<authority>_<regime> in lower snake case.

Null whenever clearance_status is not_required, which is every invoice outside a clearance market. Set alongside pending, cleared and rejected: a rejected invoice still names the regime that refused it, because that is what makes the error actionable.

Read-only, and written only by a clearance adapter through POST /resource/v1/invoices/{id}/clearance. Never derived from the seller's country — clearance is a property of the clearing act, and a seller trading into two clearance markets would resolve to the wrong authority on an immutable document.

clearance_reference
required
string or null

The identity the authority minted — Nigeria's IRN, Kenya's Control Unit Invoice Number, India's IRN. Null until cleared.

clearance_stamp
required
string or null

Cryptographic signature where the authority issues one (Nigeria's CSID). Null where the regime issues none, and until cleared.

clearance_qr
required
string or null

QR payload the human-readable document must carry. Null until cleared.

cleared_at
required
integer or null

Unix seconds at which clearance was recorded. Null until cleared.

clearance_error
required
string or null

Why the authority refused, when clearance_status is rejected. Null otherwise.

required
Array of objects (InvoiceVatBreakdownLine)

Per-VAT-rate breakdown (UStG §14). Server-computed by recompute_totals(); read-only on the API. The renderer prefers this persisted list over recomputation.

vat_aggregation
required
string
Enum: "horizontal" "vertical"

How per-line VAT rolls up into the invoice totals. vertical computes VAT once per rate band on the aggregated net (the B2B norm); horizontal sums the VAT shown on each line. The two can differ by a rounding cent. Chosen by the caller on create or on a draft update; omitted, it is the default_vat_aggregation of the invoice type. Frozen once the invoice leaves draft.

language
required
string

The language the invoice is written in. Snapshotted from the business partner when the draft is created, refreshed on every draft save and frozen once the invoice leaves draft; falls back to the tenant's default language when the partner has none. Always present. Read-only: it is not accepted on input.

notes
required
Array of strings

EN 16931 BT-22 invoice notes (0..n).

type_id
required
string

The tenant type this invoice was created under.

required
object
required
object

Snapshotted address per slot the invoice type declared. Slot keys are a subset of the type's address_slots (invoice | shipping | service). Empty object when no slot is configured for the invoice type or the partner has no eligible address.

created_at
required
integer
updated_at
required
integer
required
object (ReferenceValue)

Stored value for a field of type "reference"

required
object (ReferenceValue)

Stored value for a field of type "reference"

required
Array of objects (IdentifierResponse)

The numbers this record carries (ISBN, GTIN, VAT ID, ...). Stored on the record itself; the identifiers sub-resource addresses one entry.

ReferenceValue (object) or null

Issuing Company (BG-7 Seller). Snapshotted onto the immutable seller_* columns at draft → finalized. Null until the auto-default resolves (Phase D seed guarantees a value for new tenants).

Reference (object) or null

Sales channel selected for this invoice, null if none.

dunning_level
integer

Dunning escalation level (0 = none, 1..N = reminder ladder step).

InvoicePaymentTerms (object) or null

Structured BT-20 sub-block. null when unset.

Request samples

Content type
application/json
{
  • "line_item_ids": [
    ],
  • "type_id": "standard_invoice"
}

Response samples

Content type
application/json
{
  • "identifiers": [
    ],
  • "_id": "inv_a1b2c3d4e5f6",
  • "_class": "invoice",
  • "_name": "Acme Corp",
  • "_links": {
    },
  • "invoice_number": "2026-R-0042",
  • "business_partner": {
    },
  • "company": {
    },
  • "channel": {
    },
  • "offer": {
    },
  • "status": "draft",
  • "issue_date": "2026-05-01",
  • "due_date": "2026-05-31",
  • "dunning_level": 0,
  • "document_type_code": "380",
  • "currency": "EUR",
  • "payment_means_code": "30",
  • "line_items": [
    ],
  • "tax_exclusive": {
    },
  • "tax_total": {
    },
  • "tax_inclusive": {
    },
  • "payment_status": "unpaid",
  • "amount_paid": {
    },
  • "amount_written_off": {
    },
  • "amount_due": {
    },
  • "allocation_version": 0,
  • "clearance_status": "not_required",
  • "language": "de",
  • "clearance_scheme": "ke_kra_etims",
  • "clearance_reference": "IRN-2026-000123",
  • "clearance_stamp": "string",
  • "clearance_qr": "string",
  • "cleared_at": 0,
  • "clearance_error": "string",
  • "vat_breakdown": [
    ],
  • "vat_aggregation": "horizontal",
  • "notes": [
    ],
  • "payment_terms": {
    },
  • "type_id": "string",
  • "data": { },
  • "addresses": {
    },
  • "created_at": 1746144000,
  • "updated_at": 1746144000,
  • "created_by": {
    },
  • "tenant": {
    }
}

Get offer by ID

Authorizations:
BearerAuth
path Parameters
offer_id
required
string
Example: off_a1b2c3d4e5f6

Responses

Response Schema: application/json
_id
required
string
_class
required
string
_name
required
string (EntityName)

Read-only, backend-composed display label (the offer number, or id while still a draft).

required
object

self is the offer's own resource URL. Its documents live under /resource/v1/offers/{id}/documents. Further rels may be present; a client follows the ones it knows.

required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

status
required
string
Enum: "draft" "finalized" "sent" "accepted" "rejected" "expired" "superseded"
issue_date
required
string <date>
currency
required
string
required
Array of objects (OfferLineItemResponse)
required
object (MonetaryAmount)

EN 16931 BT-109 — sum of line nets, as a MonetaryAmount carrying the offer's currency (BT-5). A draft whose totals haven't been computed yet returns the identity element ({"amount": 0, "currency": null}).

required
object (MonetaryAmount)

EN 16931 BT-110.

required
object (MonetaryAmount)

EN 16931 BT-112. BT-115 (payable_amount) equals this until prepaid / rounding ships.

required
Array of objects (InvoiceVatBreakdownLine)

Per-VAT-rate breakdown (UStG §14). Server-computed by recompute_totals(); read-only on the API. The renderer prefers this persisted list over recomputation.

vat_aggregation
required
string
Enum: "horizontal" "vertical"

How per-line VAT rolls up into the offer totals. vertical computes VAT once per rate band on the aggregated net (the B2B norm); horizontal sums the VAT shown on each line. The two can differ by a rounding cent. Chosen by the caller on create or on a draft update; omitted, it is the default_vat_aggregation of the offer type.

language
required
string

The language the offer is written in. Snapshotted from the business partner when the draft is created, refreshed on every draft save and frozen once the offer leaves draft; falls back to the tenant's default language when the partner has none. Always present. Read-only: it is not accepted on input.

notes
required
Array of strings

EN 16931 BT-22 notes (0..n), printed on the offer.

type_id
required
string

The tenant type this offer was created under.

required
object
required
object

Snapshotted address per slot the offer type declared. Slot keys are a subset of the type's address_slots (invoice | shipping | service). Empty object when no slot is configured for the type or the partner has no eligible address.

created_at
required
integer
updated_at
required
integer
required
object (ReferenceValue)

Stored value for a field of type "reference"

required
object (ReferenceValue)

Stored value for a field of type "reference"

offer_number
required
string

Empty ("") until the offer is finalized. Offer numbers are not gap-free.

valid_until
required
string or null <date>
required
Reference (object) or null

The offer this one replaces, when it is a revision.

accepted_at
required
integer or null

Unix seconds.

rejected_at
required
integer or null

Unix seconds.

required
Array of objects (IdentifierResponse)

The numbers this record carries (ISBN, GTIN, VAT ID, ...), stored on the record itself.

ReferenceValue (object) or null

Issuing Company (BG-7 Seller). Snapshotted onto the immutable seller_* columns at draft → finalized. Null until the auto-default resolves (Phase D seed guarantees a value for new tenants).

Reference (object) or null

Sales channel selected for this offer, null if none.

InvoicePaymentTerms (object) or null

Structured BT-20 sub-block. null when unset.

Response samples

Content type
application/json
{
  • "identifiers": [
    ],
  • "_id": "off_a1b2c3d4e5f6",
  • "_class": "offer",
  • "_name": "Acme Corp",
  • "_links": {
    },
  • "business_partner": {
    },
  • "company": {
    },
  • "channel": {
    },
  • "status": "draft",
  • "issue_date": "2026-05-01",
  • "currency": "EUR",
  • "line_items": [
    ],
  • "tax_exclusive": {
    },
  • "tax_total": {
    },
  • "tax_inclusive": {
    },
  • "language": "de",
  • "vat_breakdown": [
    ],
  • "vat_aggregation": "horizontal",
  • "notes": [
    ],
  • "payment_terms": {
    },
  • "type_id": "string",
  • "data": { },
  • "addresses": {
    },
  • "created_at": 1746144000,
  • "updated_at": 1746144000,
  • "created_by": {
    },
  • "tenant": {
    },
  • "offer_number": "AN-2026-0001",
  • "valid_until": "2019-08-24",
  • "supersedes": {
    },
  • "accepted_at": 0,
  • "rejected_at": 0
}

Update offer fields and/or status

Fields may be changed only while the offer is a draft — once finalized, the customer holds that document. Moving the status to finalized stamps the offer number in the same write, and asks for the offer PDF: the document row is created afterwards and rendered in the background, so it is not on the response and a failure there does not undo the finalize. sent only records that the finalized offer went out. rejected back to sent reopens a declined offer: it keeps the number and the document and only clears rejected_at.

Authorizations:
BearerAuth
path Parameters
offer_id
required
string
Example: off_a1b2c3d4e5f6
Request Body schema: application/json
required
Reference (object) or null
ReferenceValue (object) or null

Re-pick the issuing Company. Validated against the tenant's per-doc-type allowed list. Only editable while the offer is in draft. Per the no-bare-id rule the wire shape is the full Reference object.

Reference (object) or null

Re-pick the sales channel, or null to clear. Only editable while the offer is in draft.

issue_date
string <date>
currency
string = 3 characters
buyer_reference
string
purchase_order_reference
string
contract_reference
string
project_reference
string
subject
string or null
object (InvoicePeriod)

Invoice-level invoicing period (EN 16931 BG-14).

buyer_name
string
buyer_legal_registration_id
string
buyer_identifier
string
buyer_customer_number
string or null
buyer_vat_id
string
buyer_vat_id_type
string

EN 16931 VAT scheme of buyer_vat_id — the identifier registry schema_key (e.g. vat_eu, vat_gb).

object (InvoicePartyContact)

Contact group (EN 16931 BG-6 / BG-9).

object (InvoiceElectronicAddress)

Electronic address (EN 16931 BT-34 / BT-49). scheme_id follows the EAS code list: EM = email, 9930 = German VAT, 0088 = GLN, etc.

object (InvoicePayee)

Third-party payee (EN 16931 BG-10). Populated only when the party receiving payment differs from the seller (e.g. factoring).

payment_terms_text
string
InvoicePaymentTerms (object) or null
remittance_information
string
Array of objects (InvoiceLineItemCreate)
notes
Array of strings or null

Replaces the persisted list. null / omitted = no change; [] clears the field.

status
string
Enum: "draft" "finalized" "sent" "accepted" "rejected" "expired" "superseded"

Moving to finalized stamps the offer number and asks for the PDF, which is rendered in the background — poll /offers/{offer_id}/documents for it. sent only records that the finalized offer went out; a finalized offer may also be answered without it. A rejected offer may go back to sent — reopening keeps the number and the document. expired and superseded are terminal.

vat_aggregation
string
Enum: "horizontal" "vertical"

How per-line VAT rolls up into the offer totals. vertical computes VAT once per rate band on the aggregated net (the B2B norm); horizontal sums the VAT shown on each line. Optional: omitted, the offer takes the default_vat_aggregation of its offer type.

type_id
string or null
schema_version
string or null
object
object
valid_until
string <date>

Last day the quoted price stands. Defaults to issue_date + the tenant's offer.default_validity_days.

Responses

Response Schema: application/json
_id
required
string
_class
required
string
_name
required
string (EntityName)

Read-only, backend-composed display label (the offer number, or id while still a draft).

required
object

self is the offer's own resource URL. Its documents live under /resource/v1/offers/{id}/documents. Further rels may be present; a client follows the ones it knows.

required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

status
required
string
Enum: "draft" "finalized" "sent" "accepted" "rejected" "expired" "superseded"
issue_date
required
string <date>
currency
required
string
required
Array of objects (OfferLineItemResponse)
required
object (MonetaryAmount)

EN 16931 BT-109 — sum of line nets, as a MonetaryAmount carrying the offer's currency (BT-5). A draft whose totals haven't been computed yet returns the identity element ({"amount": 0, "currency": null}).

required
object (MonetaryAmount)

EN 16931 BT-110.

required
object (MonetaryAmount)

EN 16931 BT-112. BT-115 (payable_amount) equals this until prepaid / rounding ships.

required
Array of objects (InvoiceVatBreakdownLine)

Per-VAT-rate breakdown (UStG §14). Server-computed by recompute_totals(); read-only on the API. The renderer prefers this persisted list over recomputation.

vat_aggregation
required
string
Enum: "horizontal" "vertical"

How per-line VAT rolls up into the offer totals. vertical computes VAT once per rate band on the aggregated net (the B2B norm); horizontal sums the VAT shown on each line. The two can differ by a rounding cent. Chosen by the caller on create or on a draft update; omitted, it is the default_vat_aggregation of the offer type.

language
required
string

The language the offer is written in. Snapshotted from the business partner when the draft is created, refreshed on every draft save and frozen once the offer leaves draft; falls back to the tenant's default language when the partner has none. Always present. Read-only: it is not accepted on input.

notes
required
Array of strings

EN 16931 BT-22 notes (0..n), printed on the offer.

type_id
required
string

The tenant type this offer was created under.

required
object
required
object

Snapshotted address per slot the offer type declared. Slot keys are a subset of the type's address_slots (invoice | shipping | service). Empty object when no slot is configured for the type or the partner has no eligible address.

created_at
required
integer
updated_at
required
integer
required
object (ReferenceValue)

Stored value for a field of type "reference"

required
object (ReferenceValue)

Stored value for a field of type "reference"

offer_number
required
string

Empty ("") until the offer is finalized. Offer numbers are not gap-free.

valid_until
required
string or null <date>
required
Reference (object) or null

The offer this one replaces, when it is a revision.

accepted_at
required
integer or null

Unix seconds.

rejected_at
required
integer or null

Unix seconds.

required
Array of objects (IdentifierResponse)

The numbers this record carries (ISBN, GTIN, VAT ID, ...), stored on the record itself.

ReferenceValue (object) or null

Issuing Company (BG-7 Seller). Snapshotted onto the immutable seller_* columns at draft → finalized. Null until the auto-default resolves (Phase D seed guarantees a value for new tenants).

Reference (object) or null

Sales channel selected for this offer, null if none.

InvoicePaymentTerms (object) or null

Structured BT-20 sub-block. null when unset.

Request samples

Content type
application/json
{
  • "business_partner": {
    },
  • "company": {
    },
  • "channel": {
    },
  • "issue_date": "2019-08-24",
  • "currency": "str",
  • "buyer_reference": "string",
  • "purchase_order_reference": "string",
  • "contract_reference": "string",
  • "project_reference": "string",
  • "subject": "string",
  • "invoicing_period": {
    },
  • "buyer_name": "string",
  • "buyer_legal_registration_id": "string",
  • "buyer_identifier": "string",
  • "buyer_customer_number": "string",
  • "buyer_vat_id": "string",
  • "buyer_vat_id_type": "string",
  • "buyer_contact": {
    },
  • "buyer_electronic_address": {
    },
  • "payee": {
    },
  • "payment_terms_text": "string",
  • "payment_terms": {
    },
  • "remittance_information": "string",
  • "line_items": [
    ],
  • "notes": [
    ],
  • "status": "draft",
  • "vat_aggregation": "horizontal",
  • "type_id": "string",
  • "schema_version": "string",
  • "data": { },
  • "addresses": {
    },
  • "valid_until": "2019-08-24"
}

Response samples

Content type
application/json
{
  • "identifiers": [
    ],
  • "_id": "off_a1b2c3d4e5f6",
  • "_class": "offer",
  • "_name": "Acme Corp",
  • "_links": {
    },
  • "business_partner": {
    },
  • "company": {
    },
  • "channel": {
    },
  • "status": "draft",
  • "issue_date": "2026-05-01",
  • "currency": "EUR",
  • "line_items": [
    ],
  • "tax_exclusive": {
    },
  • "tax_total": {
    },
  • "tax_inclusive": {
    },
  • "language": "de",
  • "vat_breakdown": [
    ],
  • "vat_aggregation": "horizontal",
  • "notes": [
    ],
  • "payment_terms": {
    },
  • "type_id": "string",
  • "data": { },
  • "addresses": {
    },
  • "created_at": 1746144000,
  • "updated_at": 1746144000,
  • "created_by": {
    },
  • "tenant": {
    },
  • "offer_number": "AN-2026-0001",
  • "valid_until": "2019-08-24",
  • "supersedes": {
    },
  • "accepted_at": 0,
  • "rejected_at": 0
}

Partially update offer fields and/or status

Authorizations:
BearerAuth
path Parameters
offer_id
required
string
Example: off_a1b2c3d4e5f6
Request Body schema: application/json
required
Reference (object) or null
ReferenceValue (object) or null

Re-pick the issuing Company. Validated against the tenant's per-doc-type allowed list. Only editable while the offer is in draft. Per the no-bare-id rule the wire shape is the full Reference object.

Reference (object) or null

Re-pick the sales channel, or null to clear. Only editable while the offer is in draft.

issue_date
string <date>
currency
string = 3 characters
buyer_reference
string
purchase_order_reference
string
contract_reference
string
project_reference
string
subject
string or null
object (InvoicePeriod)

Invoice-level invoicing period (EN 16931 BG-14).

buyer_name
string
buyer_legal_registration_id
string
buyer_identifier
string
buyer_customer_number
string or null
buyer_vat_id
string
buyer_vat_id_type
string

EN 16931 VAT scheme of buyer_vat_id — the identifier registry schema_key (e.g. vat_eu, vat_gb).

object (InvoicePartyContact)

Contact group (EN 16931 BG-6 / BG-9).

object (InvoiceElectronicAddress)

Electronic address (EN 16931 BT-34 / BT-49). scheme_id follows the EAS code list: EM = email, 9930 = German VAT, 0088 = GLN, etc.

object (InvoicePayee)

Third-party payee (EN 16931 BG-10). Populated only when the party receiving payment differs from the seller (e.g. factoring).

payment_terms_text
string
InvoicePaymentTerms (object) or null
remittance_information
string
Array of objects (InvoiceLineItemCreate)
notes
Array of strings or null

Replaces the persisted list. null / omitted = no change; [] clears the field.

status
string
Enum: "draft" "finalized" "sent" "accepted" "rejected" "expired" "superseded"

Moving to finalized stamps the offer number and asks for the PDF, which is rendered in the background — poll /offers/{offer_id}/documents for it. sent only records that the finalized offer went out; a finalized offer may also be answered without it. A rejected offer may go back to sent — reopening keeps the number and the document. expired and superseded are terminal.

vat_aggregation
string
Enum: "horizontal" "vertical"

How per-line VAT rolls up into the offer totals. vertical computes VAT once per rate band on the aggregated net (the B2B norm); horizontal sums the VAT shown on each line. Optional: omitted, the offer takes the default_vat_aggregation of its offer type.

type_id
string or null
schema_version
string or null
object
object
valid_until
string <date>

Last day the quoted price stands. Defaults to issue_date + the tenant's offer.default_validity_days.

Responses

Response Schema: application/json
_id
required
string
_class
required
string
_name
required
string (EntityName)

Read-only, backend-composed display label (the offer number, or id while still a draft).

required
object

self is the offer's own resource URL. Its documents live under /resource/v1/offers/{id}/documents. Further rels may be present; a client follows the ones it knows.

required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

status
required
string
Enum: "draft" "finalized" "sent" "accepted" "rejected" "expired" "superseded"
issue_date
required
string <date>
currency
required
string
required
Array of objects (OfferLineItemResponse)
required
object (MonetaryAmount)

EN 16931 BT-109 — sum of line nets, as a MonetaryAmount carrying the offer's currency (BT-5). A draft whose totals haven't been computed yet returns the identity element ({"amount": 0, "currency": null}).

required
object (MonetaryAmount)

EN 16931 BT-110.

required
object (MonetaryAmount)

EN 16931 BT-112. BT-115 (payable_amount) equals this until prepaid / rounding ships.

required
Array of objects (InvoiceVatBreakdownLine)

Per-VAT-rate breakdown (UStG §14). Server-computed by recompute_totals(); read-only on the API. The renderer prefers this persisted list over recomputation.

vat_aggregation
required
string
Enum: "horizontal" "vertical"

How per-line VAT rolls up into the offer totals. vertical computes VAT once per rate band on the aggregated net (the B2B norm); horizontal sums the VAT shown on each line. The two can differ by a rounding cent. Chosen by the caller on create or on a draft update; omitted, it is the default_vat_aggregation of the offer type.

language
required
string

The language the offer is written in. Snapshotted from the business partner when the draft is created, refreshed on every draft save and frozen once the offer leaves draft; falls back to the tenant's default language when the partner has none. Always present. Read-only: it is not accepted on input.

notes
required
Array of strings

EN 16931 BT-22 notes (0..n), printed on the offer.

type_id
required
string

The tenant type this offer was created under.

required
object
required
object

Snapshotted address per slot the offer type declared. Slot keys are a subset of the type's address_slots (invoice | shipping | service). Empty object when no slot is configured for the type or the partner has no eligible address.

created_at
required
integer
updated_at
required
integer
required
object (ReferenceValue)

Stored value for a field of type "reference"

required
object (ReferenceValue)

Stored value for a field of type "reference"

offer_number
required
string

Empty ("") until the offer is finalized. Offer numbers are not gap-free.

valid_until
required
string or null <date>
required
Reference (object) or null

The offer this one replaces, when it is a revision.

accepted_at
required
integer or null

Unix seconds.

rejected_at
required
integer or null

Unix seconds.

required
Array of objects (IdentifierResponse)

The numbers this record carries (ISBN, GTIN, VAT ID, ...), stored on the record itself.

ReferenceValue (object) or null

Issuing Company (BG-7 Seller). Snapshotted onto the immutable seller_* columns at draft → finalized. Null until the auto-default resolves (Phase D seed guarantees a value for new tenants).

Reference (object) or null

Sales channel selected for this offer, null if none.

InvoicePaymentTerms (object) or null

Structured BT-20 sub-block. null when unset.

Request samples

Content type
application/json
{
  • "business_partner": {
    },
  • "company": {
    },
  • "channel": {
    },
  • "issue_date": "2019-08-24",
  • "currency": "str",
  • "buyer_reference": "string",
  • "purchase_order_reference": "string",
  • "contract_reference": "string",
  • "project_reference": "string",
  • "subject": "string",
  • "invoicing_period": {
    },
  • "buyer_name": "string",
  • "buyer_legal_registration_id": "string",
  • "buyer_identifier": "string",
  • "buyer_customer_number": "string",
  • "buyer_vat_id": "string",
  • "buyer_vat_id_type": "string",
  • "buyer_contact": {
    },
  • "buyer_electronic_address": {
    },
  • "payee": {
    },
  • "payment_terms_text": "string",
  • "payment_terms": {
    },
  • "remittance_information": "string",
  • "line_items": [
    ],
  • "notes": [
    ],
  • "status": "draft",
  • "vat_aggregation": "horizontal",
  • "type_id": "string",
  • "schema_version": "string",
  • "data": { },
  • "addresses": {
    },
  • "valid_until": "2019-08-24"
}

Response samples

Content type
application/json
{
  • "identifiers": [
    ],
  • "_id": "off_a1b2c3d4e5f6",
  • "_class": "offer",
  • "_name": "Acme Corp",
  • "_links": {
    },
  • "business_partner": {
    },
  • "company": {
    },
  • "channel": {
    },
  • "status": "draft",
  • "issue_date": "2026-05-01",
  • "currency": "EUR",
  • "line_items": [
    ],
  • "tax_exclusive": {
    },
  • "tax_total": {
    },
  • "tax_inclusive": {
    },
  • "language": "de",
  • "vat_breakdown": [
    ],
  • "vat_aggregation": "horizontal",
  • "notes": [
    ],
  • "payment_terms": {
    },
  • "type_id": "string",
  • "data": { },
  • "addresses": {
    },
  • "created_at": 1746144000,
  • "updated_at": 1746144000,
  • "created_by": {
    },
  • "tenant": {
    },
  • "offer_number": "AN-2026-0001",
  • "valid_until": "2019-08-24",
  • "supersedes": {
    },
  • "accepted_at": 0,
  • "rejected_at": 0
}

Delete a draft offer

Only a draft. A finalized offer is a document the customer holds — reject it or let it expire, which keeps the record of what was quoted.

Authorizations:
BearerAuth
path Parameters
offer_id
required
string
Example: off_a1b2c3d4e5f6

Responses

Response samples

Content type
application/json
{
  • "error": "not_found",
  • "message": "Business partner not found",
  • "details": { }
}

Payments

List the payments allocated to an invoice

Voided allocations are included: they are kept as history and shown struck through, not filtered out.

Authorizations:
BearerAuth
path Parameters
invoice_id
required
string
Example: inv_a1b2c3d4e5f6

Responses

Response Schema: application/json
required
Array of objects (InvoicePaymentResponse)
next_token
required
string or null

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_token": "string"
}

Record a payment against an invoice

This is what marks an invoice paid — PATCH {status: "paid"} is rejected. Supply payment to apply an existing payment, or new_payment to record and allocate in one transaction. Where the customer withheld tax or took a discount, pass the shortfall in deductions and the invoice still settles.

Rejections: invoice_payment.invoice_not_finalized (draft), invoice_payment.invoice_cancelled, invoice_payment.currency_mismatch, invoice_payment.exceeds_payment_remainder, invoice_payment.exceeds_open_balance — the excess deliberately stays unallocated on the payment as credit on account.

Authorizations:
BearerAuth
path Parameters
invoice_id
required
string
Example: inv_a1b2c3d4e5f6
Request Body schema: application/json
required
One of
required
Reference (object) or null

Reference to an existing payment. Mutually exclusive with new_payment.

object or null
object (MonetaryAmountInput)

Cash to apply. Omit to settle as much of the open balance as the payment's remainder covers, which is what the record-payment dialog wants. May not exceed either the invoice's amount_due or the payment's amount_unallocated.

Array of objects (PaymentDeduction)

Parts of the invoice this payment deliberately did not cover. The invoice settles on amount + deductions, so a Kenyan invoice of 100,000 paid as 95,000 cash plus a 5,000 withholding_tax deduction reaches paid.

dedupe_key
string or null [ 1 .. 128 ] characters

Optional. A second write with the same key within the tenant does not create a second row. The allocation already recorded is returned unchanged.

Responses

Response Schema: application/json
_id
required
string
_class
required
string
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

required
object (MonetaryAmount)

The cash applied — what the bank actually credited.

amount_deducted
required
number

Sum of this allocation's deductions, as a bare number in the invoice's currency (the figure is only meaningful alongside amount).

required
Array of objects (PaymentDeduction)
is_voided
required
boolean

Voided allocations are kept, not deleted, so "who un-paid this invoice, and why" stays answerable. They are excluded from every balance and are shown struck through.

voided_at
required
integer or null

Unix epoch seconds

void_reason
required
string or null
dedupe_key
required
string or null
invoice_number
required
string

The invoice's number as it stood when the money was applied. It duplicates invoice._name on purpose: an invoice is numbered in the same transaction that finalizes it and a draft cannot be allocated, so the value is immutable by the time this row exists — and carrying it here lets a list of allocations render the number a tenant recognises without reading an invoice per row.

Empty on rows written before this field existed; those fall back to reading the invoice.

required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

created_at
required
integer

Unix epoch seconds

updated_at
required
integer

Unix epoch seconds

required
Reference (object) or null
required
object
required
object (InvoiceTotalsAfterAllocation)

The invoice figures as recomputed by this write, echoed so no caller needs a follow-up GET — the record-payment dialog needs the new status and open balance the moment it closes.

Request samples

Content type
application/json
{
  • "payment": {
    },
  • "new_payment": {
    },
  • "amount": {
    },
  • "deductions": [
    ],
  • "dedupe_key": "string"
}

Response samples

Content type
application/json
{
  • "_id": "ipay_a1b2c3d4e5f6",
  • "_class": "invoice_payment",
  • "invoice": {
    },
  • "payment": {
    },
  • "amount": {
    },
  • "amount_deducted": 0,
  • "deductions": [
    ],
  • "is_voided": true,
  • "voided_at": 0,
  • "void_reason": "string",
  • "dedupe_key": "string",
  • "invoice_number": "string",
  • "tenant": {
    },
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    },
  • "_links": {
    },
  • "invoice_totals": {
    }
}

Void an allocation

Soft-void: the row is kept and excluded from every balance, so the audit trail survives. The invoice's balance walks back and its status reopens to finalized or overdue as its due date dictates. Deliberately not blocked on a dunned invoice — the commonest reason to unallocate is a payment matched to the wrong invoice, which is exactly the one that stopped being chased.

Authorizations:
BearerAuth
path Parameters
invoice_id
required
string
Example: inv_a1b2c3d4e5f6
invoice_payment_id
required
string
Example: ipay_a1b2c3d4e5f6
Request Body schema: application/json
optional
void_reason
string or null

Why the allocation was reversed. Recorded on the audit trail.

Responses

Response Schema: application/json
_id
required
string
_class
required
string
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

required
object (MonetaryAmount)

The cash applied — what the bank actually credited.

amount_deducted
required
number

Sum of this allocation's deductions, as a bare number in the invoice's currency (the figure is only meaningful alongside amount).

required
Array of objects (PaymentDeduction)
is_voided
required
boolean

Voided allocations are kept, not deleted, so "who un-paid this invoice, and why" stays answerable. They are excluded from every balance and are shown struck through.

voided_at
required
integer or null

Unix epoch seconds

void_reason
required
string or null
dedupe_key
required
string or null
invoice_number
required
string

The invoice's number as it stood when the money was applied. It duplicates invoice._name on purpose: an invoice is numbered in the same transaction that finalizes it and a draft cannot be allocated, so the value is immutable by the time this row exists — and carrying it here lets a list of allocations render the number a tenant recognises without reading an invoice per row.

Empty on rows written before this field existed; those fall back to reading the invoice.

required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

created_at
required
integer

Unix epoch seconds

updated_at
required
integer

Unix epoch seconds

required
Reference (object) or null
required
object
required
object (InvoiceTotalsAfterAllocation)

The invoice figures as recomputed by this write, echoed so no caller needs a follow-up GET — the record-payment dialog needs the new status and open balance the moment it closes.

Request samples

Content type
application/json
{
  • "void_reason": "string"
}

Response samples

Content type
application/json
{
  • "_id": "ipay_a1b2c3d4e5f6",
  • "_class": "invoice_payment",
  • "invoice": {
    },
  • "payment": {
    },
  • "amount": {
    },
  • "amount_deducted": 0,
  • "deductions": [
    ],
  • "is_voided": true,
  • "voided_at": 0,
  • "void_reason": "string",
  • "dedupe_key": "string",
  • "invoice_number": "string",
  • "tenant": {
    },
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    },
  • "_links": {
    },
  • "invoice_totals": {
    }
}

List where a payment's money went

The payment's own view of its allocations, which is what the allocation screen needs. Voided rows are included.

Authorizations:
BearerAuth
path Parameters
payment_id
required
string
Example: pay_a1b2c3d4e5f6

Responses

Response Schema: application/json
required
Array of objects (InvoicePaymentResponse)
next_token
required
string or null

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_token": "string"
}

Spread a payment across several invoices

Settles up to 50 invoices from one payment. Each row is its own transaction, applied in the order given, so the remainder is consumed top-down and one row's failure never rolls back the rows that succeeded.

Answers 200 even when some rows failedresults[] carries a per-row verdict with a machine-readable error_code, and allocated_count / failed_count say how the batch went. A 4xx over the whole request would deny allocations that really happened.

Request-level rejections (422): invoice_payment.no_allocations, invoice_payment.too_many_allocations.

Authorizations:
BearerAuth
path Parameters
payment_id
required
string
Example: pay_a1b2c3d4e5f6
Request Body schema: application/json
required
required
Array of objects (BulkAllocationRow) [ 1 .. 50 ] items

Responses

Response Schema: application/json
required
Array of objects (BulkAllocationResult)
allocated_count
required
integer
failed_count
required
integer
required
object (PaymentResponse)

The payment with its recomputed amount_allocated / amount_unallocated, so the caller needs no follow-up GET.

Request samples

Content type
application/json
{
  • "allocations": [
    ]
}

Response samples

Content type
application/json
{
  • "results": [
    ],
  • "allocated_count": 0,
  • "failed_count": 0,
  • "payment": {
    }
}

List the invoices a payment could settle

Every invoice with an open balance that can still receive money — non-draft, non-cancelled — oldest due date first, with the payment's own currency first. Invoices in another currency are returned flagged rather than dropped: allocation refuses them, but an invoice the tenant can see in the invoice list must not vanish here unexplained.

Authorizations:
BearerAuth
path Parameters
payment_id
required
string
Example: pay_a1b2c3d4e5f6
query Parameters
business_partner_id
string

Restrict candidates to one customer's invoices.

Responses

Response Schema: application/json
required
Array of objects (AllocationCandidate)
next_token
required
string or null

Always null — the open set is returned in one read.

truncated
required
boolean

True when the tenant has more open invoices than one read returns. A capped list presented as complete is how money gets allocated against a picture that is missing invoices, so the cap is stated rather than implied.

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_token": "string",
  • "truncated": true
}

List payments received by the authenticated tenant

Newest first by paid_at. Filters are applied after the page is read, so a page can come back short while next_token is still set — keep following the token until it is null.

Authorizations:
BearerAuth
query Parameters
limit
integer [ 1 .. 200 ]
Default: 50
next_token
string

Base64-encoded pagination cursor from previous response

business_partner_id
string

Only payments from this payer.

allocation_state
string (AllocationState)
Enum: "unallocated" "partially_allocated" "allocated"

Only payments in this allocation state. unallocated is the work queue — money received that no invoice claims yet.

method
string (PaymentMethod)
Enum: "bank_transfer" "bank_deposit" "cash" "card" "cheque" "mobile_money" "pix" "upi" "boleto" "other"

How the money arrived. This is metadata about a payment that already happened — Raccoon does not process card, mobile-money, Pix, UPI or boleto transactions. bank_deposit is cash or a cheque paid in over the counter (distinct from cash in the till and from a bank_transfer); each value maps to a UNTDID 4461 payment-means code for e-invoice export.

paid_at_from
string <date>

Inclusive lower bound on paid_at.

paid_at_to
string <date>

Inclusive upper bound on paid_at.

reference
string

Exact-match payment reference. Used to warn about a double-recorded payment; references are not unique, so this can legitimately return more than one row.

q
string non-empty

Full-text search term. When provided, the endpoint delegates to the shared search index (matches across name/title/email/identifier fields, tenant-scoped) and returns the matching entities in the same response shape as the unfiltered list. Pagination (limit, next_token) is not honoured in this mode; results are capped at the search index size limit (20 items in MVP).

Responses

Response Schema: application/json
required
Array of objects (PaymentResponse)
next_token
required
string or null

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_token": "string"
}

Record a payment received

Records money that arrived. It does not mark any invoice paid — allocate the payment to an invoice separately. Supplying dedupe_key makes the create idempotent: a repeat returns the payment already recorded rather than recording the money twice.

Authorizations:
BearerAuth
Request Body schema: application/json
required
required
object (MonetaryAmountInput)

The amount actually received, as it appears on the bank statement or payment confirmation. Must be greater than zero. Where the customer withheld tax, this is the net figure that arrived; the withheld part is recorded as a deduction on the allocation, not here.

method
string (PaymentMethod)
Default: "bank_transfer"
Enum: "bank_transfer" "bank_deposit" "cash" "card" "cheque" "mobile_money" "pix" "upi" "boleto" "other"

How the money arrived. This is metadata about a payment that already happened — Raccoon does not process card, mobile-money, Pix, UPI or boleto transactions. bank_deposit is cash or a cheque paid in over the counter (distinct from cash in the till and from a bank_transfer); each value maps to a UNTDID 4461 payment-means code for e-invoice export.

paid_at
string <date>

Date the money was received. Defaults to today (UTC).

reference
string or null <= 200 characters

The reference the payer's rail produced — an M-Pesa confirmation code, a UPI UTR, a Pix EndToEndId, a NIP session id, or a SEPA statement narrative. Free text: the formats share no structure across markets, so nothing is validated. Sized for the longest real value (the 140-character SEPA remittance field).

Reference (object) or null

Optional — who paid. A payment may exist with no payer set.

notes
string or null
dedupe_key
string or null [ 1 .. 64 ] characters

Optional. A second write with the same key within the tenant does not create a second row. The payment already recorded is returned unchanged.

Responses

Response Schema: application/json
_id
required
string
_class
required
string
_name
required
string (EntityName)

Backend-composed label. A payment has no name field, so this is the amount plus the rail's own reference — recognisable in a picker or an approval card. Read the amount field to render the figure properly.

required
object (MonetaryAmount)

A monetary value with an ISO 4217 currency code.

currency may be null to denote the identity ("zero") element{"amount": 0, "currency": null}. The identity absorbs into arithmetic (identity + x = x, x - x = identity, identity * n = identity); any non-zero amount must carry a currency. This is the wire form returned for an invoice draft whose totals haven't been computed yet.

Precision contract

amount is emitted as a JSON number carrying the server's Decimal with full precision — the server writes the Decimal's exact textual form as a raw JSON number token, bypassing any IEEE-754 round-trip. Sub-cent values such as 0.0003 USD (audiobook-streaming royalty rates, FX conversions, regulated reference prices) come over the wire exactly.

Client-side parsing

Standard JSON parsers convert JSON numbers to IEEE-754 floats, which lose precision for arithmetic on decimals that aren't power-of-10 fractions. Pick the right tool for the job:

  • Display only — the default float is fine. Both Python's repr(0.0003) and JavaScript's (0.0003).toString() return "0.0003", so user-visible numbers render correctly.
  • Exact arithmetic (summing line items, computing tax) — parse with a Decimal / BigDecimal type. Python: json.loads(text, parse_float=Decimal). JavaScript: a BigDecimal library such as big.js or decimal.js.

Client-side encoding for sub-cent values

A client that produces a sub-cent amount with json.dumps (Python) or JSON.stringify (JS) will also lose precision before the bytes leave the process — both encoders convert Decimal/BigDecimal to float first. Two paths preserve precision on the way in:

  1. Send the amount as a quoted decimal string. Request bodies schema this as MonetaryAmountInput, whose amount is a oneOf over number and string; the server's Pydantic coerces the string to an exact Decimal. (MonetaryAmount itself is the response shape and stays type: number.)
  2. Or use a JSON encoder on the client that supports raw number tokens (same trick as our server's encoder).

For everyday cent-precision values, sending a JSON number is fine.

required
object (MonetaryAmount)

Backend-computed total applied to invoices. Maintained by the allocation transaction; never accepted on input.

required
object (MonetaryAmount)

Backend-computed remainder (amountamount_allocated) — credit on account. Unlike a bare zero MonetaryAmount, this always carries the payment's currency, so a fully-allocated payment reads {"amount": 0, "currency": "KES"} rather than a null currency.

allocation_state
required
string (AllocationState)
Enum: "unallocated" "partially_allocated" "allocated"

How much of the payment has been applied to invoices. unallocated money is real money received that no invoice claims yet — credit on account against the payer.

allocation_version
required
integer

Optimistic-lock counter for the allocation transaction. Read-only; exposed so a client can tell two allocation states apart.

method
required
string (PaymentMethod)
Enum: "bank_transfer" "bank_deposit" "cash" "card" "cheque" "mobile_money" "pix" "upi" "boleto" "other"

How the money arrived. This is metadata about a payment that already happened — Raccoon does not process card, mobile-money, Pix, UPI or boleto transactions. bank_deposit is cash or a cheque paid in over the counter (distinct from cash in the till and from a bank_transfer); each value maps to a UNTDID 4461 payment-means code for e-invoice export.

paid_at
required
string <date>
reference
required
string or null
notes
required
string or null
dedupe_key
required
string or null
required
Reference (object) or null
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

created_at
required
integer

Unix epoch seconds

updated_at
required
integer

Unix epoch seconds

required
Reference (object) or null
required
object

self is the payment's own URL.

Request samples

Content type
application/json
{
  • "amount": {
    },
  • "method": "bank_transfer",
  • "paid_at": "2019-08-24",
  • "reference": "QGH7K3MNOP",
  • "business_partner": {
    },
  • "notes": "string",
  • "dedupe_key": "string"
}

Response samples

Content type
application/json
{
  • "_id": "pay_a1b2c3d4e5f6",
  • "_class": "payment",
  • "_name": "Acme Corp",
  • "amount": {
    },
  • "amount_allocated": {
    },
  • "amount_unallocated": {
    },
  • "allocation_state": "unallocated",
  • "allocation_version": 0,
  • "method": "bank_transfer",
  • "paid_at": "2019-08-24",
  • "reference": "string",
  • "notes": "string",
  • "dedupe_key": "string",
  • "business_partner": {
    },
  • "tenant": {
    },
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    },
  • "_links": {
    }
}

Get a payment

Authorizations:
BearerAuth
path Parameters
payment_id
required
string
Example: pay_a1b2c3d4e5f6

Responses

Response Schema: application/json
_id
required
string
_class
required
string
_name
required
string (EntityName)

Backend-composed label. A payment has no name field, so this is the amount plus the rail's own reference — recognisable in a picker or an approval card. Read the amount field to render the figure properly.

required
object (MonetaryAmount)

A monetary value with an ISO 4217 currency code.

currency may be null to denote the identity ("zero") element{"amount": 0, "currency": null}. The identity absorbs into arithmetic (identity + x = x, x - x = identity, identity * n = identity); any non-zero amount must carry a currency. This is the wire form returned for an invoice draft whose totals haven't been computed yet.

Precision contract

amount is emitted as a JSON number carrying the server's Decimal with full precision — the server writes the Decimal's exact textual form as a raw JSON number token, bypassing any IEEE-754 round-trip. Sub-cent values such as 0.0003 USD (audiobook-streaming royalty rates, FX conversions, regulated reference prices) come over the wire exactly.

Client-side parsing

Standard JSON parsers convert JSON numbers to IEEE-754 floats, which lose precision for arithmetic on decimals that aren't power-of-10 fractions. Pick the right tool for the job:

  • Display only — the default float is fine. Both Python's repr(0.0003) and JavaScript's (0.0003).toString() return "0.0003", so user-visible numbers render correctly.
  • Exact arithmetic (summing line items, computing tax) — parse with a Decimal / BigDecimal type. Python: json.loads(text, parse_float=Decimal). JavaScript: a BigDecimal library such as big.js or decimal.js.

Client-side encoding for sub-cent values

A client that produces a sub-cent amount with json.dumps (Python) or JSON.stringify (JS) will also lose precision before the bytes leave the process — both encoders convert Decimal/BigDecimal to float first. Two paths preserve precision on the way in:

  1. Send the amount as a quoted decimal string. Request bodies schema this as MonetaryAmountInput, whose amount is a oneOf over number and string; the server's Pydantic coerces the string to an exact Decimal. (MonetaryAmount itself is the response shape and stays type: number.)
  2. Or use a JSON encoder on the client that supports raw number tokens (same trick as our server's encoder).

For everyday cent-precision values, sending a JSON number is fine.

required
object (MonetaryAmount)

Backend-computed total applied to invoices. Maintained by the allocation transaction; never accepted on input.

required
object (MonetaryAmount)

Backend-computed remainder (amountamount_allocated) — credit on account. Unlike a bare zero MonetaryAmount, this always carries the payment's currency, so a fully-allocated payment reads {"amount": 0, "currency": "KES"} rather than a null currency.

allocation_state
required
string (AllocationState)
Enum: "unallocated" "partially_allocated" "allocated"

How much of the payment has been applied to invoices. unallocated money is real money received that no invoice claims yet — credit on account against the payer.

allocation_version
required
integer

Optimistic-lock counter for the allocation transaction. Read-only; exposed so a client can tell two allocation states apart.

method
required
string (PaymentMethod)
Enum: "bank_transfer" "bank_deposit" "cash" "card" "cheque" "mobile_money" "pix" "upi" "boleto" "other"

How the money arrived. This is metadata about a payment that already happened — Raccoon does not process card, mobile-money, Pix, UPI or boleto transactions. bank_deposit is cash or a cheque paid in over the counter (distinct from cash in the till and from a bank_transfer); each value maps to a UNTDID 4461 payment-means code for e-invoice export.

paid_at
required
string <date>
reference
required
string or null
notes
required
string or null
dedupe_key
required
string or null
required
Reference (object) or null
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

created_at
required
integer

Unix epoch seconds

updated_at
required
integer

Unix epoch seconds

required
Reference (object) or null
required
object

self is the payment's own URL.

Response samples

Content type
application/json
{
  • "_id": "pay_a1b2c3d4e5f6",
  • "_class": "payment",
  • "_name": "Acme Corp",
  • "amount": {
    },
  • "amount_allocated": {
    },
  • "amount_unallocated": {
    },
  • "allocation_state": "unallocated",
  • "allocation_version": 0,
  • "method": "bank_transfer",
  • "paid_at": "2019-08-24",
  • "reference": "string",
  • "notes": "string",
  • "dedupe_key": "string",
  • "business_partner": {
    },
  • "tenant": {
    },
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    },
  • "_links": {
    }
}

Update a payment (alias of PATCH — only supplied fields change)

Authorizations:
BearerAuth
path Parameters
payment_id
required
string
Example: pay_a1b2c3d4e5f6
Request Body schema: application/json
required
object (MonetaryAmountInput)

Request-side counterpart of MonetaryAmount. Identical, except that amount also accepts a quoted decimal string — the encoding the precision contract above tells clients to use for sub-cent values, which the server's Pydantic coerces to an exact Decimal.

Responses always carry amount as a JSON number; use MonetaryAmount for those.

method
string (PaymentMethod)
Enum: "bank_transfer" "bank_deposit" "cash" "card" "cheque" "mobile_money" "pix" "upi" "boleto" "other"

How the money arrived. This is metadata about a payment that already happened — Raccoon does not process card, mobile-money, Pix, UPI or boleto transactions. bank_deposit is cash or a cheque paid in over the counter (distinct from cash in the till and from a bank_transfer); each value maps to a UNTDID 4461 payment-means code for e-invoice export.

paid_at
string <date>
reference
string or null <= 200 characters
Reference (object) or null
notes
string or null

Responses

Response Schema: application/json
_id
required
string
_class
required
string
_name
required
string (EntityName)

Backend-composed label. A payment has no name field, so this is the amount plus the rail's own reference — recognisable in a picker or an approval card. Read the amount field to render the figure properly.

required
object (MonetaryAmount)

A monetary value with an ISO 4217 currency code.

currency may be null to denote the identity ("zero") element{"amount": 0, "currency": null}. The identity absorbs into arithmetic (identity + x = x, x - x = identity, identity * n = identity); any non-zero amount must carry a currency. This is the wire form returned for an invoice draft whose totals haven't been computed yet.

Precision contract

amount is emitted as a JSON number carrying the server's Decimal with full precision — the server writes the Decimal's exact textual form as a raw JSON number token, bypassing any IEEE-754 round-trip. Sub-cent values such as 0.0003 USD (audiobook-streaming royalty rates, FX conversions, regulated reference prices) come over the wire exactly.

Client-side parsing

Standard JSON parsers convert JSON numbers to IEEE-754 floats, which lose precision for arithmetic on decimals that aren't power-of-10 fractions. Pick the right tool for the job:

  • Display only — the default float is fine. Both Python's repr(0.0003) and JavaScript's (0.0003).toString() return "0.0003", so user-visible numbers render correctly.
  • Exact arithmetic (summing line items, computing tax) — parse with a Decimal / BigDecimal type. Python: json.loads(text, parse_float=Decimal). JavaScript: a BigDecimal library such as big.js or decimal.js.

Client-side encoding for sub-cent values

A client that produces a sub-cent amount with json.dumps (Python) or JSON.stringify (JS) will also lose precision before the bytes leave the process — both encoders convert Decimal/BigDecimal to float first. Two paths preserve precision on the way in:

  1. Send the amount as a quoted decimal string. Request bodies schema this as MonetaryAmountInput, whose amount is a oneOf over number and string; the server's Pydantic coerces the string to an exact Decimal. (MonetaryAmount itself is the response shape and stays type: number.)
  2. Or use a JSON encoder on the client that supports raw number tokens (same trick as our server's encoder).

For everyday cent-precision values, sending a JSON number is fine.

required
object (MonetaryAmount)

Backend-computed total applied to invoices. Maintained by the allocation transaction; never accepted on input.

required
object (MonetaryAmount)

Backend-computed remainder (amountamount_allocated) — credit on account. Unlike a bare zero MonetaryAmount, this always carries the payment's currency, so a fully-allocated payment reads {"amount": 0, "currency": "KES"} rather than a null currency.

allocation_state
required
string (AllocationState)
Enum: "unallocated" "partially_allocated" "allocated"

How much of the payment has been applied to invoices. unallocated money is real money received that no invoice claims yet — credit on account against the payer.

allocation_version
required
integer

Optimistic-lock counter for the allocation transaction. Read-only; exposed so a client can tell two allocation states apart.

method
required
string (PaymentMethod)
Enum: "bank_transfer" "bank_deposit" "cash" "card" "cheque" "mobile_money" "pix" "upi" "boleto" "other"

How the money arrived. This is metadata about a payment that already happened — Raccoon does not process card, mobile-money, Pix, UPI or boleto transactions. bank_deposit is cash or a cheque paid in over the counter (distinct from cash in the till and from a bank_transfer); each value maps to a UNTDID 4461 payment-means code for e-invoice export.

paid_at
required
string <date>
reference
required
string or null
notes
required
string or null
dedupe_key
required
string or null
required
Reference (object) or null
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

created_at
required
integer

Unix epoch seconds

updated_at
required
integer

Unix epoch seconds

required
Reference (object) or null
required
object

self is the payment's own URL.

Request samples

Content type
application/json
{
  • "amount": {
    },
  • "method": "bank_transfer",
  • "paid_at": "2019-08-24",
  • "reference": "string",
  • "business_partner": {
    },
  • "notes": "string"
}

Response samples

Content type
application/json
{
  • "_id": "pay_a1b2c3d4e5f6",
  • "_class": "payment",
  • "_name": "Acme Corp",
  • "amount": {
    },
  • "amount_allocated": {
    },
  • "amount_unallocated": {
    },
  • "allocation_state": "unallocated",
  • "allocation_version": 0,
  • "method": "bank_transfer",
  • "paid_at": "2019-08-24",
  • "reference": "string",
  • "notes": "string",
  • "dedupe_key": "string",
  • "business_partner": {
    },
  • "tenant": {
    },
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    },
  • "_links": {
    }
}

Partially update a payment

Payments are correctable in place. Lowering amount below amount_allocated is rejected with 422 — unallocate first.

Authorizations:
BearerAuth
path Parameters
payment_id
required
string
Example: pay_a1b2c3d4e5f6
Request Body schema: application/json
required
object (MonetaryAmountInput)

Request-side counterpart of MonetaryAmount. Identical, except that amount also accepts a quoted decimal string — the encoding the precision contract above tells clients to use for sub-cent values, which the server's Pydantic coerces to an exact Decimal.

Responses always carry amount as a JSON number; use MonetaryAmount for those.

method
string (PaymentMethod)
Enum: "bank_transfer" "bank_deposit" "cash" "card" "cheque" "mobile_money" "pix" "upi" "boleto" "other"

How the money arrived. This is metadata about a payment that already happened — Raccoon does not process card, mobile-money, Pix, UPI or boleto transactions. bank_deposit is cash or a cheque paid in over the counter (distinct from cash in the till and from a bank_transfer); each value maps to a UNTDID 4461 payment-means code for e-invoice export.

paid_at
string <date>
reference
string or null <= 200 characters
Reference (object) or null
notes
string or null

Responses

Response Schema: application/json
_id
required
string
_class
required
string
_name
required
string (EntityName)

Backend-composed label. A payment has no name field, so this is the amount plus the rail's own reference — recognisable in a picker or an approval card. Read the amount field to render the figure properly.

required
object (MonetaryAmount)

A monetary value with an ISO 4217 currency code.

currency may be null to denote the identity ("zero") element{"amount": 0, "currency": null}. The identity absorbs into arithmetic (identity + x = x, x - x = identity, identity * n = identity); any non-zero amount must carry a currency. This is the wire form returned for an invoice draft whose totals haven't been computed yet.

Precision contract

amount is emitted as a JSON number carrying the server's Decimal with full precision — the server writes the Decimal's exact textual form as a raw JSON number token, bypassing any IEEE-754 round-trip. Sub-cent values such as 0.0003 USD (audiobook-streaming royalty rates, FX conversions, regulated reference prices) come over the wire exactly.

Client-side parsing

Standard JSON parsers convert JSON numbers to IEEE-754 floats, which lose precision for arithmetic on decimals that aren't power-of-10 fractions. Pick the right tool for the job:

  • Display only — the default float is fine. Both Python's repr(0.0003) and JavaScript's (0.0003).toString() return "0.0003", so user-visible numbers render correctly.
  • Exact arithmetic (summing line items, computing tax) — parse with a Decimal / BigDecimal type. Python: json.loads(text, parse_float=Decimal). JavaScript: a BigDecimal library such as big.js or decimal.js.

Client-side encoding for sub-cent values

A client that produces a sub-cent amount with json.dumps (Python) or JSON.stringify (JS) will also lose precision before the bytes leave the process — both encoders convert Decimal/BigDecimal to float first. Two paths preserve precision on the way in:

  1. Send the amount as a quoted decimal string. Request bodies schema this as MonetaryAmountInput, whose amount is a oneOf over number and string; the server's Pydantic coerces the string to an exact Decimal. (MonetaryAmount itself is the response shape and stays type: number.)
  2. Or use a JSON encoder on the client that supports raw number tokens (same trick as our server's encoder).

For everyday cent-precision values, sending a JSON number is fine.

required
object (MonetaryAmount)

Backend-computed total applied to invoices. Maintained by the allocation transaction; never accepted on input.

required
object (MonetaryAmount)

Backend-computed remainder (amountamount_allocated) — credit on account. Unlike a bare zero MonetaryAmount, this always carries the payment's currency, so a fully-allocated payment reads {"amount": 0, "currency": "KES"} rather than a null currency.

allocation_state
required
string (AllocationState)
Enum: "unallocated" "partially_allocated" "allocated"

How much of the payment has been applied to invoices. unallocated money is real money received that no invoice claims yet — credit on account against the payer.

allocation_version
required
integer

Optimistic-lock counter for the allocation transaction. Read-only; exposed so a client can tell two allocation states apart.

method
required
string (PaymentMethod)
Enum: "bank_transfer" "bank_deposit" "cash" "card" "cheque" "mobile_money" "pix" "upi" "boleto" "other"

How the money arrived. This is metadata about a payment that already happened — Raccoon does not process card, mobile-money, Pix, UPI or boleto transactions. bank_deposit is cash or a cheque paid in over the counter (distinct from cash in the till and from a bank_transfer); each value maps to a UNTDID 4461 payment-means code for e-invoice export.

paid_at
required
string <date>
reference
required
string or null
notes
required
string or null
dedupe_key
required
string or null
required
Reference (object) or null
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

created_at
required
integer

Unix epoch seconds

updated_at
required
integer

Unix epoch seconds

required
Reference (object) or null
required
object

self is the payment's own URL.

Request samples

Content type
application/json
{
  • "amount": {
    },
  • "method": "bank_transfer",
  • "paid_at": "2019-08-24",
  • "reference": "string",
  • "business_partner": {
    },
  • "notes": "string"
}

Response samples

Content type
application/json
{
  • "_id": "pay_a1b2c3d4e5f6",
  • "_class": "payment",
  • "_name": "Acme Corp",
  • "amount": {
    },
  • "amount_allocated": {
    },
  • "amount_unallocated": {
    },
  • "allocation_state": "unallocated",
  • "allocation_version": 0,
  • "method": "bank_transfer",
  • "paid_at": "2019-08-24",
  • "reference": "string",
  • "notes": "string",
  • "dedupe_key": "string",
  • "business_partner": {
    },
  • "tenant": {
    },
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    },
  • "_links": {
    }
}

Delete a payment

Only a payment with no live allocations can be deleted; one that is allocated to an invoice returns 409. Unallocate it first.

Authorizations:
BearerAuth
path Parameters
payment_id
required
string
Example: pay_a1b2c3d4e5f6

Responses

Response samples

Content type
application/json
{
  • "error": "not_found",
  • "message": "Business partner not found",
  • "details": { }
}

Bank Reconciliation

The camt statements this tenant has imported

Authorizations:
BearerAuth
query Parameters
q
string

Narrows the list to statements whose file name or uploader contains this term, in any case. It matches what the row shows and nothing else — status and counts are not free text. The whole set is read before the filter runs, so a term that matches nothing answers with an empty list, never a short page.

Responses

Response Schema: application/json
required
Array of objects (BankStatement)
next_token
required
string or null

Pass back as next_token for the following page. Null when done.

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_token": "string"
}

Import a camt statement

Opens an import and answers with where to upload the camt.053/054 file. The statement is created first, so a file can never sit in storage with nothing recording that it should be read — upload the bytes to the returned URL and there is nothing further to call.

Reading the file is a worker's job: a statement is megabytes of XML and one write per entry, which is not work a request should hold open.

Each credit is matched to an open payment collection by the structured reference it carries — never by payer name, amount or date, because a wrong automatic match moves money against the wrong invoice. A credit that matches nothing becomes an unallocated payment for a human to place.

Authorizations:
BearerAuth
Request Body schema: application/json
required
file_name
required
string
file_size
required
integer

Size in bytes. Refused above the registered cap for a statement.

mime_type
required
string

Responses

Response Schema: application/json
_id
required
string
_class
required
string
required
Reference (object) or null

Null only in the window between the statement being opened and its file row being created — a crash in between leaves a visible statement rather than an orphaned upload.

status
required
string
Enum: "awaiting_file" "in_progress" "finished" "failed"

awaiting_file — created, waiting for the upload to land. in_progress — the worker has picked the file up and is reading it. finished / failed — it is done.

Written only by the worker, through /status. The counts come from the entries callback and describe what was read; they do not decide what the import is doing.

credits_seen
required
integer
credits_matched
required
integer

Credits that carried a reference matching an open collection.

credits_unmatched
required
integer

Credits recorded as unallocated payments. Money that arrived with nothing to point it at is not an error — it is what the payments list is for.

debits_seen
required
integer

Outgoing entries kept from this file. They are stored as a record and nothing else — never matched, never a payment — so they are counted separately rather than folded into the credit tallies.

failure_reason
required
string or null
processed_at
required
integer or null
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

created_at
required
integer
updated_at
required
integer
required
Reference (object) or null
required
object
required
object (PresignedPost)

S3 presigned-POST form. The client must POST multipart/form-data with every field from fields plus a final file part containing the binary body.

Request samples

Content type
application/json
{
  • "file_name": "august-2026.xml",
  • "file_size": 248000,
  • "mime_type": "application/xml"
}

Response samples

Content type
application/json
{
  • "_id": "bstm_9f2c1a4b7e05",
  • "_class": "string",
  • "file": {
    },
  • "status": "awaiting_file",
  • "credits_seen": 0,
  • "credits_matched": 0,
  • "credits_unmatched": 0,
  • "debits_seen": 0,
  • "failure_reason": "string",
  • "processed_at": 0,
  • "tenant": {
    },
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    },
  • "_links": {
    },
  • "upload": {
    }
}

One statement, and how its reading went

Authorizations:
BearerAuth
path Parameters
bank_statement_id
required
string

Responses

Response Schema: application/json
_id
required
string
_class
required
string
required
Reference (object) or null

Null only in the window between the statement being opened and its file row being created — a crash in between leaves a visible statement rather than an orphaned upload.

status
required
string
Enum: "awaiting_file" "in_progress" "finished" "failed"

awaiting_file — created, waiting for the upload to land. in_progress — the worker has picked the file up and is reading it. finished / failed — it is done.

Written only by the worker, through /status. The counts come from the entries callback and describe what was read; they do not decide what the import is doing.

credits_seen
required
integer
credits_matched
required
integer

Credits that carried a reference matching an open collection.

credits_unmatched
required
integer

Credits recorded as unallocated payments. Money that arrived with nothing to point it at is not an error — it is what the payments list is for.

debits_seen
required
integer

Outgoing entries kept from this file. They are stored as a record and nothing else — never matched, never a payment — so they are counted separately rather than folded into the credit tallies.

failure_reason
required
string or null
processed_at
required
integer or null
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

created_at
required
integer
updated_at
required
integer
required
Reference (object) or null
required
object

Response samples

Content type
application/json
{
  • "_id": "bstm_9f2c1a4b7e05",
  • "_class": "string",
  • "file": {
    },
  • "status": "awaiting_file",
  • "credits_seen": 0,
  • "credits_matched": 0,
  • "credits_unmatched": 0,
  • "debits_seen": 0,
  • "failure_reason": "string",
  • "processed_at": 0,
  • "tenant": {
    },
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    },
  • "_links": {
    }
}

Report where the import has got to (service-to-service)

The worker says it has picked the file up, and later that it is done. Service-gated for the same reason as the entries callback: a forged status tells a tenant their money has been looked at when it has not.

The only writer of status. The counts arrive through the entries callback and describe what was read; they do not decide what the import is doing.

Authorizations:
BearerAuth
path Parameters
bank_statement_id
required
string
Request Body schema: application/json
required
status
required
string
Enum: "in_progress" "finished" "failed"

Responses

Response Schema: application/json
_id
required
string
_class
required
string
required
Reference (object) or null

Null only in the window between the statement being opened and its file row being created — a crash in between leaves a visible statement rather than an orphaned upload.

status
required
string
Enum: "awaiting_file" "in_progress" "finished" "failed"

awaiting_file — created, waiting for the upload to land. in_progress — the worker has picked the file up and is reading it. finished / failed — it is done.

Written only by the worker, through /status. The counts come from the entries callback and describe what was read; they do not decide what the import is doing.

credits_seen
required
integer
credits_matched
required
integer

Credits that carried a reference matching an open collection.

credits_unmatched
required
integer

Credits recorded as unallocated payments. Money that arrived with nothing to point it at is not an error — it is what the payments list is for.

debits_seen
required
integer

Outgoing entries kept from this file. They are stored as a record and nothing else — never matched, never a payment — so they are counted separately rather than folded into the credit tallies.

failure_reason
required
string or null
processed_at
required
integer or null
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

created_at
required
integer
updated_at
required
integer
required
Reference (object) or null
required
object

Request samples

Content type
application/json
{
  • "status": "in_progress"
}

Response samples

Content type
application/json
{
  • "_id": "bstm_9f2c1a4b7e05",
  • "_class": "string",
  • "file": {
    },
  • "status": "awaiting_file",
  • "credits_seen": 0,
  • "credits_matched": 0,
  • "credits_unmatched": 0,
  • "debits_seen": 0,
  • "failure_reason": "string",
  • "processed_at": 0,
  • "tenant": {
    },
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    },
  • "_links": {
    }
}

Report the credits a statement contained (service-to-service)

The worker parses; this endpoint books. Gated to service identities and superusers only — a forged credit is money, so it is held to the tier a payment provider's callback uses.

Reporting the same credit twice books nothing twice: the bank's own reference for the entry is the idempotency key, which is what makes importing overlapping statements safe.

Authorizations:
BearerAuth
path Parameters
bank_statement_id
required
string
Request Body schema: application/json
required
Array of objects (BankTransactionCreate)
failure_reason
string or null

Responses

Response Schema: application/json
required
Array of objects

Request samples

Content type
application/json
{
  • "transactions": [
    ],
  • "failure_reason": "string"
}

Response samples

Content type
application/json
{
  • "results": [
    ]
}

Entries seen on this tenant's account, and what became of them

Authorizations:
BearerAuth
query Parameters
status
string
Value: "unmatched"

unmatched narrows to money that arrived carrying no reference Raccoon recognised — the queue a human works through. Outgoing entries are excluded: a debit is never matched, so it is not waiting for anyone.

limit
integer [ 1 .. 200 ]
Default: 50
next_token
string

The next_token from the previous page.

statement_id
string

Only the entries one imported file contained. The statement's own counts say how many there were; this is how to see them.

direction
string
Enum: "credit" "debit"

debit answers "what went out?". Outgoing entries are kept as a record only — never matched, never a payment. Both directions are returned when this is omitted.

Responses

Response Schema: application/json
required
Array of objects (BankTransaction)
next_token
required
string or null

Pass back as next_token for the following page. Null when done.

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_token": "string"
}

One credit, by the reference its bank gave it

A transaction is addressed by its bank reference rather than by its own id: that is the key it is stored under, and the one anything holding a credit already knows.

Authorizations:
BearerAuth
path Parameters
bank_reference
required
string

Responses

Response Schema: application/json
_id
required
string
_class
required
string
bank_reference
required
string
required
object (MonetaryAmount)

A monetary value with an ISO 4217 currency code.

currency may be null to denote the identity ("zero") element{"amount": 0, "currency": null}. The identity absorbs into arithmetic (identity + x = x, x - x = identity, identity * n = identity); any non-zero amount must carry a currency. This is the wire form returned for an invoice draft whose totals haven't been computed yet.

Precision contract

amount is emitted as a JSON number carrying the server's Decimal with full precision — the server writes the Decimal's exact textual form as a raw JSON number token, bypassing any IEEE-754 round-trip. Sub-cent values such as 0.0003 USD (audiobook-streaming royalty rates, FX conversions, regulated reference prices) come over the wire exactly.

Client-side parsing

Standard JSON parsers convert JSON numbers to IEEE-754 floats, which lose precision for arithmetic on decimals that aren't power-of-10 fractions. Pick the right tool for the job:

  • Display only — the default float is fine. Both Python's repr(0.0003) and JavaScript's (0.0003).toString() return "0.0003", so user-visible numbers render correctly.
  • Exact arithmetic (summing line items, computing tax) — parse with a Decimal / BigDecimal type. Python: json.loads(text, parse_float=Decimal). JavaScript: a BigDecimal library such as big.js or decimal.js.

Client-side encoding for sub-cent values

A client that produces a sub-cent amount with json.dumps (Python) or JSON.stringify (JS) will also lose precision before the bytes leave the process — both encoders convert Decimal/BigDecimal to float first. Two paths preserve precision on the way in:

  1. Send the amount as a quoted decimal string. Request bodies schema this as MonetaryAmountInput, whose amount is a oneOf over number and string; the server's Pydantic coerces the string to an exact Decimal. (MonetaryAmount itself is the response shape and stays type: number.)
  2. Or use a JSON encoder on the client that supports raw number tokens (same trick as our server's encoder).

For everyday cent-precision values, sending a JSON number is fine.

booked_on
required
string
direction
required
string
Enum: "credit" "debit"

Money in or money out. A debit is inert: it is never matched and never carries a payment, so its status is always unmatched.

status
required
string
Enum: "matched" "unmatched"
remittance_information
required
string or null
counterparty_name
required
string or null
required
Reference (object) or null
required
Array of objects (Reference)

The invoices this entry's money currently sits against, resolved from the live allocations rather than stored when the entry was booked — so a payment re-allocated or split afterwards answers with where the money is now. Each _name is the invoice number.

Empty for a debit — outgoing money is kept as a record and never matched to an invoice — and for a credit whose allocations have all been voided.

required
Reference (object) or null
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

created_at
required
integer
updated_at
required
integer
required
Reference (object) or null
required
object

Response samples

Content type
application/json
{
  • "_id": "string",
  • "_class": "string",
  • "bank_reference": "string",
  • "amount": {
    },
  • "booked_on": "string",
  • "direction": "credit",
  • "status": "matched",
  • "remittance_information": "string",
  • "counterparty_name": "string",
  • "invoices": [
    ],
  • "payment": {
    },
  • "statement": {
    },
  • "tenant": {
    },
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    },
  • "_links": {
    }
}

Connectors

List the connectors offered to this tenant

The outside services this tenant can plug into: the ways their customers can pay an invoice from their phone, and the tax authorities that clear an invoice before it is valid. Which appear is derived from the tenant's country at read time, so a corrected market list reaches every tenant without a migration.

Every payment rail settles into the tenant's own merchant account — Raccoon never holds the funds. Connectors are switched on and off through their own tenant-config key, not here.

Authorizations:
BearerAuth
query Parameters
kind
string
Enum: "payment_rail" "clearance"

Only connectors of this kind.

Responses

Response Schema: application/json
required
Array of objects (Connector)
total
required
integer
page
required
integer
page_size
required
integer

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "total": 0,
  • "page": 0,
  • "page_size": 0
}

Read one connector's state for this tenant

Answers for any registered connector, including one not offered in the tenant's country — available_in_country carries that rather than the endpoint 404-ing, so a tenant who moved gets an explanation.

Authorizations:
BearerAuth
path Parameters
connector_id
required
string
Example: girocode

Responses

Response Schema: application/json
connector_id
required
string
kind
required
string
Enum: "payment_rail" "clearance"

What this connector connects to.

display_name
required
string
shape
required
string or null
Enum: "push" "qr" "link" null

How the buyer meets a payment rail. Null for other kinds.

reconciliation
required
string or null
Enum: "callback" "matched" null

callback — the rail confirms the payment itself. matched — it cannot, so the money is recognised by the reference it carries. Null for other kinds.

clearance_scheme
required
string or null
Enum: "ke_kra_etims" "ng_firs_mbs" "in_gstn_irp" "br_sefaz_nfe" null

The regime a clearance connector clears under, as it is stamped on the cleared invoice. Null for other kinds. Not the same identifier as connector_id: one regime may have more than one connector.

enabled
required
boolean

This connector's own enable key in the tenant's config.

configured
required
boolean

Whether anything is left for the tenant to do before this connector can be used: true once every required configuration field is on file. A connector with no required field — nothing to hand over — is configured by default. A missing IBAN and a missing API key are the same answer.

available_in_country
required
boolean

Whether the tenant's own country is one of the connector's default markets — where it is listed, defaults on, and shows a "configure" call-to-action. A soft default, not a usability gate: a payment rail switched on and configured can be collected on from any country (a clearance connector stays country-gated for legality).

required
Array of objects (ConnectorField)

What the tenant supplies to configure this connector — credentials, an IBAN. The configure screen renders from this rather than carrying its own copy of every connector's fields, so adding a connector stays a backend change. Empty for a connector with nothing to configure.

required
Array of objects (ConnectorRequirement)

What the connector needs on each invoice before it can run — a buyer phone for a rail that pushes a request to one. Distinct from a configuration field: checked against the invoice at collection time rather than supplied once. Empty when the connector needs nothing beyond being configured.

description
string

Response samples

Content type
application/json
{
  • "connector_id": "mpesa_push",
  • "kind": "payment_rail",
  • "display_name": "M-Pesa request on the phone",
  • "shape": "push",
  • "reconciliation": "callback",
  • "clearance_scheme": "ke_kra_etims",
  • "enabled": true,
  • "configured": true,
  • "available_in_country": true,
  • "config_fields": [
    ],
  • "requirements": [
    ],
  • "description": "string"
}

Switch a connector on or off for this tenant

Whether the tenant may use this connector at all — the first of the three axes, and a separate control from its credentials. A connector switched off leaves the surfaces that offer it; one switched on without credentials stays visible with something to do.

The connector owns its own switch rather than exposing the tenant_config key behind it, so no caller has to name that key. The key's own edit scope still decides who may flip it.

Authorizations:
BearerAuth
path Parameters
connector_id
required
string
Example: girocode
Request Body schema: application/json
required
enabled
required
boolean

Responses

Response Schema: application/json
connector_id
required
string
kind
required
string
Enum: "payment_rail" "clearance"

What this connector connects to.

display_name
required
string
shape
required
string or null
Enum: "push" "qr" "link" null

How the buyer meets a payment rail. Null for other kinds.

reconciliation
required
string or null
Enum: "callback" "matched" null

callback — the rail confirms the payment itself. matched — it cannot, so the money is recognised by the reference it carries. Null for other kinds.

clearance_scheme
required
string or null
Enum: "ke_kra_etims" "ng_firs_mbs" "in_gstn_irp" "br_sefaz_nfe" null

The regime a clearance connector clears under, as it is stamped on the cleared invoice. Null for other kinds. Not the same identifier as connector_id: one regime may have more than one connector.

enabled
required
boolean

This connector's own enable key in the tenant's config.

configured
required
boolean

Whether anything is left for the tenant to do before this connector can be used: true once every required configuration field is on file. A connector with no required field — nothing to hand over — is configured by default. A missing IBAN and a missing API key are the same answer.

available_in_country
required
boolean

Whether the tenant's own country is one of the connector's default markets — where it is listed, defaults on, and shows a "configure" call-to-action. A soft default, not a usability gate: a payment rail switched on and configured can be collected on from any country (a clearance connector stays country-gated for legality).

required
Array of objects (ConnectorField)

What the tenant supplies to configure this connector — credentials, an IBAN. The configure screen renders from this rather than carrying its own copy of every connector's fields, so adding a connector stays a backend change. Empty for a connector with nothing to configure.

required
Array of objects (ConnectorRequirement)

What the connector needs on each invoice before it can run — a buyer phone for a rail that pushes a request to one. Distinct from a configuration field: checked against the invoice at collection time rather than supplied once. Empty when the connector needs nothing beyond being configured.

description
string

Request samples

Content type
application/json
{
  • "enabled": true
}

Response samples

Content type
application/json
{
  • "connector_id": "mpesa_push",
  • "kind": "payment_rail",
  • "display_name": "M-Pesa request on the phone",
  • "shape": "push",
  • "reconciliation": "callback",
  • "clearance_scheme": "ke_kra_etims",
  • "enabled": true,
  • "configured": true,
  • "available_in_country": true,
  • "config_fields": [
    ],
  • "requirements": [
    ],
  • "description": "string"
}

What is known about this connector's stored credentials

Metadata only. No human tier can read a secret value back once saved: the record carries it as ciphertext and never as plaintext. The one route that returns the values is GET .../credentials/values, which is callable by a registered service and by nobody else.

Authorizations:
BearerAuth
path Parameters
connector_id
required
string
Example: mpesa_push

Responses

Response Schema: application/json
connector_id
required
string
configured
required
boolean

Whether credentials are on file for this connector.

object

The connector's non-secret fields, as the tenant entered them.

verified_at
integer or null

When the credentials were last confirmed to work, as a unix timestamp. Null means they have not been checked — which is not the same as broken.

verification_error
string or null

Why the last check failed, when it did.

updated_at
integer or null

Response samples

Content type
application/json
{
  • "connector_id": "mpesa_push",
  • "configured": true,
  • "hint": {
    },
  • "verified_at": 0,
  • "verification_error": "string",
  • "updated_at": 0
}

Store this tenant's own credentials for a connector

Replaces the whole credential set and then checks that it works. The credentials belong to the tenant's own relationship with the provider or the tax authority — Raccoon uses them to act on the tenant's behalf, and never holds their money.

Authorizations:
BearerAuth
path Parameters
connector_id
required
string
Example: mpesa_push
Request Body schema: application/json
required
required
object non-empty

Field id to value. Every required field the connector declares must be present; optional ones may be included. Unknown fields, a missing required one, or a value that fails the field's format check are rejected.

Responses

Response Schema: application/json
connector_id
required
string
configured
required
boolean

Whether credentials are on file for this connector.

object

The connector's non-secret fields, as the tenant entered them.

verified_at
integer or null

When the credentials were last confirmed to work, as a unix timestamp. Null means they have not been checked — which is not the same as broken.

verification_error
string or null

Why the last check failed, when it did.

updated_at
integer or null

Request samples

Content type
application/json
{
  • "values": {
    }
}

Response samples

Content type
application/json
{
  • "connector_id": "mpesa_push",
  • "configured": true,
  • "hint": {
    },
  • "verified_at": 0,
  • "verification_error": "string",
  • "updated_at": 0
}

Forget this connector's credentials

Removes the stored values immediately. The connector stays switched on and becomes unconfigured again, so it keeps its place in the list with something to do rather than disappearing.

Authorizations:
BearerAuth
path Parameters
connector_id
required
string
Example: mpesa_push

Responses

Response samples

Content type
application/json
{
  • "error": "not_found",
  • "message": "Business partner not found",
  • "details": { }
}

Resolve a connector's credentials for a worker service

Returns the plaintext credentials, and is the only route that does.

It exists because an adapter running outside the core cannot resolve them in process — a service may not import core code, and the stored values are ciphertext — so a clearance worker has no other way to authenticate its one call to the tax authority.

Callable only by a registered service: no administrator, owner or ordinary user reaches it at any tier. Each service is additionally pinned to the connectors it may resolve by the scope table in middleware/service_auth.py, so a payment adapter cannot read a tax authority's credentials or the reverse.

Authorizations:
BearerAuth
path Parameters
connector_id
required
string
Example: nrs_mbs

Responses

Response Schema: application/json
connector_id
required
string
required
object

Every field the connector declares, secret and non-secret alike.

Response samples

Content type
application/json
{
  • "connector_id": "nrs_mbs",
  • "values": {
    }
}

Documents

List documents for an invoice

Returns all document rows attached to the invoice. A document is a derived business document (invoice PDF, delivery slip, offer, …) generated asynchronously by a worker.

Authorizations:
BearerAuth
path Parameters
invoice_id
required
string
Example: inv_a1b2c3d4e5f6

Responses

Response Schema: application/json
required
Array of objects (DocumentResponse)
next_token
required
string or null

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_token": "string"
}

Create a document for an invoice

Creates a document row in pdf_pending and emits a document.created event. The PDF generation worker subscribes and asynchronously attaches the rendered file — clients should poll the row until status flips to pdf_ready.

Authorizations:
BearerAuth
path Parameters
invoice_id
required
string
Example: inv_a1b2c3d4e5f6
Request Body schema: application/json
required
document_type
required
string (DocumentType)
Enum: "invoice" "invoice_ubl" "delivery_slip" "pro_forma_invoice" "intermediate_receipt"

The kind of business document this document represents. invoice renders a PDF. invoice_ubl renders the same invoice as a Peppol BIS Billing 3.0 (UBL 2.1) XML file, the structured form EN 16931 requires; it is generated beside the PDF, never instead of it. The remaining values are reserved for upcoming templates. An offer's PDF is not here: an offer is its own record with its own documents, under /resource/v1/offers/{id}/documents.

Responses

Response Schema: application/json
_id
required
string
_class
required
string
invoice_id
required
string
document_type
required
string (DocumentType)
Enum: "invoice" "invoice_ubl" "delivery_slip" "pro_forma_invoice" "intermediate_receipt"

The kind of business document this document represents. invoice renders a PDF. invoice_ubl renders the same invoice as a Peppol BIS Billing 3.0 (UBL 2.1) XML file, the structured form EN 16931 requires; it is generated beside the PDF, never instead of it. The remaining values are reserved for upcoming templates. An offer's PDF is not here: an offer is its own record with its own documents, under /resource/v1/offers/{id}/documents.

status
required
string (DocumentStatus)
Enum: "draft" "pdf_pending" "pdf_ready" "pdf_failed"

Lifecycle state. New documents start at pdf_pending; the async PDF worker promotes them to pdf_ready (or pdf_failed on permanent error). draft is reserved for future use.

required
Reference (object) or null

Reference to the rendered PDF. null until the document-generator worker reports the output via PATCH .../documents/{id}.

template_version
required
string
created_at
required
integer
updated_at
required
integer
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

required
ReferenceValue (object) or null

Request samples

Content type
application/json
{
  • "document_type": "invoice"
}

Response samples

Content type
application/json
{
  • "_id": "doc_a1b2c3d4e5f6789012345",
  • "_class": "document",
  • "invoice_id": "inv_a1b2c3d4e5f6",
  • "document_type": "invoice",
  • "status": "draft",
  • "file": {
    },
  • "template_version": "1.0.0",
  • "created_at": 0,
  • "updated_at": 0,
  • "tenant": {
    },
  • "created_by": {
    }
}

Get a single document

Authorizations:
BearerAuth
path Parameters
invoice_id
required
string
document_id
required
string
Example: doc_a1b2c3d4e5f67890

Responses

Response Schema: application/json
_id
required
string
_class
required
string
invoice_id
required
string
document_type
required
string (DocumentType)
Enum: "invoice" "invoice_ubl" "delivery_slip" "pro_forma_invoice" "intermediate_receipt"

The kind of business document this document represents. invoice renders a PDF. invoice_ubl renders the same invoice as a Peppol BIS Billing 3.0 (UBL 2.1) XML file, the structured form EN 16931 requires; it is generated beside the PDF, never instead of it. The remaining values are reserved for upcoming templates. An offer's PDF is not here: an offer is its own record with its own documents, under /resource/v1/offers/{id}/documents.

status
required
string (DocumentStatus)
Enum: "draft" "pdf_pending" "pdf_ready" "pdf_failed"

Lifecycle state. New documents start at pdf_pending; the async PDF worker promotes them to pdf_ready (or pdf_failed on permanent error). draft is reserved for future use.

required
Reference (object) or null

Reference to the rendered PDF. null until the document-generator worker reports the output via PATCH .../documents/{id}.

template_version
required
string
created_at
required
integer
updated_at
required
integer
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

required
ReferenceValue (object) or null

Response samples

Content type
application/json
{
  • "_id": "doc_a1b2c3d4e5f6789012345",
  • "_class": "document",
  • "invoice_id": "inv_a1b2c3d4e5f6",
  • "document_type": "invoice",
  • "status": "draft",
  • "file": {
    },
  • "template_version": "1.0.0",
  • "created_at": 0,
  • "updated_at": 0,
  • "tenant": {
    },
  • "created_by": {
    }
}

Partial update — attach the rendered PDF, or retry a failed one

Two mutable fields, file and status. Setting file on a pdf_pending document implicitly transitions it to pdf_ready. Setting status to pdf_pending moves a pdf_failed document back to pending and re-publishes the document.created event so the worker picks it up again — idempotent for an already-pending row. No other status is writable; the rest of the FSM is server-controlled. Used by the invoice-document-generator service to report a rendered PDF (per architecture.md § Async Workers — no internal API; a UI client could call it too). Requires admin/superuser role. Idempotent: replaying on an already-attached document returns 200 with the existing row (the originally attached PDF is preserved). Per architecture.md § Embedded References, only _id is required inside the file Reference; _class and _name are accepted and resolved server-side from the linked File row.

Authorizations:
BearerAuth
path Parameters
invoice_id
required
string
document_id
required
string
Example: doc_a1b2c3d4e5f67890
Request Body schema: application/json
required
Reference (object) or null
status
string
Value: "pdf_pending"

Set to pdf_pending to re-request the render of a pdf_failed document. Idempotent for an already-pending row; 422 from any other status, and 422 when sent together with file — those are two different writes.

Responses

Response Schema: application/json
_id
required
string
_class
required
string
invoice_id
required
string
document_type
required
string (DocumentType)
Enum: "invoice" "invoice_ubl" "delivery_slip" "pro_forma_invoice" "intermediate_receipt"

The kind of business document this document represents. invoice renders a PDF. invoice_ubl renders the same invoice as a Peppol BIS Billing 3.0 (UBL 2.1) XML file, the structured form EN 16931 requires; it is generated beside the PDF, never instead of it. The remaining values are reserved for upcoming templates. An offer's PDF is not here: an offer is its own record with its own documents, under /resource/v1/offers/{id}/documents.

status
required
string (DocumentStatus)
Enum: "draft" "pdf_pending" "pdf_ready" "pdf_failed"

Lifecycle state. New documents start at pdf_pending; the async PDF worker promotes them to pdf_ready (or pdf_failed on permanent error). draft is reserved for future use.

required
Reference (object) or null

Reference to the rendered PDF. null until the document-generator worker reports the output via PATCH .../documents/{id}.

template_version
required
string
created_at
required
integer
updated_at
required
integer
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

required
ReferenceValue (object) or null

Request samples

Content type
application/json
{
  • "file": {
    }
}

Response samples

Content type
application/json
{
  • "_id": "doc_a1b2c3d4e5f6789012345",
  • "_class": "document",
  • "invoice_id": "inv_a1b2c3d4e5f6",
  • "document_type": "invoice",
  • "status": "draft",
  • "file": {
    },
  • "template_version": "1.0.0",
  • "created_at": 0,
  • "updated_at": 0,
  • "tenant": {
    },
  • "created_by": {
    }
}

Delete a document

Authorizations:
BearerAuth
path Parameters
invoice_id
required
string
document_id
required
string
Example: doc_a1b2c3d4e5f67890

Responses

Response samples

Content type
application/json
{
  • "error": "not_found",
  • "message": "Business partner not found",
  • "details": { }
}

Email

List the tenant's business email, newest first

The shared mailbox, readable by every signed-in user of the tenant. Nothing here is filtered by user.

Bodies are never in a list response — they live in a sibling row and are returned by the detail route. preview is the one line that makes the list readable without them.

Every filter is applied after the query rather than by an index, so a page can come back short — or empty — while next_token is still set. Exhaustion is a null token, never a short page.

todo_counts rides this response so the queue's three sections can be labelled from one call. It is tallied on the first page only and is null on a continuation page: the headings do not change while paging, and the tally walks the tenant's whole partition.

Authorizations:
BearerAuth
query Parameters
direction
string
Enum: "inbound" "outbound"
todo_kind
string (TodoKind)
Enum: "action" "reply" "notice"

Only messages of this kind.

todo_status
string (TodoStatus)
Enum: "open" "done"

Only messages anyone has, or has not, dealt with.

assignee
string

Only the messages one person holds. A user id, me for the calling user, or none for the work nobody has picked up. This narrows the answer, never the reader: without it every signed-in user sees the whole mailbox, and with it they still may.

document_id
string

Only messages about this invoice or offer. A post-query filter over the tenant's whole mailbox, so its cost grows with the mailbox rather than with the answer.

link_state
string
Enum: "linked" "unlinked"

linked: the message names a document. unlinked: it names none.

limit
integer [ 1 .. 200 ]
Default: 50
next_token
string

Base64-encoded pagination cursor from previous response

Responses

Response Schema: application/json
required
Array of objects (EmailMessageResponse)
next_token
required
string or null
required
TodoCounts (object) or null

Tallied on the first page only, and null on a continuation page — the section headings do not change while paging, and the tally walks the tenant's partition, so recomputing it per page turn would cost more than the page. A client keeps the counts it already has.

todo_counts_are_a_floor
required
boolean or null

The counting walk hit its row budget with rows left, so each count is at least what it says rather than exactly. False for any mailbox a tenant is likely to have, and null wherever todo_counts is.

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_token": "string",
  • "todo_counts": {
    },
  • "todo_counts_are_a_floor": true
}

Record a message (internal — service callers only)

Internal. Called by the mail worker holding a service identity, never by a tenant: a tenant token is refused with 403.

This is the only write on the message itself. The envelope, headers, verdicts, bodies and attachments are a record of what arrived or was sent, and a record the recipient can edit is not a record — so there is no tenant-facing POST, PUT or DELETE here at all, and the one tenant-facing PATCH takes the to-do and refuses every other key.

Two bounds are applied server-side rather than asserted by the caller: the references chain is cut to 20 entries, and the bodies are truncated to their inline caps with the full copy written to S3. Both truncate and neither rejects — the message already arrived.

An inbound message must carry recipient, the envelope address it was delivered to. The core resolves it against the alias claims and refuses the message with 403 unless it belongs to the token's tenant, so the tenant a message lands in is decided here and never by the caller. Its sub-address tag becomes thread_token; an inbound caller may not send one.

A thread — the tag, or the sender when there is none — may add 20 inbound messages per UTC day; past that the answer is 429 and the worker drops the message. A dedupe_key seen before answers 200 with the message it stored.

Authorizations:
ServiceToken
Request Body schema: application/json
required
direction
required
string
Enum: "inbound" "outbound"
status
required
string
Enum: "received" "sent" "failed" "queued" "quarantined"
from_address
required
string <= 320 characters
from_name
string or null <= 320 characters
to
Array of strings <= 100 items
cc
Array of strings <= 100 items
subject
string <= 998 characters
preview
string or null <= 400 characters

The one line the list shows. Derived from the plain-text body when omitted.

message_id
string or null <= 998 characters
in_reply_to
string or null <= 998 characters
references
Array of strings

The References header chain. Kept to the first 20 entries — a long thread's chain runs to tens of kilobytes.

spf_verdict
string (EmailVerdict)
Enum: "pass" "fail" "gray" "processing_failed" "not_available"

An authentication result reported by the receiving mail service. SPF and DKIM are recorded and never gate; a message is not authenticated by its From header alone.

dkim_verdict
string (EmailVerdict)
Enum: "pass" "fail" "gray" "processing_failed" "not_available"

An authentication result reported by the receiving mail service. SPF and DKIM are recorded and never gate; a message is not authenticated by its From header alone.

dmarc_verdict
string (EmailVerdict)
Enum: "pass" "fail" "gray" "processing_failed" "not_available"

An authentication result reported by the receiving mail service. SPF and DKIM are recorded and never gate; a message is not authenticated by its From header alone.

TodoKind (string) or null

What the message is for. The only to-do field a writer may state; omitted, it follows the direction — reply inbound, notice outbound. Whether anyone still has to act on it is derived, never supplied.

Reference (object) or null

The invoice or offer this message is about.

document_link_source
string (DocumentLinkSource)
Enum: "tag" "header" "manual" "none"

How the link to a document was made — from the reply-to thread token, from a mail header, by hand, or not at all.

thread_token
string or null <= 64 characters
raw_mime_key
string or null <= 1024 characters
raw_mime_expires_at
integer or null

Unix seconds. Past this the API serves a null raw_mime_key.

EmailBodyCreate (object) or null
recipient
string or null <= 320 characters

The envelope address the message was delivered to, not a header. Required on an inbound message; decides the tenant and the thread token, and is not stored.

dedupe_key
string or null [ 1 .. 128 ] characters

Reserved together with the message. A second call with the same key answers 200 with the stored message.

Responses

Response Schema: application/json
_id
required
string
_class
required
string
_name
required
string

The subject line.

required
object

self is the message's own resource URL. An attachment is fetched from /resource/v1/emails/{id}/attachments/{file_id}. Further rels may be present; a client follows the ones it knows.

direction
required
string
Enum: "inbound" "outbound"
status
required
string
Enum: "received" "sent" "failed" "queued" "quarantined"
from_address
required
string
from_name
required
string or null
to
required
Array of strings
cc
required
Array of strings
subject
required
string
preview
required
string

One line of the message, so the list reads without fetching bodies.

message_id
required
string or null
in_reply_to
required
string or null
references
required
Array of strings
spf_verdict
required
string (EmailVerdict)
Enum: "pass" "fail" "gray" "processing_failed" "not_available"

An authentication result reported by the receiving mail service. SPF and DKIM are recorded and never gate; a message is not authenticated by its From header alone.

dkim_verdict
required
string (EmailVerdict)
Enum: "pass" "fail" "gray" "processing_failed" "not_available"

An authentication result reported by the receiving mail service. SPF and DKIM are recorded and never gate; a message is not authenticated by its From header alone.

dmarc_verdict
required
string (EmailVerdict)
Enum: "pass" "fail" "gray" "processing_failed" "not_available"

An authentication result reported by the receiving mail service. SPF and DKIM are recorded and never gate; a message is not authenticated by its From header alone.

blocked_remote_reference_count
required
integer
attachment_count
required
integer

0 on a stored row, for the reason attachments is empty. The detail response recomputes it from the file rows.

required
Array of objects (Reference)

Always empty on a stored row: nothing can write it, because a file's parent is the message and the message is minted before any file exists. The detail response replaces it with the files actually parented on the message, which is the tenant-isolated fact.

todo_kind
required
string (TodoKind)
Enum: "action" "reply" "notice"

What the message is for. action is something Raccoon can do, reply something only a person can answer, notice a message that needs nothing. Every stored message carries one; there is no absent state, and it is not editable.

todo_status
required
string (TodoStatus)
Enum: "open" "done"

Whether anyone has dealt with the message. Not a read flag — a shared queue needs one answer to "has anyone dealt with this", not one per person. An outbound message is done when it is recorded.

required
Reference (object) or null

Who has picked this message up, or null while nobody has. Never a permission: every signed-in user of the tenant can read, open, re-assign and complete any message whoever owns it.

required
Reference (object) or null

Who completed it. Null on an outbound message, which is done because it was sent rather than because anyone cleared it.

done_at
required
integer or null

Unix epoch seconds.

required
Reference (object) or null
failure_reason
required
string or null

Why the channel refused an outbound message — the provider's own error code, not a translated sentence. null on anything that is not failed.

document_link_source
required
string (DocumentLinkSource)
Enum: "tag" "header" "manual" "none"

How the link to a document was made — from the reply-to thread token, from a mail header, by hand, or not at all.

thread_token
required
string or null
raw_mime_key
required
string or null

Null once the 90-day retention has passed, rather than a key whose object no longer exists.

raw_mime_expires_at
required
integer or null
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

created_at
required
integer

Unix epoch seconds

updated_at
required
integer

Unix epoch seconds

required
Reference (object) or null
Response Schema: application/json
_id
required
string
_class
required
string
_name
required
string

The subject line.

required
object

self is the message's own resource URL. An attachment is fetched from /resource/v1/emails/{id}/attachments/{file_id}. Further rels may be present; a client follows the ones it knows.

direction
required
string
Enum: "inbound" "outbound"
status
required
string
Enum: "received" "sent" "failed" "queued" "quarantined"
from_address
required
string
from_name
required
string or null
to
required
Array of strings
cc
required
Array of strings
subject
required
string
preview
required
string

One line of the message, so the list reads without fetching bodies.

message_id
required
string or null
in_reply_to
required
string or null
references
required
Array of strings
spf_verdict
required
string (EmailVerdict)
Enum: "pass" "fail" "gray" "processing_failed" "not_available"

An authentication result reported by the receiving mail service. SPF and DKIM are recorded and never gate; a message is not authenticated by its From header alone.

dkim_verdict
required
string (EmailVerdict)
Enum: "pass" "fail" "gray" "processing_failed" "not_available"

An authentication result reported by the receiving mail service. SPF and DKIM are recorded and never gate; a message is not authenticated by its From header alone.

dmarc_verdict
required
string (EmailVerdict)
Enum: "pass" "fail" "gray" "processing_failed" "not_available"

An authentication result reported by the receiving mail service. SPF and DKIM are recorded and never gate; a message is not authenticated by its From header alone.

blocked_remote_reference_count
required
integer
attachment_count
required
integer

0 on a stored row, for the reason attachments is empty. The detail response recomputes it from the file rows.

required
Array of objects (Reference)

Always empty on a stored row: nothing can write it, because a file's parent is the message and the message is minted before any file exists. The detail response replaces it with the files actually parented on the message, which is the tenant-isolated fact.

todo_kind
required
string (TodoKind)
Enum: "action" "reply" "notice"

What the message is for. action is something Raccoon can do, reply something only a person can answer, notice a message that needs nothing. Every stored message carries one; there is no absent state, and it is not editable.

todo_status
required
string (TodoStatus)
Enum: "open" "done"

Whether anyone has dealt with the message. Not a read flag — a shared queue needs one answer to "has anyone dealt with this", not one per person. An outbound message is done when it is recorded.

required
Reference (object) or null

Who has picked this message up, or null while nobody has. Never a permission: every signed-in user of the tenant can read, open, re-assign and complete any message whoever owns it.

required
Reference (object) or null

Who completed it. Null on an outbound message, which is done because it was sent rather than because anyone cleared it.

done_at
required
integer or null

Unix epoch seconds.

required
Reference (object) or null
failure_reason
required
string or null

Why the channel refused an outbound message — the provider's own error code, not a translated sentence. null on anything that is not failed.

document_link_source
required
string (DocumentLinkSource)
Enum: "tag" "header" "manual" "none"

How the link to a document was made — from the reply-to thread token, from a mail header, by hand, or not at all.

thread_token
required
string or null
raw_mime_key
required
string or null

Null once the 90-day retention has passed, rather than a key whose object no longer exists.

raw_mime_expires_at
required
integer or null
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

created_at
required
integer

Unix epoch seconds

updated_at
required
integer

Unix epoch seconds

required
Reference (object) or null

Request samples

Content type
application/json
{
  • "direction": "inbound",
  • "status": "received",
  • "from_address": "string",
  • "from_name": "string",
  • "to": [
    ],
  • "cc": [
    ],
  • "subject": "string",
  • "preview": "string",
  • "message_id": "string",
  • "in_reply_to": "string",
  • "references": [
    ],
  • "spf_verdict": "pass",
  • "dkim_verdict": "pass",
  • "dmarc_verdict": "pass",
  • "todo_kind": "action",
  • "document": {
    },
  • "document_link_source": "tag",
  • "thread_token": "string",
  • "raw_mime_key": "string",
  • "raw_mime_expires_at": 0,
  • "body": {
    },
  • "recipient": "string",
  • "dedupe_key": "string"
}

Response samples

Content type
application/json
{
  • "_id": "eml_a1b2c3d4e5f6",
  • "_class": "email_message",
  • "_name": "string",
  • "_links": {
    },
  • "tenant": {
    },
  • "direction": "inbound",
  • "status": "received",
  • "from_address": "string",
  • "from_name": "string",
  • "to": [
    ],
  • "cc": [
    ],
  • "subject": "string",
  • "preview": "string",
  • "message_id": "string",
  • "in_reply_to": "string",
  • "references": [
    ],
  • "spf_verdict": "pass",
  • "dkim_verdict": "pass",
  • "dmarc_verdict": "pass",
  • "blocked_remote_reference_count": 0,
  • "attachment_count": 0,
  • "attachments": [
    ],
  • "todo_kind": "action",
  • "todo_status": "open",
  • "assignee": {
    },
  • "done_by": {
    },
  • "done_at": 0,
  • "document": {
    },
  • "failure_reason": "string",
  • "document_link_source": "tag",
  • "thread_token": "string",
  • "raw_mime_key": "string",
  • "raw_mime_expires_at": 0,
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    }
}

Which tenant an inbound address belongs to (internal — service callers only)

Internal. Called by the inbound-mail worker with a token for the system tenant, which holds the alias claims; any other tenant is refused with 403. The address is split on its first +, the base is case-folded and looked up in the claims. A released alias keeps answering for as long as its claim is held.

404 for an address outside the mail domain, one the alias grammar does not admit, and one nobody holds — the worker drops all three. The address travels in the body so it never lands in an access log.

Authorizations:
ServiceToken
Request Body schema: application/json
required
address
required
string [ 3 .. 320 ] characters

Responses

Response Schema: application/json
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

local_part
required
string

The claimed base, case-folded, without its tag.

tag
required
string or null

What followed the first +, as sent.

Request samples

Content type
application/json
{
  • "address": "string"
}

Response samples

Content type
application/json
{
  • "tenant": {
    },
  • "local_part": "string",
  • "tag": "string"
}

Read one message, with its bodies and attachment details

content_type and size are joined onto each attachment here because the file rows are one read away on the server and cost a round trip each from a browser.

body.body_html is sanitised before it is stored, but it still comes from an untrusted sender. Render it as untrusted content.

Authorizations:
BearerAuth
path Parameters
email_message_id
required
string
Example: eml_a1b2c3d4e5f6

Responses

Response Schema: application/json
_id
required
string
_class
required
string
_name
required
string

The subject line.

required
object

self is the message's own resource URL. An attachment is fetched from /resource/v1/emails/{id}/attachments/{file_id}. Further rels may be present; a client follows the ones it knows.

direction
required
string
Enum: "inbound" "outbound"
status
required
string
Enum: "received" "sent" "failed" "queued" "quarantined"
from_address
required
string
from_name
required
string or null
to
required
Array of strings
cc
required
Array of strings
subject
required
string
preview
required
string

One line of the message, so the list reads without fetching bodies.

message_id
required
string or null
in_reply_to
required
string or null
references
required
Array of strings
spf_verdict
required
string (EmailVerdict)
Enum: "pass" "fail" "gray" "processing_failed" "not_available"

An authentication result reported by the receiving mail service. SPF and DKIM are recorded and never gate; a message is not authenticated by its From header alone.

dkim_verdict
required
string (EmailVerdict)
Enum: "pass" "fail" "gray" "processing_failed" "not_available"

An authentication result reported by the receiving mail service. SPF and DKIM are recorded and never gate; a message is not authenticated by its From header alone.

dmarc_verdict
required
string (EmailVerdict)
Enum: "pass" "fail" "gray" "processing_failed" "not_available"

An authentication result reported by the receiving mail service. SPF and DKIM are recorded and never gate; a message is not authenticated by its From header alone.

blocked_remote_reference_count
required
integer
attachment_count
required
integer

0 on a stored row, for the reason attachments is empty. The detail response recomputes it from the file rows.

required
Array of objects

Always empty on a stored row: nothing can write it, because a file's parent is the message and the message is minted before any file exists. The detail response replaces it with the files actually parented on the message, which is the tenant-isolated fact.

todo_kind
required
string (TodoKind)
Enum: "action" "reply" "notice"

What the message is for. action is something Raccoon can do, reply something only a person can answer, notice a message that needs nothing. Every stored message carries one; there is no absent state, and it is not editable.

todo_status
required
string (TodoStatus)
Enum: "open" "done"

Whether anyone has dealt with the message. Not a read flag — a shared queue needs one answer to "has anyone dealt with this", not one per person. An outbound message is done when it is recorded.

required
Reference (object) or null

Who has picked this message up, or null while nobody has. Never a permission: every signed-in user of the tenant can read, open, re-assign and complete any message whoever owns it.

required
Reference (object) or null

Who completed it. Null on an outbound message, which is done because it was sent rather than because anyone cleared it.

done_at
required
integer or null

Unix epoch seconds.

required
Reference (object) or null
failure_reason
required
string or null

Why the channel refused an outbound message — the provider's own error code, not a translated sentence. null on anything that is not failed.

document_link_source
required
string (DocumentLinkSource)
Enum: "tag" "header" "manual" "none"

How the link to a document was made — from the reply-to thread token, from a mail header, by hand, or not at all.

thread_token
required
string or null
raw_mime_key
required
string or null

Null once the 90-day retention has passed, rather than a key whose object no longer exists.

raw_mime_expires_at
required
integer or null
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

created_at
required
integer

Unix epoch seconds

updated_at
required
integer

Unix epoch seconds

required
Reference (object) or null
required
EmailBody (object) or null

Response samples

Content type
application/json
{
  • "_id": "eml_a1b2c3d4e5f6",
  • "_class": "email_message",
  • "_name": "string",
  • "_links": {
    },
  • "tenant": {
    },
  • "direction": "inbound",
  • "status": "received",
  • "from_address": "string",
  • "from_name": "string",
  • "to": [
    ],
  • "cc": [
    ],
  • "subject": "string",
  • "preview": "string",
  • "message_id": "string",
  • "in_reply_to": "string",
  • "references": [
    ],
  • "spf_verdict": "pass",
  • "dkim_verdict": "pass",
  • "dmarc_verdict": "pass",
  • "blocked_remote_reference_count": 0,
  • "attachment_count": 0,
  • "attachments": [
    ],
  • "todo_kind": "action",
  • "todo_status": "open",
  • "assignee": {
    },
  • "done_by": {
    },
  • "done_at": 0,
  • "document": {
    },
  • "failure_reason": "string",
  • "document_link_source": "tag",
  • "thread_token": "string",
  • "raw_mime_key": "string",
  • "raw_mime_expires_at": 0,
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    },
  • "body": {
    }
}

Assign the message's to-do, or complete it

The only tenant write on the store. It takes assignee and todo_status and refuses every other key with a 422, so the envelope, headers, verdicts and bodies stay what the worker wrote.

Any signed-in user of the tenant may assign or complete any message, including one somebody else owns. Assignment says who picked the work up; it is not a permission and it narrows nobody's view.

An omitted field is left alone. assignee: null hands the message back to the queue. Completing stamps done_by and done_at with the caller and the moment; reopening clears both.

Authorizations:
BearerAuth
path Parameters
email_message_id
required
string
Example: eml_a1b2c3d4e5f6
Request Body schema: application/json
required
Reference (object) or null

The user who owns this message, or null for nobody.

todo_status
string
Enum: "open" "done"

Omit it to leave the status alone. There is no null status, so an explicit null is refused rather than answering 200 having changed nothing — unlike assignee, where null is the real hand-back to the queue.

Responses

Response Schema: application/json
_id
required
string
_class
required
string
_name
required
string

The subject line.

required
object

self is the message's own resource URL. An attachment is fetched from /resource/v1/emails/{id}/attachments/{file_id}. Further rels may be present; a client follows the ones it knows.

direction
required
string
Enum: "inbound" "outbound"
status
required
string
Enum: "received" "sent" "failed" "queued" "quarantined"
from_address
required
string
from_name
required
string or null
to
required
Array of strings
cc
required
Array of strings
subject
required
string
preview
required
string

One line of the message, so the list reads without fetching bodies.

message_id
required
string or null
in_reply_to
required
string or null
references
required
Array of strings
spf_verdict
required
string (EmailVerdict)
Enum: "pass" "fail" "gray" "processing_failed" "not_available"

An authentication result reported by the receiving mail service. SPF and DKIM are recorded and never gate; a message is not authenticated by its From header alone.

dkim_verdict
required
string (EmailVerdict)
Enum: "pass" "fail" "gray" "processing_failed" "not_available"

An authentication result reported by the receiving mail service. SPF and DKIM are recorded and never gate; a message is not authenticated by its From header alone.

dmarc_verdict
required
string (EmailVerdict)
Enum: "pass" "fail" "gray" "processing_failed" "not_available"

An authentication result reported by the receiving mail service. SPF and DKIM are recorded and never gate; a message is not authenticated by its From header alone.

blocked_remote_reference_count
required
integer
attachment_count
required
integer

0 on a stored row, for the reason attachments is empty. The detail response recomputes it from the file rows.

required
Array of objects (Reference)

Always empty on a stored row: nothing can write it, because a file's parent is the message and the message is minted before any file exists. The detail response replaces it with the files actually parented on the message, which is the tenant-isolated fact.

todo_kind
required
string (TodoKind)
Enum: "action" "reply" "notice"

What the message is for. action is something Raccoon can do, reply something only a person can answer, notice a message that needs nothing. Every stored message carries one; there is no absent state, and it is not editable.

todo_status
required
string (TodoStatus)
Enum: "open" "done"

Whether anyone has dealt with the message. Not a read flag — a shared queue needs one answer to "has anyone dealt with this", not one per person. An outbound message is done when it is recorded.

required
Reference (object) or null

Who has picked this message up, or null while nobody has. Never a permission: every signed-in user of the tenant can read, open, re-assign and complete any message whoever owns it.

required
Reference (object) or null

Who completed it. Null on an outbound message, which is done because it was sent rather than because anyone cleared it.

done_at
required
integer or null

Unix epoch seconds.

required
Reference (object) or null
failure_reason
required
string or null

Why the channel refused an outbound message — the provider's own error code, not a translated sentence. null on anything that is not failed.

document_link_source
required
string (DocumentLinkSource)
Enum: "tag" "header" "manual" "none"

How the link to a document was made — from the reply-to thread token, from a mail header, by hand, or not at all.

thread_token
required
string or null
raw_mime_key
required
string or null

Null once the 90-day retention has passed, rather than a key whose object no longer exists.

raw_mime_expires_at
required
integer or null
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

created_at
required
integer

Unix epoch seconds

updated_at
required
integer

Unix epoch seconds

required
Reference (object) or null

Request samples

Content type
application/json
{
  • "assignee": {
    },
  • "todo_status": "open"
}

Response samples

Content type
application/json
{
  • "_id": "eml_a1b2c3d4e5f6",
  • "_class": "email_message",
  • "_name": "string",
  • "_links": {
    },
  • "tenant": {
    },
  • "direction": "inbound",
  • "status": "received",
  • "from_address": "string",
  • "from_name": "string",
  • "to": [
    ],
  • "cc": [
    ],
  • "subject": "string",
  • "preview": "string",
  • "message_id": "string",
  • "in_reply_to": "string",
  • "references": [
    ],
  • "spf_verdict": "pass",
  • "dkim_verdict": "pass",
  • "dmarc_verdict": "pass",
  • "blocked_remote_reference_count": 0,
  • "attachment_count": 0,
  • "attachments": [
    ],
  • "todo_kind": "action",
  • "todo_status": "open",
  • "assignee": {
    },
  • "done_by": {
    },
  • "done_at": 0,
  • "document": {
    },
  • "failure_reason": "string",
  • "document_link_source": "tag",
  • "thread_token": "string",
  • "raw_mime_key": "string",
  • "raw_mime_expires_at": 0,
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    }
}

Download one attachment of one message

Ordinary bearer auth. Answers a presigned URL rather than the bytes, like every other file download.

The file must be an attachment of this message: without that check the route would be a way to read any file in the tenant through a message id.

Authorizations:
BearerAuth
path Parameters
email_message_id
required
string
Example: eml_a1b2c3d4e5f6
file_id
required
string
Example: fil_a1b2c3d4e5f6

Responses

Response Schema: application/json
url
required
string

Presigned S3 URL for the attachment.

expires_at
required
integer

Unix epoch seconds after which the URL stops working.

Response samples

Content type
application/json
{
  • "url": "string",
  • "expires_at": 0
}

Projects

List projects for the authenticated tenant

Authorizations:
BearerAuth
query Parameters
limit
integer [ 1 .. 200 ]
Default: 50
next_token
string

Base64-encoded pagination cursor from previous response

q
string non-empty

Full-text search term. When provided, the endpoint delegates to the shared search index (matches across name/title/email/identifier fields, tenant-scoped) and returns the matching entities in the same response shape as the unfiltered list. Pagination (limit, next_token) is not honoured in this mode; results are capped at the search index size limit (20 items in MVP).

Responses

Response Schema: application/json
required
Array of objects (ProjectResponse)
next_token
required
string or null

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_token": "string"
}

Create a project

Authorizations:
BearerAuth
Request Body schema: application/json
required
title
required
string [ 1 .. 200 ] characters
type_id
string [ 1 .. 100 ] characters

The project-class tenant type this project is created under. Every project has one. Omitted, the project is created under the tenant's default project type; that is a 422 when the tenant has several and marks none of them default.

schema_version
string or null

Type version. Omitted = latest.

object

Custom field values keyed by section field IDs.

Responses

Response Schema: application/json
_id
required
string
_class
required
string
_name
required
string (EntityName)

Read-only, backend-composed display label of this entity (a person's full name, a product's name, a project's title, …). A backend-meta field (sibling of _id / _class) — never accepted on input.

required
object

self is the project's own resource URL, texts the collection of the texts it carries. Further rels may be present; a client follows the ones it knows.

title
required
string
type_id
required
string

The project-class tenant type this project was created under.

required
object (ReferenceValue)

Stored value for a field of type "reference"

required
object

Custom field values; {} when the type declares no fields

created_at
required
integer

Unix epoch seconds

updated_at
required
integer

Unix epoch seconds

required
ReferenceValue (object) or null
required
Array of objects (IdentifierResponse)

The numbers this record carries (ISBN, GTIN, VAT ID, ...). Stored on the record itself; the identifiers sub-resource addresses one entry.

required
Array of objects (PrintedIdentifier)

Read-only. The numbers the record's type marked primary, in the order it declared them — what a picker option, a search row or a detail header shows without resolving the type itself.

Request samples

Content type
application/json
{
  • "title": "Q2 2026 Launch",
  • "type_id": "client_engagement",
  • "schema_version": "string",
  • "data": { }
}

Response samples

Content type
application/json
{
  • "_links": {
    },
  • "primary_identifiers": [
    ],
  • "identifiers": [
    ],
  • "_id": "prj_a1b2c3d4e5f6",
  • "_class": "project",
  • "_name": "Acme Corp",
  • "title": "string",
  • "type_id": "string",
  • "tenant": {
    },
  • "data": { },
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    }
}

Get a project

Authorizations:
BearerAuth
path Parameters
project_id
required
string
Example: prj_a1b2c3d4e5f6

Responses

Response Schema: application/json
_id
required
string
_class
required
string
_name
required
string (EntityName)

Read-only, backend-composed display label of this entity (a person's full name, a product's name, a project's title, …). A backend-meta field (sibling of _id / _class) — never accepted on input.

required
object

self is the project's own resource URL, texts the collection of the texts it carries. Further rels may be present; a client follows the ones it knows.

title
required
string
type_id
required
string

The project-class tenant type this project was created under.

required
object (ReferenceValue)

Stored value for a field of type "reference"

required
object

Custom field values; {} when the type declares no fields

created_at
required
integer

Unix epoch seconds

updated_at
required
integer

Unix epoch seconds

required
ReferenceValue (object) or null
required
Array of objects (IdentifierResponse)

The numbers this record carries (ISBN, GTIN, VAT ID, ...). Stored on the record itself; the identifiers sub-resource addresses one entry.

required
Array of objects (PrintedIdentifier)

Read-only. The numbers the record's type marked primary, in the order it declared them — what a picker option, a search row or a detail header shows without resolving the type itself.

Response samples

Content type
application/json
{
  • "_links": {
    },
  • "primary_identifiers": [
    ],
  • "identifiers": [
    ],
  • "_id": "prj_a1b2c3d4e5f6",
  • "_class": "project",
  • "_name": "Acme Corp",
  • "title": "string",
  • "type_id": "string",
  • "tenant": {
    },
  • "data": { },
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    }
}

Partially update a project

Authorizations:
BearerAuth
path Parameters
project_id
required
string
Example: prj_a1b2c3d4e5f6
Request Body schema: application/json
required
title
string [ 1 .. 200 ] characters
type_id
string or null
schema_version
string or null
object

Responses

Response Schema: application/json
_id
required
string
_class
required
string
_name
required
string (EntityName)

Read-only, backend-composed display label of this entity (a person's full name, a product's name, a project's title, …). A backend-meta field (sibling of _id / _class) — never accepted on input.

required
object

self is the project's own resource URL, texts the collection of the texts it carries. Further rels may be present; a client follows the ones it knows.

title
required
string
type_id
required
string

The project-class tenant type this project was created under.

required
object (ReferenceValue)

Stored value for a field of type "reference"

required
object

Custom field values; {} when the type declares no fields

created_at
required
integer

Unix epoch seconds

updated_at
required
integer

Unix epoch seconds

required
ReferenceValue (object) or null
required
Array of objects (IdentifierResponse)

The numbers this record carries (ISBN, GTIN, VAT ID, ...). Stored on the record itself; the identifiers sub-resource addresses one entry.

required
Array of objects (PrintedIdentifier)

Read-only. The numbers the record's type marked primary, in the order it declared them — what a picker option, a search row or a detail header shows without resolving the type itself.

Request samples

Content type
application/json
{
  • "title": "string",
  • "type_id": "string",
  • "schema_version": "string",
  • "data": { }
}

Response samples

Content type
application/json
{
  • "_links": {
    },
  • "primary_identifiers": [
    ],
  • "identifiers": [
    ],
  • "_id": "prj_a1b2c3d4e5f6",
  • "_class": "project",
  • "_name": "Acme Corp",
  • "title": "string",
  • "type_id": "string",
  • "tenant": {
    },
  • "data": { },
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    }
}

Delete a project

Authorizations:
BearerAuth
path Parameters
project_id
required
string
Example: prj_a1b2c3d4e5f6

Responses

Response samples

Content type
application/json
{
  • "error": "not_found",
  • "message": "Business partner not found",
  • "details": { }
}

Custom Objects

List a tenant type's custom objects (type-scoped, name-sorted)

Lists the custom objects of one tenant custom_object-class type. The type query parameter is required — objects are only ever viewed within their type group, and the query is strongly consistent.

Authorizations:
BearerAuth
query Parameters
type
required
string
Example: type=series
limit
integer [ 1 .. 200 ]
Default: 50
next_token
string

Base64-encoded pagination cursor from previous response

Responses

Response Schema: application/json
required
Array of objects (CustomObjectResponse)
next_token
required
string or null

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_token": "string"
}

Create a custom object

Authorizations:
BearerAuth
Request Body schema: application/json
required
type_id
required
string [ 1 .. 100 ] characters

The tenant custom_object-class type this record is shaped by (e.g. "series").

name
required
string [ 1 .. 200 ] characters
object

Custom field values keyed by the type's section field IDs.

Responses

Response Schema: application/json
_id
required
string
_class
required
string
_name
required
string (EntityName)

Read-only, backend-composed display label of this entity (a person's full name, a product's name, a project's title, …). A backend-meta field (sibling of _id / _class) — never accepted on input.

required
object

self is the custom object's own resource URL, texts the collection of the texts it carries. Further rels may be present; a client follows the ones it knows.

type_id
required
string
name
required
string
schema_version
required
string
required
object (ReferenceValue)

Stored value for a field of type "reference"

required
object
created_at
required
integer

Unix epoch seconds

updated_at
required
integer

Unix epoch seconds

required
ReferenceValue (object) or null
required
Array of objects (IdentifierResponse)

The numbers this record carries (ISBN, GTIN, VAT ID, ...). Stored on the record itself; the identifiers sub-resource addresses one entry.

required
Array of objects (PrintedIdentifier)

Read-only. The numbers the record's type marked primary, in the order it declared them — what a picker option, a search row or a detail header shows without resolving the type itself.

Request samples

Content type
application/json
{
  • "type_id": "string",
  • "name": "Studies in Modern Poetry",
  • "data": { }
}

Response samples

Content type
application/json
{
  • "_links": {
    },
  • "primary_identifiers": [
    ],
  • "identifiers": [
    ],
  • "_id": "cob_a1b2c3d4e5f6",
  • "_class": "custom_object",
  • "_name": "Acme Corp",
  • "type_id": "series",
  • "name": "string",
  • "schema_version": "1.0.0",
  • "tenant": {
    },
  • "data": { },
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    }
}

Get a custom object

Authorizations:
BearerAuth
path Parameters
custom_object_id
required
string
Example: cob_a1b2c3d4e5f6

Responses

Response Schema: application/json
_id
required
string
_class
required
string
_name
required
string (EntityName)

Read-only, backend-composed display label of this entity (a person's full name, a product's name, a project's title, …). A backend-meta field (sibling of _id / _class) — never accepted on input.

required
object

self is the custom object's own resource URL, texts the collection of the texts it carries. Further rels may be present; a client follows the ones it knows.

type_id
required
string
name
required
string
schema_version
required
string
required
object (ReferenceValue)

Stored value for a field of type "reference"

required
object
created_at
required
integer

Unix epoch seconds

updated_at
required
integer

Unix epoch seconds

required
ReferenceValue (object) or null
required
Array of objects (IdentifierResponse)

The numbers this record carries (ISBN, GTIN, VAT ID, ...). Stored on the record itself; the identifiers sub-resource addresses one entry.

required
Array of objects (PrintedIdentifier)

Read-only. The numbers the record's type marked primary, in the order it declared them — what a picker option, a search row or a detail header shows without resolving the type itself.

Response samples

Content type
application/json
{
  • "_links": {
    },
  • "primary_identifiers": [
    ],
  • "identifiers": [
    ],
  • "_id": "cob_a1b2c3d4e5f6",
  • "_class": "custom_object",
  • "_name": "Acme Corp",
  • "type_id": "series",
  • "name": "string",
  • "schema_version": "1.0.0",
  • "tenant": {
    },
  • "data": { },
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    }
}

Replace a custom object

Authorizations:
BearerAuth
path Parameters
custom_object_id
required
string
Example: cob_a1b2c3d4e5f6
Request Body schema: application/json
required
type_id
string [ 1 .. 100 ] characters
name
string [ 1 .. 200 ] characters
object

Responses

Response Schema: application/json
_id
required
string
_class
required
string
_name
required
string (EntityName)

Read-only, backend-composed display label of this entity (a person's full name, a product's name, a project's title, …). A backend-meta field (sibling of _id / _class) — never accepted on input.

required
object

self is the custom object's own resource URL, texts the collection of the texts it carries. Further rels may be present; a client follows the ones it knows.

type_id
required
string
name
required
string
schema_version
required
string
required
object (ReferenceValue)

Stored value for a field of type "reference"

required
object
created_at
required
integer

Unix epoch seconds

updated_at
required
integer

Unix epoch seconds

required
ReferenceValue (object) or null
required
Array of objects (IdentifierResponse)

The numbers this record carries (ISBN, GTIN, VAT ID, ...). Stored on the record itself; the identifiers sub-resource addresses one entry.

required
Array of objects (PrintedIdentifier)

Read-only. The numbers the record's type marked primary, in the order it declared them — what a picker option, a search row or a detail header shows without resolving the type itself.

Request samples

Content type
application/json
{
  • "type_id": "string",
  • "name": "string",
  • "data": { }
}

Response samples

Content type
application/json
{
  • "_links": {
    },
  • "primary_identifiers": [
    ],
  • "identifiers": [
    ],
  • "_id": "cob_a1b2c3d4e5f6",
  • "_class": "custom_object",
  • "_name": "Acme Corp",
  • "type_id": "series",
  • "name": "string",
  • "schema_version": "1.0.0",
  • "tenant": {
    },
  • "data": { },
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    }
}

Partially update a custom object

Authorizations:
BearerAuth
path Parameters
custom_object_id
required
string
Example: cob_a1b2c3d4e5f6
Request Body schema: application/json
required
type_id
string [ 1 .. 100 ] characters
name
string [ 1 .. 200 ] characters
object

Responses

Response Schema: application/json
_id
required
string
_class
required
string
_name
required
string (EntityName)

Read-only, backend-composed display label of this entity (a person's full name, a product's name, a project's title, …). A backend-meta field (sibling of _id / _class) — never accepted on input.

required
object

self is the custom object's own resource URL, texts the collection of the texts it carries. Further rels may be present; a client follows the ones it knows.

type_id
required
string
name
required
string
schema_version
required
string
required
object (ReferenceValue)

Stored value for a field of type "reference"

required
object
created_at
required
integer

Unix epoch seconds

updated_at
required
integer

Unix epoch seconds

required
ReferenceValue (object) or null
required
Array of objects (IdentifierResponse)

The numbers this record carries (ISBN, GTIN, VAT ID, ...). Stored on the record itself; the identifiers sub-resource addresses one entry.

required
Array of objects (PrintedIdentifier)

Read-only. The numbers the record's type marked primary, in the order it declared them — what a picker option, a search row or a detail header shows without resolving the type itself.

Request samples

Content type
application/json
{
  • "type_id": "string",
  • "name": "string",
  • "data": { }
}

Response samples

Content type
application/json
{
  • "_links": {
    },
  • "primary_identifiers": [
    ],
  • "identifiers": [
    ],
  • "_id": "cob_a1b2c3d4e5f6",
  • "_class": "custom_object",
  • "_name": "Acme Corp",
  • "type_id": "series",
  • "name": "string",
  • "schema_version": "1.0.0",
  • "tenant": {
    },
  • "data": { },
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    }
}

Delete a custom object

Authorizations:
BearerAuth
path Parameters
custom_object_id
required
string
Example: cob_a1b2c3d4e5f6

Responses

Response samples

Content type
application/json
{
  • "error": "not_found",
  • "message": "Business partner not found",
  • "details": { }
}

Files

Issue a presigned upload form for a new file

Validates the request against the file kind named by type — a declaration on the parent's type, or a system kind — enforces cardinality (a cardinality: one kind rejects a second active upload with HTTP 409), and returns a presigned POST form scoped to the caller's tenant prefix in S3.

Some system kinds are written by a service rather than by a person: the generated documents (invoice.document_pdf, invoice.document_xml, offer.document_pdf, stock_dispatch.document_pdf) by the document generator, and email_message.attachment by the inbound-mail worker. Each is written by that one service: a human caller gets 403, and so does a service posting a kind it does not own.

A declared kind is resolved against the parent, so a parent that does not exist in the caller's tenant is 404.

After uploading the bytes via the returned upload form, an S3 event promotes the row from status=pending to ready (or failed).

Authorizations:
BearerAuth
Request Body schema: application/json
required
required
object (FileParent)

The record a file hangs off. The classes listed here are the file parents the backend accepts; a reference to anything else is rejected with 422.

type
required
string

Either a file kind the parent's type declares — fkd_ followed by 12 hex digits — or a system kind, <parent_class>.<name>. Anything else is rejected with 422 files.unknown_kind.

file_name
required
string
file_size
required
integer >= 1
mime_type
required
string^[\w.+-]+/[\w.+-]+(\s*;.*)?$
description
string or null
language
string or null^[a-z]{2}$

ISO 639-1 language of the content.

content_id
string or null [ 1 .. 250 ] characters ^[\x21-\x7e]+$

The Content-ID of the MIME part an email attachment came from, without angle brackets. Accepted on email_message.attachment only; any other kind answers 422.

ReferenceValue (object) or null

The file this upload takes the place of, for a kind that holds one file. It must hang off the same parent under the same kind. The named row keeps serving downloads until the replacement reaches ready, and is soft-deleted at that moment; a failed upload leaves it alone.

Responses

Response Schema: application/json
_id
required
string
_class
required
string
Value: "file"
_name
required
string (EntityName)

Read-only, backend-composed display label of this entity (a person's full name, a product's name, a project's title, …). A backend-meta field (sibling of _id / _class) — never accepted on input.

required
object (ReferenceValue)

Stored value for a field of type "reference"

required
object (FileParent)

The record a file hangs off. The classes listed here are the file parents the backend accepts; a reference to anything else is rejected with 422.

type
required
string
file_name
required
string
file_size
required
integer
mime_type
required
string
sha256
required
string or null

Hex-encoded SHA-256, populated once status=ready.

status
required
string (FileStatus)
Enum: "pending" "ready" "quarantined" "failed" "soft_deleted"

Lifecycle state of a file. pending is set when the upload URL is issued; the S3 ObjectCreated event handler promotes to ready after validation, or to failed/quarantined if validation or malware scanning rejects the object.

retention_class
required
string
Enum: "transient" "standard" "archival"
legal_hold
required
boolean
description
required
string or null
language
required
string or null

ISO 639-1 language of the content.

content_id
required
string or null

The Content-ID of the MIME part an email attachment came from, without angle brackets. Null on every other file.

required
ReferenceValue (object) or null

The file this one replaces while its upload is still in flight; null once the swap completed or when nothing was replaced.

required
object

self is the file's own resource URL; download is the link to the download endpoint.

created_at
required
integer
updated_at
required
integer
required
ReferenceValue (object) or null
required
object (PresignedPost)

S3 presigned-POST form. The client must POST multipart/form-data with every field from fields plus a final file part containing the binary body.

Request samples

Content type
application/json
{
  • "parent": {
    },
  • "type": "fkd_9f3c1a2b4d5e",
  • "file_name": "string",
  • "file_size": 1,
  • "mime_type": "image/png",
  • "description": "string",
  • "language": "de",
  • "content_id": "string",
  • "replaces": {
    }
}

Response samples

Content type
application/json
{
  • "_id": "fil_a1b2c3d4e5f6",
  • "_class": "file",
  • "_name": "Acme Corp",
  • "tenant": {
    },
  • "parent": {
    },
  • "type": "fkd_9f3c1a2b4d5e",
  • "file_name": "string",
  • "file_size": 0,
  • "mime_type": "string",
  • "sha256": "string",
  • "status": "pending",
  • "retention_class": "transient",
  • "legal_hold": true,
  • "description": "string",
  • "language": "string",
  • "content_id": "string",
  • "replaces": {
    },
  • "_links": {
    },
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    },
  • "upload": {
    }
}

List files attached to a parent entity

Authorizations:
BearerAuth
query Parameters
parent_id
required
string
parent_class
required
string
Enum: "business_partner" "product" "project" "invoice" "offer" "custom_object" "tenant" "chat_session" "bank_statement" "stock_dispatch" "email_message"
type
string

Filter by namespaced file-type key.

limit
integer <= 200
Default: 50
next_token
string

Responses

Response Schema: application/json
required
Array of objects (FileResponse)
next_token
required
string or null

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_token": "string"
}

Presigned URLs for the pictures of a page of records

One call for a whole list page: the picture of each named record — the newest ready file of the kind its type marks role: avatar — with a short-lived presigned URL.

A record without a picture is left out of the answer, as is one that does not exist in the caller's tenant: the lookup never leaves the tenant's partition, so a foreign id returns nothing.

Authorizations:
BearerAuth
Request Body schema: application/json
required
required
Array of objects (FileDownloadUrlsParent) [ 1 .. 200 ] items

The records whose pictures are wanted. Each entry needs _id and _class; more than 200 is 422.

Responses

Response Schema: application/json
required
Array of objects

One entry per record that has a picture. A record without one — and a record of another tenant — is absent.

Request samples

Content type
application/json
{
  • "parents": [
    ]
}

Response samples

Content type
application/json
{
  • "items": [
    ]
}

Get file metadata

Authorizations:
BearerAuth
path Parameters
file_id
required
string
Example: fil_a1b2c3d4e5f6

Responses

Response Schema: application/json
_id
required
string
_class
required
string
Value: "file"
_name
required
string (EntityName)

Read-only, backend-composed display label of this entity (a person's full name, a product's name, a project's title, …). A backend-meta field (sibling of _id / _class) — never accepted on input.

required
object (ReferenceValue)

Stored value for a field of type "reference"

required
object (FileParent)

The record a file hangs off. The classes listed here are the file parents the backend accepts; a reference to anything else is rejected with 422.

type
required
string
file_name
required
string
file_size
required
integer
mime_type
required
string
sha256
required
string or null

Hex-encoded SHA-256, populated once status=ready.

status
required
string (FileStatus)
Enum: "pending" "ready" "quarantined" "failed" "soft_deleted"

Lifecycle state of a file. pending is set when the upload URL is issued; the S3 ObjectCreated event handler promotes to ready after validation, or to failed/quarantined if validation or malware scanning rejects the object.

retention_class
required
string
Enum: "transient" "standard" "archival"
legal_hold
required
boolean
description
required
string or null
language
required
string or null

ISO 639-1 language of the content.

content_id
required
string or null

The Content-ID of the MIME part an email attachment came from, without angle brackets. Null on every other file.

required
ReferenceValue (object) or null

The file this one replaces while its upload is still in flight; null once the swap completed or when nothing was replaced.

required
object

self is the file's own resource URL; download is the link to the download endpoint.

created_at
required
integer
updated_at
required
integer
required
ReferenceValue (object) or null

Response samples

Content type
application/json
{
  • "_id": "fil_a1b2c3d4e5f6",
  • "_class": "file",
  • "_name": "Acme Corp",
  • "tenant": {
    },
  • "parent": {
    },
  • "type": "fkd_9f3c1a2b4d5e",
  • "file_name": "string",
  • "file_size": 0,
  • "mime_type": "string",
  • "sha256": "string",
  • "status": "pending",
  • "retention_class": "transient",
  • "legal_hold": true,
  • "description": "string",
  • "language": "string",
  • "content_id": "string",
  • "replaces": {
    },
  • "_links": {
    },
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    }
}

Soft-delete a file

Sets status=soft_deleted and deleted_at. The S3 object is kept for 30 days then hard-deleted by a nightly cleanup, unless the file carries legal_hold=true.

Authorizations:
BearerAuth
path Parameters
file_id
required
string
Example: fil_a1b2c3d4e5f6

Responses

Response samples

Content type
application/json
{
  • "error": "not_found",
  • "message": "Business partner not found",
  • "details": { }
}

Issue a short-lived presigned download URL

Authorizations:
BearerAuth
path Parameters
file_id
required
string

Responses

Response Schema: application/json
url
required
string <uri>
expires_at
required
integer

Response samples

Content type
application/json
{}

Codelists

List codelists in the catalog (global + tenant)

Authorizations:
BearerAuth
query Parameters
scope
string
Enum: "global" "tenant"
namespace
string
industry
string
authority
string
status
string
Enum: "draft" "active" "deprecated"

Responses

Response Schema: application/json
required
Array of objects (CodelistHeader)
count
required
integer

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "count": 0
}

Get a codelist header

Authorizations:
BearerAuth
path Parameters
namespace
required
string

Codelist namespace; may contain slashes (e.g. iso/3166-1). Pass each segment as a separate path segment.

version
required
string

Responses

Response Schema: application/json
_id
required
string
_class
required
string
_name
required
string (EntityName)

Read-only, backend-composed display label of this entity (a person's full name, a product's name, a project's title, …). A backend-meta field (sibling of _id / _class) — never accepted on input.

namespace
required
string
version
required
string
scope
required
string
Enum: "global" "tenant"
status
required
string
Enum: "draft" "active" "deprecated"
required
object
loading
required
string
Enum: "eager" "lazy"
authority
string
industry
Array of strings
object
is_stub
boolean
created_at
integer
updated_at
integer

Response samples

Content type
application/json
{
  • "_id": "cl_iso_3166_1_2020",
  • "_class": "codelist",
  • "_name": "Acme Corp",
  • "namespace": "iso/3166-1",
  • "version": "2020",
  • "authority": "string",
  • "industry": [
    ],
  • "scope": "global",
  • "display_name": {
    },
  • "description": {
    },
  • "loading": "eager",
  • "status": "draft",
  • "is_stub": true,
  • "created_at": 0,
  • "updated_at": 0
}

List codes in a codelist

Authorizations:
BearerAuth
path Parameters
namespace
required
string
version
required
string
query Parameters
locale
string
Default: "en"

Preferred display language. Never filters which codes are returned; every returned row carries all its translations in display_name, and clients pick their language from there.

parent_code
string
status
string
Enum: "active" "deprecated" "retired"
q
string
limit
integer
Default: 50
offset
integer
Default: 0

Responses

Response Schema: application/json
required
Array of objects (CodeValueResponse)
count
required
integer
total
required
integer

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "count": 0,
  • "total": 0
}

Get a single code

Authorizations:
BearerAuth
path Parameters
namespace
required
string
version
required
string
code
required
string

Responses

Response Schema: application/json
_id
required
string
_class
required
string
namespace
required
string
version
required
string
code
required
string
status
required
string
Enum: "active" "deprecated" "retired"
required
object
required
object (ReferenceValue)

Stored value for a field of type "reference"

object
created_at
integer
updated_at
integer

Response samples

Content type
application/json
{
  • "_id": "EUR",
  • "_class": "code_value",
  • "codelist": {
    },
  • "namespace": "string",
  • "version": "string",
  • "code": "string",
  • "display_name": {
    },
  • "status": "active",
  • "metadata": { },
  • "created_at": 0,
  • "updated_at": 0
}

Walk replaced_by_code chain to a terminal node

Authorizations:
BearerAuth
path Parameters
namespace
required
string
version
required
string
code
required
string

Responses

Response Schema: application/json
required
object
required
Array of objects (CodeValueResponse)
required
object (CodeValueResponse)

Response samples

Content type
application/json
{
  • "from": {
    },
  • "chain": [
    ],
  • "terminal": {
    }
}

Validate a (namespace, version, code) tuple

Authorizations:
BearerAuth
Request Body schema: application/json
required
namespace
required
string
code
required
string
version
string
Default: "latest"
locale
string
Default: "en"

Preferred language for snapshot.display_snapshot. Never affects whether a code validates: an untranslated code is still valid and falls back to English, with snapshot.snapshot_locale reporting the language actually used.

Responses

Response Schema: application/json
valid
required
boolean
namespace
required
string
version
required
string
code
required
string

Request samples

Content type
application/json
{
  • "namespace": "string",
  • "version": "latest",
  • "code": "string",
  • "locale": "en"
}

Response samples

Content type
application/json
{
  • "valid": true,
  • "namespace": "string",
  • "version": "string",
  • "code": "string"
}

Create a tenant codelist (slug becomes part of the namespace)

Authorizations:
BearerAuth
Request Body schema: application/json
required
slug
required
string
required
object

Responses

Response Schema: application/json
_id
required
string
_class
required
string
_name
required
string (EntityName)

Read-only, backend-composed display label of this entity (a person's full name, a product's name, a project's title, …). A backend-meta field (sibling of _id / _class) — never accepted on input.

namespace
required
string
version
required
string
scope
required
string
Enum: "global" "tenant"
status
required
string
Enum: "draft" "active" "deprecated"
required
object
loading
required
string
Enum: "eager" "lazy"
authority
string
industry
Array of strings
object
is_stub
boolean
created_at
integer
updated_at
integer

Request samples

Content type
application/json
{
  • "slug": "my_categories",
  • "codelist": {
    }
}

Response samples

Content type
application/json
{
  • "_id": "cl_iso_3166_1_2020",
  • "_class": "codelist",
  • "_name": "Acme Corp",
  • "namespace": "iso/3166-1",
  • "version": "2020",
  • "authority": "string",
  • "industry": [
    ],
  • "scope": "global",
  • "display_name": {
    },
  • "description": {
    },
  • "loading": "eager",
  • "status": "draft",
  • "is_stub": true,
  • "created_at": 0,
  • "updated_at": 0
}

Publish a new immutable version of a tenant codelist

Authorizations:
BearerAuth
path Parameters
namespace
required
string

Tenant codelist namespace (must start with tenant:).

Request Body schema: application/json
required
version
required
string
published_at
string <date>
authority
string
industry
Array of strings
object
object
licence
string
attribution
string
status
string
Enum: "draft" "active" "deprecated"

Responses

Response Schema: application/json
_id
required
string
_class
required
string
_name
required
string (EntityName)

Read-only, backend-composed display label of this entity (a person's full name, a product's name, a project's title, …). A backend-meta field (sibling of _id / _class) — never accepted on input.

namespace
required
string
version
required
string
scope
required
string
Enum: "global" "tenant"
status
required
string
Enum: "draft" "active" "deprecated"
required
object
loading
required
string
Enum: "eager" "lazy"
authority
string
industry
Array of strings
object
is_stub
boolean
created_at
integer
updated_at
integer

Request samples

Content type
application/json
{
  • "version": "string",
  • "published_at": "2019-08-24",
  • "authority": "string",
  • "industry": [
    ],
  • "display_name": {
    },
  • "description": {
    },
  • "licence": "string",
  • "attribution": "string",
  • "status": "draft"
}

Response samples

Content type
application/json
{
  • "_id": "cl_iso_3166_1_2020",
  • "_class": "codelist",
  • "_name": "Acme Corp",
  • "namespace": "iso/3166-1",
  • "version": "2020",
  • "authority": "string",
  • "industry": [
    ],
  • "scope": "global",
  • "display_name": {
    },
  • "description": {
    },
  • "loading": "eager",
  • "status": "draft",
  • "is_stub": true,
  • "created_at": 0,
  • "updated_at": 0
}

Add a code to a tenant codelist

Authorizations:
BearerAuth
path Parameters
namespace
required
string
version
required
string
Request Body schema: application/json
required
code
required
string
object
object
parent_code
string
valid_from
string <date>
valid_to
string <date>
object

Responses

Response Schema: application/json
_id
required
string
_class
required
string
namespace
required
string
version
required
string
code
required
string
status
required
string
Enum: "active" "deprecated" "retired"
required
object
required
object (ReferenceValue)

Stored value for a field of type "reference"

object
created_at
integer
updated_at
integer

Request samples

Content type
application/json
{
  • "code": "string",
  • "display_name": {
    },
  • "description": {
    },
  • "parent_code": "string",
  • "valid_from": "2019-08-24",
  • "valid_to": "2019-08-24",
  • "metadata": { }
}

Response samples

Content type
application/json
{
  • "_id": "EUR",
  • "_class": "code_value",
  • "codelist": {
    },
  • "namespace": "string",
  • "version": "string",
  • "code": "string",
  • "display_name": {
    },
  • "status": "active",
  • "metadata": { },
  • "created_at": 0,
  • "updated_at": 0
}

Update a tenant codelist code

Authorizations:
BearerAuth
path Parameters
namespace
required
string
version
required
string
code
required
string
Request Body schema: application/json
required
object
object
parent_code
string
valid_from
string <date>
valid_to
string <date>
status
string
Enum: "active" "deprecated" "retired"
replaced_by_code
string
object

Responses

Response Schema: application/json
_id
required
string
_class
required
string
namespace
required
string
version
required
string
code
required
string
status
required
string
Enum: "active" "deprecated" "retired"
required
object
required
object (ReferenceValue)

Stored value for a field of type "reference"

object
created_at
integer
updated_at
integer

Request samples

Content type
application/json
{
  • "display_name": {
    },
  • "description": {
    },
  • "parent_code": "string",
  • "valid_from": "2019-08-24",
  • "valid_to": "2019-08-24",
  • "status": "active",
  • "replaced_by_code": "string",
  • "metadata": { }
}

Response samples

Content type
application/json
{
  • "_id": "EUR",
  • "_class": "code_value",
  • "codelist": {
    },
  • "namespace": "string",
  • "version": "string",
  • "code": "string",
  • "display_name": {
    },
  • "status": "active",
  • "metadata": { },
  • "created_at": 0,
  • "updated_at": 0
}

Delete a code value (only allowed while the version is unpublished)

Authorizations:
BearerAuth
path Parameters
namespace
required
string
version
required
string
code
required
string

Responses

Response samples

Content type
application/json
{
  • "error": "not_found",
  • "message": "Business partner not found",
  • "details": { }
}

Mark a tenant code as deprecated

Authorizations:
BearerAuth
path Parameters
namespace
required
string
version
required
string
code
required
string
Request Body schema: application/json
optional
replaced_by_code
string

Responses

Response Schema: application/json
_id
required
string
_class
required
string
namespace
required
string
version
required
string
code
required
string
status
required
string
Enum: "active" "deprecated" "retired"
required
object
required
object (ReferenceValue)

Stored value for a field of type "reference"

object
created_at
integer
updated_at
integer

Request samples

Content type
application/json
{
  • "replaced_by_code": "string"
}

Response samples

Content type
application/json
{
  • "_id": "EUR",
  • "_class": "code_value",
  • "codelist": {
    },
  • "namespace": "string",
  • "version": "string",
  • "code": "string",
  • "display_name": {
    },
  • "status": "active",
  • "metadata": { },
  • "created_at": 0,
  • "updated_at": 0
}

Identifiers

List identifiers for a company

Authorizations:
BearerAuth
path Parameters
company_id
required
string

Responses

Response Schema: application/json
required
Array of objects (IdentifierResponse)

Response samples

Content type
application/json
{
  • "items": [
    ]
}

Add an identifier to a company

Authorizations:
BearerAuth
path Parameters
company_id
required
string
Request Body schema: application/json
required
schema_key
required
string
value
required
string
label
string or null

Responses

Response Schema: application/json
_id
required
string
_class
required
string
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

schema_key
required
string
value
required
string
required
Array of objects

Zero or more validation results for this identifier value, one per validator. Open shape (each is {type, status, result, ...}); VIES is the first producer. Written only by the validations-append endpoint (worker callback), never by the general identifier PATCH.

created_at
required
integer
updated_at
required
integer
required
ReferenceValue (object) or null

Request samples

Content type
application/json
{
  • "schema_key": "ean13",
  • "value": "4006381333931",
  • "label": "string"
}

Response samples

Content type
application/json
{
  • "_id": "string",
  • "_class": "identifier",
  • "parent": {
    },
  • "schema_key": "string",
  • "value": "string",
  • "validations": [
    ],
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    }
}

Get a company identifier

Authorizations:
BearerAuth
path Parameters
company_id
required
string
identifier_id
required
string

Responses

Response Schema: application/json
_id
required
string
_class
required
string
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

schema_key
required
string
value
required
string
required
Array of objects

Zero or more validation results for this identifier value, one per validator. Open shape (each is {type, status, result, ...}); VIES is the first producer. Written only by the validations-append endpoint (worker callback), never by the general identifier PATCH.

created_at
required
integer
updated_at
required
integer
required
ReferenceValue (object) or null

Response samples

Content type
application/json
{
  • "_id": "string",
  • "_class": "identifier",
  • "parent": {
    },
  • "schema_key": "string",
  • "value": "string",
  • "validations": [
    ],
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    }
}

Update a company identifier

Authorizations:
BearerAuth
path Parameters
company_id
required
string
identifier_id
required
string
Request Body schema: application/json
required
value
string or null
label
string or null

Responses

Response Schema: application/json
_id
required
string
_class
required
string
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

schema_key
required
string
value
required
string
required
Array of objects

Zero or more validation results for this identifier value, one per validator. Open shape (each is {type, status, result, ...}); VIES is the first producer. Written only by the validations-append endpoint (worker callback), never by the general identifier PATCH.

created_at
required
integer
updated_at
required
integer
required
ReferenceValue (object) or null

Request samples

Content type
application/json
{
  • "value": "string",
  • "label": "string"
}

Response samples

Content type
application/json
{
  • "_id": "string",
  • "_class": "identifier",
  • "parent": {
    },
  • "schema_key": "string",
  • "value": "string",
  • "validations": [
    ],
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    }
}

Delete a company identifier

Authorizations:
BearerAuth
path Parameters
company_id
required
string
identifier_id
required
string

Responses

Response samples

Content type
application/json
{
  • "error": "not_found",
  • "message": "Business partner not found",
  • "details": { }
}

Append a validation result to an identifier (async-validator callback)

Authorizations:
BearerAuth
path Parameters
company_id
required
string
identifier_id
required
string
Request Body schema: application/json
required
value
required
string

The identifier value the validator checked (staleness guard).

type
required
string
status
required
string
result
required
string
Enum: "SUCCESS" "INVALID" "UNAVAILABLE"
validated_at
required
integer
name_on_record
string or null
consultation_number
string or null
work_type
string or null

Crossref work type (e.g. journal-article) for a doi result.

log
string or null

Responses

Response Schema: application/json
_id
required
string
_class
required
string
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

schema_key
required
string
value
required
string
required
Array of objects

Zero or more validation results for this identifier value, one per validator. Open shape (each is {type, status, result, ...}); VIES is the first producer. Written only by the validations-append endpoint (worker callback), never by the general identifier PATCH.

created_at
required
integer
updated_at
required
integer
required
ReferenceValue (object) or null
applied
required
boolean

False when the result was dropped (staleness — value changed — or never-downgrade of a cached SUCCESS); the identifier is unchanged.

Request samples

Content type
application/json
{
  • "value": "string",
  • "type": "vies",
  • "status": "completed",
  • "result": "SUCCESS",
  • "validated_at": 0,
  • "name_on_record": "string",
  • "consultation_number": "string",
  • "work_type": "string",
  • "log": "string"
}

Response samples

Content type
application/json
{
  • "_id": "string",
  • "_class": "identifier",
  • "parent": {
    },
  • "schema_key": "string",
  • "value": "string",
  • "validations": [
    ],
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    },
  • "applied": true
}

Cross-entity reverse lookup by identifier value

Returns all identifier records matching the given schema_key and value within the authenticated tenant. Each result contains a parent Reference pointing to the owning entity. Use the nested sub-resource endpoints for CRUD operations on identifiers.

Authorizations:
BearerAuth
query Parameters
schema_key
required
string
Example: schema_key=ean13
identifier
required
string
Example: identifier=4006381333931

Responses

Response Schema: application/json
required
Array of objects (IdentifierResponse)
next_token
required
string or null

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_token": "string"
}

Identifier schemas selectable for an entity class

Returns the platform identifier schemas that can be attached to the given entity class. Every scheme is returned (a company may hold a foreign VAT registration, so none are hidden); when country is supplied the scheme issued by that country (e.g. vat_eu for an EU company, vat_gb for a UK company) is sorted first and flagged recommended. Drives the add-identifier dropdown on the Company form.

Authorizations:
BearerAuth
query Parameters
entity_class
required
string
Example: entity_class=company
country
string
Example: country=DE

ISO 3166-1 alpha-2 code of the entity's home country.

Responses

Response Schema: application/json
required
Array of objects (IdentifierSchema)
next_token
required
string or null

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_token": "string"
}

List identifiers for a product

Authorizations:
BearerAuth
path Parameters
product_id
required
string
Example: prd_a1b2c3d4e5f6

Responses

Response Schema: application/json
required
Array of objects (IdentifierResponse)

Response samples

Content type
application/json
{
  • "items": [
    ]
}

Add an identifier to a product

Authorizations:
BearerAuth
path Parameters
product_id
required
string
Example: prd_a1b2c3d4e5f6
Request Body schema: application/json
required
schema_key
required
string
value
required
string
label
string or null

Responses

Response Schema: application/json
_id
required
string
_class
required
string
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

schema_key
required
string
value
required
string
required
Array of objects

Zero or more validation results for this identifier value, one per validator. Open shape (each is {type, status, result, ...}); VIES is the first producer. Written only by the validations-append endpoint (worker callback), never by the general identifier PATCH.

created_at
required
integer
updated_at
required
integer
required
ReferenceValue (object) or null

Request samples

Content type
application/json
{
  • "schema_key": "ean13",
  • "value": "4006381333931",
  • "label": "string"
}

Response samples

Content type
application/json
{
  • "_id": "string",
  • "_class": "identifier",
  • "parent": {
    },
  • "schema_key": "string",
  • "value": "string",
  • "validations": [
    ],
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    }
}

Get a product identifier

Authorizations:
BearerAuth
path Parameters
product_id
required
string
identifier_id
required
string

Responses

Response Schema: application/json
_id
required
string
_class
required
string
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

schema_key
required
string
value
required
string
required
Array of objects

Zero or more validation results for this identifier value, one per validator. Open shape (each is {type, status, result, ...}); VIES is the first producer. Written only by the validations-append endpoint (worker callback), never by the general identifier PATCH.

created_at
required
integer
updated_at
required
integer
required
ReferenceValue (object) or null

Response samples

Content type
application/json
{
  • "_id": "string",
  • "_class": "identifier",
  • "parent": {
    },
  • "schema_key": "string",
  • "value": "string",
  • "validations": [
    ],
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    }
}

Update a product identifier

Authorizations:
BearerAuth
path Parameters
product_id
required
string
identifier_id
required
string
Request Body schema: application/json
required
value
string or null
label
string or null

Responses

Response Schema: application/json
_id
required
string
_class
required
string
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

schema_key
required
string
value
required
string
required
Array of objects

Zero or more validation results for this identifier value, one per validator. Open shape (each is {type, status, result, ...}); VIES is the first producer. Written only by the validations-append endpoint (worker callback), never by the general identifier PATCH.

created_at
required
integer
updated_at
required
integer
required
ReferenceValue (object) or null

Request samples

Content type
application/json
{
  • "value": "string",
  • "label": "string"
}

Response samples

Content type
application/json
{
  • "_id": "string",
  • "_class": "identifier",
  • "parent": {
    },
  • "schema_key": "string",
  • "value": "string",
  • "validations": [
    ],
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    }
}

Delete a product identifier

Authorizations:
BearerAuth
path Parameters
product_id
required
string
identifier_id
required
string

Responses

Response samples

Content type
application/json
{
  • "error": "not_found",
  • "message": "Business partner not found",
  • "details": { }
}

Append a validation result to an identifier (async-validator callback)

Authorizations:
BearerAuth
path Parameters
product_id
required
string
identifier_id
required
string
Request Body schema: application/json
required
value
required
string

The identifier value the validator checked (staleness guard).

type
required
string
status
required
string
result
required
string
Enum: "SUCCESS" "INVALID" "UNAVAILABLE"
validated_at
required
integer
name_on_record
string or null
consultation_number
string or null
work_type
string or null

Crossref work type (e.g. journal-article) for a doi result.

log
string or null

Responses

Response Schema: application/json
_id
required
string
_class
required
string
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

schema_key
required
string
value
required
string
required
Array of objects

Zero or more validation results for this identifier value, one per validator. Open shape (each is {type, status, result, ...}); VIES is the first producer. Written only by the validations-append endpoint (worker callback), never by the general identifier PATCH.

created_at
required
integer
updated_at
required
integer
required
ReferenceValue (object) or null
applied
required
boolean

False when the result was dropped (staleness — value changed — or never-downgrade of a cached SUCCESS); the identifier is unchanged.

Request samples

Content type
application/json
{
  • "value": "string",
  • "type": "vies",
  • "status": "completed",
  • "result": "SUCCESS",
  • "validated_at": 0,
  • "name_on_record": "string",
  • "consultation_number": "string",
  • "work_type": "string",
  • "log": "string"
}

Response samples

Content type
application/json
{
  • "_id": "string",
  • "_class": "identifier",
  • "parent": {
    },
  • "schema_key": "string",
  • "value": "string",
  • "validations": [
    ],
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    },
  • "applied": true
}

List identifiers for a business partner

Authorizations:
BearerAuth
path Parameters
business_partner_id
required
string

Responses

Response Schema: application/json
required
Array of objects (IdentifierResponse)

Response samples

Content type
application/json
{
  • "items": [
    ]
}

Add an identifier to a business partner

Authorizations:
BearerAuth
path Parameters
business_partner_id
required
string
Request Body schema: application/json
required
schema_key
required
string
value
required
string
label
string or null

Responses

Response Schema: application/json
_id
required
string
_class
required
string
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

schema_key
required
string
value
required
string
required
Array of objects

Zero or more validation results for this identifier value, one per validator. Open shape (each is {type, status, result, ...}); VIES is the first producer. Written only by the validations-append endpoint (worker callback), never by the general identifier PATCH.

created_at
required
integer
updated_at
required
integer
required
ReferenceValue (object) or null

Request samples

Content type
application/json
{
  • "schema_key": "ean13",
  • "value": "4006381333931",
  • "label": "string"
}

Response samples

Content type
application/json
{
  • "_id": "string",
  • "_class": "identifier",
  • "parent": {
    },
  • "schema_key": "string",
  • "value": "string",
  • "validations": [
    ],
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    }
}

Get a business partner identifier

Authorizations:
BearerAuth
path Parameters
business_partner_id
required
string
identifier_id
required
string

Responses

Response Schema: application/json
_id
required
string
_class
required
string
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

schema_key
required
string
value
required
string
required
Array of objects

Zero or more validation results for this identifier value, one per validator. Open shape (each is {type, status, result, ...}); VIES is the first producer. Written only by the validations-append endpoint (worker callback), never by the general identifier PATCH.

created_at
required
integer
updated_at
required
integer
required
ReferenceValue (object) or null

Response samples

Content type
application/json
{
  • "_id": "string",
  • "_class": "identifier",
  • "parent": {
    },
  • "schema_key": "string",
  • "value": "string",
  • "validations": [
    ],
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    }
}

Update a business partner identifier

Authorizations:
BearerAuth
path Parameters
business_partner_id
required
string
identifier_id
required
string
Request Body schema: application/json
required
value
string or null
label
string or null

Responses

Response Schema: application/json
_id
required
string
_class
required
string
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

schema_key
required
string
value
required
string
required
Array of objects

Zero or more validation results for this identifier value, one per validator. Open shape (each is {type, status, result, ...}); VIES is the first producer. Written only by the validations-append endpoint (worker callback), never by the general identifier PATCH.

created_at
required
integer
updated_at
required
integer
required
ReferenceValue (object) or null

Request samples

Content type
application/json
{
  • "value": "string",
  • "label": "string"
}

Response samples

Content type
application/json
{
  • "_id": "string",
  • "_class": "identifier",
  • "parent": {
    },
  • "schema_key": "string",
  • "value": "string",
  • "validations": [
    ],
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    }
}

Delete a business partner identifier

Authorizations:
BearerAuth
path Parameters
business_partner_id
required
string
identifier_id
required
string

Responses

Response samples

Content type
application/json
{
  • "error": "not_found",
  • "message": "Business partner not found",
  • "details": { }
}

Append a validation result to an identifier (async-validator callback)

Dedicated write path for async validators (e.g. the VIES worker) to report a verdict, kept off the general identifier PATCH so a worker write and a concurrent user value-edit can't clobber each other. Applies staleness + never-downgrade rules; applied=false when skipped.

Authorizations:
BearerAuth
path Parameters
business_partner_id
required
string
identifier_id
required
string
Request Body schema: application/json
required
value
required
string

The identifier value the validator checked (staleness guard).

type
required
string
status
required
string
result
required
string
Enum: "SUCCESS" "INVALID" "UNAVAILABLE"
validated_at
required
integer
name_on_record
string or null
consultation_number
string or null
work_type
string or null

Crossref work type (e.g. journal-article) for a doi result.

log
string or null

Responses

Response Schema: application/json
_id
required
string
_class
required
string
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

schema_key
required
string
value
required
string
required
Array of objects

Zero or more validation results for this identifier value, one per validator. Open shape (each is {type, status, result, ...}); VIES is the first producer. Written only by the validations-append endpoint (worker callback), never by the general identifier PATCH.

created_at
required
integer
updated_at
required
integer
required
ReferenceValue (object) or null
applied
required
boolean

False when the result was dropped (staleness — value changed — or never-downgrade of a cached SUCCESS); the identifier is unchanged.

Request samples

Content type
application/json
{
  • "value": "string",
  • "type": "vies",
  • "status": "completed",
  • "result": "SUCCESS",
  • "validated_at": 0,
  • "name_on_record": "string",
  • "consultation_number": "string",
  • "work_type": "string",
  • "log": "string"
}

Response samples

Content type
application/json
{
  • "_id": "string",
  • "_class": "identifier",
  • "parent": {
    },
  • "schema_key": "string",
  • "value": "string",
  • "validations": [
    ],
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    },
  • "applied": true
}

List identifiers for an invoice

Authorizations:
BearerAuth
path Parameters
invoice_id
required
string

Responses

Response Schema: application/json
required
Array of objects (IdentifierResponse)

Response samples

Content type
application/json
{
  • "items": [
    ]
}

Add an identifier to an invoice

Authorizations:
BearerAuth
path Parameters
invoice_id
required
string
Request Body schema: application/json
required
schema_key
required
string
value
required
string
label
string or null

Responses

Response Schema: application/json
_id
required
string
_class
required
string
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

schema_key
required
string
value
required
string
required
Array of objects

Zero or more validation results for this identifier value, one per validator. Open shape (each is {type, status, result, ...}); VIES is the first producer. Written only by the validations-append endpoint (worker callback), never by the general identifier PATCH.

created_at
required
integer
updated_at
required
integer
required
ReferenceValue (object) or null

Request samples

Content type
application/json
{
  • "schema_key": "ean13",
  • "value": "4006381333931",
  • "label": "string"
}

Response samples

Content type
application/json
{
  • "_id": "string",
  • "_class": "identifier",
  • "parent": {
    },
  • "schema_key": "string",
  • "value": "string",
  • "validations": [
    ],
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    }
}

Get an invoice identifier

Authorizations:
BearerAuth
path Parameters
invoice_id
required
string
identifier_id
required
string

Responses

Response Schema: application/json
_id
required
string
_class
required
string
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

schema_key
required
string
value
required
string
required
Array of objects

Zero or more validation results for this identifier value, one per validator. Open shape (each is {type, status, result, ...}); VIES is the first producer. Written only by the validations-append endpoint (worker callback), never by the general identifier PATCH.

created_at
required
integer
updated_at
required
integer
required
ReferenceValue (object) or null

Response samples

Content type
application/json
{
  • "_id": "string",
  • "_class": "identifier",
  • "parent": {
    },
  • "schema_key": "string",
  • "value": "string",
  • "validations": [
    ],
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    }
}

Update an invoice identifier

Authorizations:
BearerAuth
path Parameters
invoice_id
required
string
identifier_id
required
string
Request Body schema: application/json
required
value
string or null
label
string or null

Responses

Response Schema: application/json
_id
required
string
_class
required
string
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

schema_key
required
string
value
required
string
required
Array of objects

Zero or more validation results for this identifier value, one per validator. Open shape (each is {type, status, result, ...}); VIES is the first producer. Written only by the validations-append endpoint (worker callback), never by the general identifier PATCH.

created_at
required
integer
updated_at
required
integer
required
ReferenceValue (object) or null

Request samples

Content type
application/json
{
  • "value": "string",
  • "label": "string"
}

Response samples

Content type
application/json
{
  • "_id": "string",
  • "_class": "identifier",
  • "parent": {
    },
  • "schema_key": "string",
  • "value": "string",
  • "validations": [
    ],
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    }
}

Delete an invoice identifier

Authorizations:
BearerAuth
path Parameters
invoice_id
required
string
identifier_id
required
string

Responses

Response samples

Content type
application/json
{
  • "error": "not_found",
  • "message": "Business partner not found",
  • "details": { }
}

Append a validation result to an identifier (async-validator callback)

Authorizations:
BearerAuth
path Parameters
invoice_id
required
string
identifier_id
required
string
Request Body schema: application/json
required
value
required
string

The identifier value the validator checked (staleness guard).

type
required
string
status
required
string
result
required
string
Enum: "SUCCESS" "INVALID" "UNAVAILABLE"
validated_at
required
integer
name_on_record
string or null
consultation_number
string or null
work_type
string or null

Crossref work type (e.g. journal-article) for a doi result.

log
string or null

Responses

Response Schema: application/json
_id
required
string
_class
required
string
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

schema_key
required
string
value
required
string
required
Array of objects

Zero or more validation results for this identifier value, one per validator. Open shape (each is {type, status, result, ...}); VIES is the first producer. Written only by the validations-append endpoint (worker callback), never by the general identifier PATCH.

created_at
required
integer
updated_at
required
integer
required
ReferenceValue (object) or null
applied
required
boolean

False when the result was dropped (staleness — value changed — or never-downgrade of a cached SUCCESS); the identifier is unchanged.

Request samples

Content type
application/json
{
  • "value": "string",
  • "type": "vies",
  • "status": "completed",
  • "result": "SUCCESS",
  • "validated_at": 0,
  • "name_on_record": "string",
  • "consultation_number": "string",
  • "work_type": "string",
  • "log": "string"
}

Response samples

Content type
application/json
{
  • "_id": "string",
  • "_class": "identifier",
  • "parent": {
    },
  • "schema_key": "string",
  • "value": "string",
  • "validations": [
    ],
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    },
  • "applied": true
}

List identifiers for a project

Authorizations:
BearerAuth
path Parameters
project_id
required
string

Responses

Response Schema: application/json
required
Array of objects (IdentifierResponse)

Response samples

Content type
application/json
{
  • "items": [
    ]
}

Add an identifier to a project

Authorizations:
BearerAuth
path Parameters
project_id
required
string
Request Body schema: application/json
required
schema_key
required
string
value
required
string
label
string or null

Responses

Response Schema: application/json
_id
required
string
_class
required
string
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

schema_key
required
string
value
required
string
required
Array of objects

Zero or more validation results for this identifier value, one per validator. Open shape (each is {type, status, result, ...}); VIES is the first producer. Written only by the validations-append endpoint (worker callback), never by the general identifier PATCH.

created_at
required
integer
updated_at
required
integer
required
ReferenceValue (object) or null

Request samples

Content type
application/json
{
  • "schema_key": "ean13",
  • "value": "4006381333931",
  • "label": "string"
}

Response samples

Content type
application/json
{
  • "_id": "string",
  • "_class": "identifier",
  • "parent": {
    },
  • "schema_key": "string",
  • "value": "string",
  • "validations": [
    ],
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    }
}

Get a project identifier

Authorizations:
BearerAuth
path Parameters
project_id
required
string
identifier_id
required
string

Responses

Response Schema: application/json
_id
required
string
_class
required
string
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

schema_key
required
string
value
required
string
required
Array of objects

Zero or more validation results for this identifier value, one per validator. Open shape (each is {type, status, result, ...}); VIES is the first producer. Written only by the validations-append endpoint (worker callback), never by the general identifier PATCH.

created_at
required
integer
updated_at
required
integer
required
ReferenceValue (object) or null

Response samples

Content type
application/json
{
  • "_id": "string",
  • "_class": "identifier",
  • "parent": {
    },
  • "schema_key": "string",
  • "value": "string",
  • "validations": [
    ],
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    }
}

Update a project identifier

Authorizations:
BearerAuth
path Parameters
project_id
required
string
identifier_id
required
string
Request Body schema: application/json
required
value
string or null
label
string or null

Responses

Response Schema: application/json
_id
required
string
_class
required
string
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

schema_key
required
string
value
required
string
required
Array of objects

Zero or more validation results for this identifier value, one per validator. Open shape (each is {type, status, result, ...}); VIES is the first producer. Written only by the validations-append endpoint (worker callback), never by the general identifier PATCH.

created_at
required
integer
updated_at
required
integer
required
ReferenceValue (object) or null

Request samples

Content type
application/json
{
  • "value": "string",
  • "label": "string"
}

Response samples

Content type
application/json
{
  • "_id": "string",
  • "_class": "identifier",
  • "parent": {
    },
  • "schema_key": "string",
  • "value": "string",
  • "validations": [
    ],
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    }
}

Delete a project identifier

Authorizations:
BearerAuth
path Parameters
project_id
required
string
identifier_id
required
string

Responses

Response samples

Content type
application/json
{
  • "error": "not_found",
  • "message": "Business partner not found",
  • "details": { }
}

Append a validation result to an identifier (async-validator callback)

Authorizations:
BearerAuth
path Parameters
project_id
required
string
identifier_id
required
string
Request Body schema: application/json
required
value
required
string

The identifier value the validator checked (staleness guard).

type
required
string
status
required
string
result
required
string
Enum: "SUCCESS" "INVALID" "UNAVAILABLE"
validated_at
required
integer
name_on_record
string or null
consultation_number
string or null
work_type
string or null

Crossref work type (e.g. journal-article) for a doi result.

log
string or null

Responses

Response Schema: application/json
_id
required
string
_class
required
string
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

schema_key
required
string
value
required
string
required
Array of objects

Zero or more validation results for this identifier value, one per validator. Open shape (each is {type, status, result, ...}); VIES is the first producer. Written only by the validations-append endpoint (worker callback), never by the general identifier PATCH.

created_at
required
integer
updated_at
required
integer
required
ReferenceValue (object) or null
applied
required
boolean

False when the result was dropped (staleness — value changed — or never-downgrade of a cached SUCCESS); the identifier is unchanged.

Request samples

Content type
application/json
{
  • "value": "string",
  • "type": "vies",
  • "status": "completed",
  • "result": "SUCCESS",
  • "validated_at": 0,
  • "name_on_record": "string",
  • "consultation_number": "string",
  • "work_type": "string",
  • "log": "string"
}

Response samples

Content type
application/json
{
  • "_id": "string",
  • "_class": "identifier",
  • "parent": {
    },
  • "schema_key": "string",
  • "value": "string",
  • "validations": [
    ],
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    },
  • "applied": true
}

List identifiers for a custom object

Authorizations:
BearerAuth
path Parameters
custom_object_id
required
string

Responses

Response Schema: application/json
required
Array of objects (IdentifierResponse)

Response samples

Content type
application/json
{
  • "items": [
    ]
}

Add an identifier to a custom object

Authorizations:
BearerAuth
path Parameters
custom_object_id
required
string
Request Body schema: application/json
required
schema_key
required
string
value
required
string
label
string or null

Responses

Response Schema: application/json
_id
required
string
_class
required
string
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

schema_key
required
string
value
required
string
required
Array of objects

Zero or more validation results for this identifier value, one per validator. Open shape (each is {type, status, result, ...}); VIES is the first producer. Written only by the validations-append endpoint (worker callback), never by the general identifier PATCH.

created_at
required
integer
updated_at
required
integer
required
ReferenceValue (object) or null

Request samples

Content type
application/json
{
  • "schema_key": "ean13",
  • "value": "4006381333931",
  • "label": "string"
}

Response samples

Content type
application/json
{
  • "_id": "string",
  • "_class": "identifier",
  • "parent": {
    },
  • "schema_key": "string",
  • "value": "string",
  • "validations": [
    ],
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    }
}

Get a custom-object identifier

Authorizations:
BearerAuth
path Parameters
custom_object_id
required
string
identifier_id
required
string

Responses

Response Schema: application/json
_id
required
string
_class
required
string
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

schema_key
required
string
value
required
string
required
Array of objects

Zero or more validation results for this identifier value, one per validator. Open shape (each is {type, status, result, ...}); VIES is the first producer. Written only by the validations-append endpoint (worker callback), never by the general identifier PATCH.

created_at
required
integer
updated_at
required
integer
required
ReferenceValue (object) or null

Response samples

Content type
application/json
{
  • "_id": "string",
  • "_class": "identifier",
  • "parent": {
    },
  • "schema_key": "string",
  • "value": "string",
  • "validations": [
    ],
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    }
}

Update a custom-object identifier

Authorizations:
BearerAuth
path Parameters
custom_object_id
required
string
identifier_id
required
string
Request Body schema: application/json
required
value
string or null
label
string or null

Responses

Response Schema: application/json
_id
required
string
_class
required
string
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

schema_key
required
string
value
required
string
required
Array of objects

Zero or more validation results for this identifier value, one per validator. Open shape (each is {type, status, result, ...}); VIES is the first producer. Written only by the validations-append endpoint (worker callback), never by the general identifier PATCH.

created_at
required
integer
updated_at
required
integer
required
ReferenceValue (object) or null

Request samples

Content type
application/json
{
  • "value": "string",
  • "label": "string"
}

Response samples

Content type
application/json
{
  • "_id": "string",
  • "_class": "identifier",
  • "parent": {
    },
  • "schema_key": "string",
  • "value": "string",
  • "validations": [
    ],
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    }
}

Delete a custom-object identifier

Authorizations:
BearerAuth
path Parameters
custom_object_id
required
string
identifier_id
required
string

Responses

Response samples

Content type
application/json
{
  • "error": "not_found",
  • "message": "Business partner not found",
  • "details": { }
}

Texts

List the texts a product carries

Authorizations:
BearerAuth
path Parameters
product_id
required
string
Example: prd_a1b2c3d4e5f6
query Parameters
next_token
string

Responses

Response Schema: application/json
required
Array of objects (TextResponse)
next_token
required
string or null

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_token": "string"
}

Write a product's text of one kind

Upsert. Answers 200 with the stored row, or 204 when the value held nothing in any language — that deletes the row, the same as DELETE.

Authorizations:
BearerAuth
path Parameters
product_id
required
string
text_kind_key
required
string
Example: tkd_0a1b2c3d4e5f

The tkd_ declaration key of the text kind.

Request Body schema: application/json
required
required
object

The text per language, keyed by ISO 639-1 code.

Responses

Response Schema: application/json
_id
required
string
_class
required
string
required
object
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

key
required
string
required
object

The text per language, keyed by ISO 639-1 code.

created_at
required
integer
updated_at
required
integer
required
ReferenceValue (object) or null

Request samples

Content type
application/json
{
  • "value": {
    }
}

Response samples

Content type
application/json
{
  • "_id": "prd_a1b2c3d4e5f6.tkd_0a1b2c3d4e5f",
  • "_class": "text",
  • "_links": {
    },
  • "tenant": {
    },
  • "parent": {
    },
  • "key": "tkd_0a1b2c3d4e5f",
  • "value": {
    },
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    }
}

Delete a product's text of one kind

Authorizations:
BearerAuth
path Parameters
product_id
required
string
text_kind_key
required
string
Example: tkd_0a1b2c3d4e5f

The tkd_ declaration key of the text kind.

Responses

Response samples

Content type
application/json
{
  • "error": "not_found",
  • "message": "Business partner not found",
  • "details": { }
}

List the texts a project carries

Authorizations:
BearerAuth
path Parameters
project_id
required
string
Example: prj_a1b2c3d4e5f6
query Parameters
next_token
string

Responses

Response Schema: application/json
required
Array of objects (TextResponse)
next_token
required
string or null

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_token": "string"
}

Write a project's text of one kind

Upsert. Answers 200 with the stored row, or 204 when the value held nothing in any language — that deletes the row, the same as DELETE.

Authorizations:
BearerAuth
path Parameters
project_id
required
string
text_kind_key
required
string
Example: tkd_0a1b2c3d4e5f

The tkd_ declaration key of the text kind.

Request Body schema: application/json
required
required
object

The text per language, keyed by ISO 639-1 code.

Responses

Response Schema: application/json
_id
required
string
_class
required
string
required
object
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

key
required
string
required
object

The text per language, keyed by ISO 639-1 code.

created_at
required
integer
updated_at
required
integer
required
ReferenceValue (object) or null

Request samples

Content type
application/json
{
  • "value": {
    }
}

Response samples

Content type
application/json
{
  • "_id": "prd_a1b2c3d4e5f6.tkd_0a1b2c3d4e5f",
  • "_class": "text",
  • "_links": {
    },
  • "tenant": {
    },
  • "parent": {
    },
  • "key": "tkd_0a1b2c3d4e5f",
  • "value": {
    },
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    }
}

Delete a project's text of one kind

Authorizations:
BearerAuth
path Parameters
project_id
required
string
text_kind_key
required
string
Example: tkd_0a1b2c3d4e5f

The tkd_ declaration key of the text kind.

Responses

Response samples

Content type
application/json
{
  • "error": "not_found",
  • "message": "Business partner not found",
  • "details": { }
}

List the texts a custom object carries

Authorizations:
BearerAuth
path Parameters
custom_object_id
required
string
Example: cob_a1b2c3d4e5f6
query Parameters
next_token
string

Responses

Response Schema: application/json
required
Array of objects (TextResponse)
next_token
required
string or null

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_token": "string"
}

Write a custom object's text of one kind

Upsert. Answers 200 with the stored row, or 204 when the value held nothing in any language — that deletes the row, the same as DELETE.

Authorizations:
BearerAuth
path Parameters
custom_object_id
required
string
text_kind_key
required
string
Example: tkd_0a1b2c3d4e5f

The tkd_ declaration key of the text kind.

Request Body schema: application/json
required
required
object

The text per language, keyed by ISO 639-1 code.

Responses

Response Schema: application/json
_id
required
string
_class
required
string
required
object
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

key
required
string
required
object

The text per language, keyed by ISO 639-1 code.

created_at
required
integer
updated_at
required
integer
required
ReferenceValue (object) or null

Request samples

Content type
application/json
{
  • "value": {
    }
}

Response samples

Content type
application/json
{
  • "_id": "prd_a1b2c3d4e5f6.tkd_0a1b2c3d4e5f",
  • "_class": "text",
  • "_links": {
    },
  • "tenant": {
    },
  • "parent": {
    },
  • "key": "tkd_0a1b2c3d4e5f",
  • "value": {
    },
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    }
}

Delete a custom object's text of one kind

Authorizations:
BearerAuth
path Parameters
custom_object_id
required
string
text_kind_key
required
string
Example: tkd_0a1b2c3d4e5f

The tkd_ declaration key of the text kind.

Responses

Response samples

Content type
application/json
{
  • "error": "not_found",
  • "message": "Business partner not found",
  • "details": { }
}

List the texts a business partner carries

Authorizations:
BearerAuth
path Parameters
business_partner_id
required
string
Example: bp_a1b2c3d4e5f6
query Parameters
next_token
string

Responses

Response Schema: application/json
required
Array of objects (TextResponse)
next_token
required
string or null

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_token": "string"
}

Write a business partner's text of one kind

Upsert. Answers 200 with the stored row, or 204 when the value held nothing in any language — that deletes the row, the same as DELETE.

Authorizations:
BearerAuth
path Parameters
business_partner_id
required
string
text_kind_key
required
string
Example: tkd_0a1b2c3d4e5f

The tkd_ declaration key of the text kind.

Request Body schema: application/json
required
required
object

The text per language, keyed by ISO 639-1 code.

Responses

Response Schema: application/json
_id
required
string
_class
required
string
required
object
required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

required
object (Reference)

An embedded reference to another entity.

In responses all three fields are always present (the backend builds every reference from the live entity), so they are marked required. _name is a backend-meta field (sibling of _id / _class): the read-only, backend-composed display label. Being readOnly, its required entry binds responses only (OpenAPI 3.1 §4.8.24.3).

In request bodies only _id is required. Both _class and _name are readOnly, so §4.8.24.3 binds their required entries to responses alone and a request body typed against this schema accepts the minimal {"_id": "bp_..."}. To clear a nullable reference, send null.

key
required
string
required
object

The text per language, keyed by ISO 639-1 code.

created_at
required
integer
updated_at
required
integer
required
ReferenceValue (object) or null

Request samples

Content type
application/json
{
  • "value": {
    }
}

Response samples

Content type
application/json
{
  • "_id": "prd_a1b2c3d4e5f6.tkd_0a1b2c3d4e5f",
  • "_class": "text",
  • "_links": {
    },
  • "tenant": {
    },
  • "parent": {
    },
  • "key": "tkd_0a1b2c3d4e5f",
  • "value": {
    },
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    }
}

Delete a business partner's text of one kind

Authorizations:
BearerAuth
path Parameters
business_partner_id
required
string
text_kind_key
required
string
Example: tkd_0a1b2c3d4e5f

The tkd_ declaration key of the text kind.

Responses

Response samples

Content type
application/json
{
  • "error": "not_found",
  • "message": "Business partner not found",
  • "details": { }
}

Billing

This tenant's subscription

What plan the tenant is on and what state it is in, together with the price actually being charged.

The price is resolved from the subscription's stored pricing region, never from where the caller is — a tenant keeps the pricing of the market it signed up in.

Never 404s: a tenant with no stored subscription is created on the free plan on first read, so no client has to handle a billing-not-set-up state.

Status changes are consequences of payment-provider events and arrive on their own endpoints; there is no route by which a tenant can put itself on a paid plan.

Authorizations:
BearerAuth

Responses

Response Schema: application/json
subscription_id
required
string
tenant_id
required
string
created_at
required
integer
updated_at
required
integer
required
object

Reference into the published plan catalogue; _id is the plan key served by GET /resource/v1/billing-plans.

plan_name
required
string
status
required
string
Enum: "free" "active" "past_due" "grace" "canceled"

past_due keeps the paid plan's limits — a failed card is not a cancelled customer. grace drops to the free tier's limits without deleting anything. canceled keeps paid limits until current_period_end.

region_key
required
string
currency
required
string = 3 characters
band
required
string
Enum: "a" "b" "c" "d"
required
MonetaryAmount (object) or null

Null on the free plan.

tax_display
required
string or null
Enum: "gross" "net" null
entitlements_active
required
boolean

Whether the subscribed plan's limits apply (active/past_due) or the free tier's do. Computed server-side so no client re-implements the rule.

required
object (ProviderCapabilities)

What this tenant's payment rail can actually do — static facts, not state.

A rail without hosted checkout or auto-renew needs a visibly different upgrade flow, and the client reads that from here rather than inferring it from provider_key. A tenant with no provider yet (everyone on the free tier) is answered with the stage's configured provider, so the billing page can render an upgrade path before any provider id exists.

provider_key
required
string or null
Enum: "stub" "stripe" null
provider_customer_id
required
string or null
provider_subscription_id
required
string or null
current_period_start
required
integer or null
current_period_end
required
integer or null
cancel_at_period_end
required
boolean
past_due_since
required
integer or null
last_provider_event_at
required
integer or null

The provider's own timestamp for the most recent event applied. Events older than this are rejected, because webhook delivery is unordered.

ReferenceValue (object) or null

Null when the subscription was created by self-service registration, where the acting user is not yet established.

Response samples

Content type
application/json
{
  • "subscription_id": "sub_9f2c1a04b7e3",
  • "tenant_id": "string",
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    },
  • "plan": {
    },
  • "plan_name": "string",
  • "status": "free",
  • "region_key": "ke",
  • "currency": "str",
  • "band": "a",
  • "price": {
    },
  • "tax_display": "gross",
  • "entitlements_active": true,
  • "provider_capabilities": {
    },
  • "provider_key": "stub",
  • "provider_customer_id": "string",
  • "provider_subscription_id": "string",
  • "current_period_start": 0,
  • "current_period_end": 0,
  • "cancel_at_period_end": true,
  • "past_due_since": 0,
  • "last_provider_event_at": 0
}

Stop the plan renewing, or undo a pending cancellation (owner)

cancel_at_period_end is the only field a tenant may write — it says what should happen when the current period ends, and touches neither status nor plan.

Setting it true is the "Cancel plan" path: the plan runs to the end of the period the tenant already paid for, nothing is taken away today, and asking twice is the same as asking once. Refused on the free plan, which has nothing to cancel.

Setting it false is the "Keep plan" path, available any time before the period actually ends. A 422 when no cancellation is pending, which is the honest answer: there is nothing to undo, and succeeding quietly would let a client report "resumed" for a plan nobody had stopped.

Owner-gated, like checkout: an administrator can read the bill, only an owner can stop it.

Authorizations:
BearerAuth
Request Body schema: application/json
required
cancel_at_period_end
required
boolean

Responses

Response Schema: application/json
subscription_id
required
string
tenant_id
required
string
created_at
required
integer
updated_at
required
integer
required
object

Reference into the published plan catalogue; _id is the plan key served by GET /resource/v1/billing-plans.

plan_name
required
string
status
required
string
Enum: "free" "active" "past_due" "grace" "canceled"

past_due keeps the paid plan's limits — a failed card is not a cancelled customer. grace drops to the free tier's limits without deleting anything. canceled keeps paid limits until current_period_end.

region_key
required
string
currency
required
string = 3 characters
band
required
string
Enum: "a" "b" "c" "d"
required
MonetaryAmount (object) or null

Null on the free plan.

tax_display
required
string or null
Enum: "gross" "net" null
entitlements_active
required
boolean

Whether the subscribed plan's limits apply (active/past_due) or the free tier's do. Computed server-side so no client re-implements the rule.

required
object (ProviderCapabilities)

What this tenant's payment rail can actually do — static facts, not state.

A rail without hosted checkout or auto-renew needs a visibly different upgrade flow, and the client reads that from here rather than inferring it from provider_key. A tenant with no provider yet (everyone on the free tier) is answered with the stage's configured provider, so the billing page can render an upgrade path before any provider id exists.

provider_key
required
string or null
Enum: "stub" "stripe" null
provider_customer_id
required
string or null
provider_subscription_id
required
string or null
current_period_start
required
integer or null
current_period_end
required
integer or null
cancel_at_period_end
required
boolean
past_due_since
required
integer or null
last_provider_event_at
required
integer or null

The provider's own timestamp for the most recent event applied. Events older than this are rejected, because webhook delivery is unordered.

ReferenceValue (object) or null

Null when the subscription was created by self-service registration, where the acting user is not yet established.

Request samples

Content type
application/json
{
  • "cancel_at_period_end": true
}

Response samples

Content type
application/json
{
  • "subscription_id": "sub_9f2c1a04b7e3",
  • "tenant_id": "string",
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    },
  • "plan": {
    },
  • "plan_name": "string",
  • "status": "free",
  • "region_key": "ke",
  • "currency": "str",
  • "band": "a",
  • "price": {
    },
  • "tax_display": "gross",
  • "entitlements_active": true,
  • "provider_capabilities": {
    },
  • "provider_key": "stub",
  • "provider_customer_id": "string",
  • "provider_subscription_id": "string",
  • "current_period_start": 0,
  • "current_period_end": 0,
  • "cancel_at_period_end": true,
  • "past_due_since": 0,
  • "last_provider_event_at": 0
}

This tenant's usage against its plan allowance

What the tenant has consumed inside the period it is currently paying for, with the plan's limit beside each meter.

The period follows the subscription, not the calendar: on a paid plan it is the billing anniversary window, on the free plan it is the calendar month. Clients render resets_at and never need to know which rule applied.

Every metric the plan defines is returned, including untouched ones at zero — an absent meter would read as a missing feature rather than an unused one.

Raccoon's own token costs are deliberately absent. The plan bundles a number of agent actions, not a number of tokens.

Authorizations:
BearerAuth

Responses

Response Schema: application/json
period
required
string

Opaque key for the window being metered. Follows the subscription's billing anniversary on a paid plan and the calendar month on free — clients display resets_at rather than parsing this.

resets_at
required
integer

Unix seconds at which these counters reset — the billing anniversary on a paid plan, the month boundary on free. One date, whichever rule produced it.

required
Array of objects (UsageMetric)
warnings
required
Array of strings

Metrics at or above 80% of their limit.

Response samples

Content type
application/json
{
  • "period": "sub#1755302400",
  • "resets_at": 0,
  • "metrics": [
    ],
  • "warnings": [
    ]
}

Start paying for a plan

Opens a checkout attempt and answers 202: nothing has been paid and nothing decided yet. The provider reports the outcome asynchronously, and the client follows the intent rather than awaiting a reply.

The plan, price and tax display are snapshotted onto the intent, so a catalogue edit between the price the tenant read and the charge the provider makes cannot change what they agreed to. The pricing region comes from the tenant's subscription — frozen at registration — never from the request.

Owner-gated: this is the call that commits the tenant to money.

Authorizations:
BearerAuth
Request Body schema: application/json
required
plan_key
required
string

A paid catalogue plan. free is refused; downgrading is a cancel.

stub_behaviour
string or null
Enum: "complete" "payment_failed" "canceled" "expire" null

Simulate an outcome. 422 unless the stage's provider is the stub.

Responses

Request samples

Content type
application/json
{
  • "plan_key": "starter",
  • "stub_behaviour": "complete"
}

Response samples

Content type
application/json
{
  • "checkout_intent_id": "chk_9f2c1a04b7e3",
  • "tenant_id": "string",
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    },
  • "plan": {
    },
  • "plan_name": "string",
  • "region_key": "ke",
  • "price": {
    },
  • "tax_display": "gross",
  • "status": "pending",
  • "provider_key": "stub",
  • "provider_session_id": "string",
  • "provider_session_url": "string",
  • "stub_behaviour": "complete",
  • "expires_at": 0,
  • "resolved_at": 0,
  • "failure_reason": "string"
}

This tenant's checkout attempts

Including the failed ones. Retrying creates a new intent rather than overwriting the old, so "my payment did not work" has evidence attached to it.

Authorizations:
BearerAuth
query Parameters
limit
integer [ 1 .. 200 ]
Default: 50
next_token
string

Base64-encoded pagination cursor from previous response

Responses

Response Schema: application/json
required
Array of objects (CheckoutIntent)
next_token
required
string or null

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_token": "string"
}

One checkout attempt

An intent past its expires_at reads as expired whatever is stored, so a lapsed checkout never looks live merely because the sweeper has not reached it yet.

Authorizations:
BearerAuth
path Parameters
checkout_intent_id
required
string
Example: chk_9f2c1a04b7e3

Responses

Response Schema: application/json
checkout_intent_id
required
string
tenant_id
required
string
created_at
required
integer
updated_at
required
integer
required
object

The plan being bought, snapshotted at create.

plan_name
required
string
region_key
required
string

The tenant's frozen pricing region, so a checkout cannot be quoted in a cheaper market than the tenant belongs to.

required
MonetaryAmount (object) or null

What the tenant agreed to pay, snapshotted at create.

tax_display
required
string or null
Enum: "gross" "net" null
status
required
string
Enum: "pending" "awaiting_payment" "completed" "payment_failed" "canceled" "expired"

pending — created, provider not yet engaged. awaiting_payment — the provider has a session open and is waiting on the payer. completed, payment_failed, canceled and expired are terminal; retrying means creating a new intent, which keeps the failed attempt readable instead of overwriting the evidence.

provider_key
required
string
Enum: "stub" "stripe"

Stamped from per-stage config, never from the request.

provider_session_id
required
string or null
provider_session_url
required
string or null

Where to send the payer, on a rail with hosted checkout. Null on a rail without one — see provider_capabilities.supports_hosted_checkout.

stub_behaviour
required
string or null
Enum: "complete" "payment_failed" "canceled" "expire" null

Which outcome the stub should simulate. Accepted only when the stage's provider is the stub; a real rail decides its own outcome, and letting a request name one would be a way to fake a payment.

expires_at
required
integer

Unix seconds after which the intent is no longer live.

resolved_at
required
integer or null
failure_reason
required
string or null
ReferenceValue (object) or null

Response samples

Content type
application/json
{
  • "checkout_intent_id": "chk_9f2c1a04b7e3",
  • "tenant_id": "string",
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    },
  • "plan": {
    },
  • "plan_name": "string",
  • "region_key": "ke",
  • "price": {
    },
  • "tax_display": "gross",
  • "status": "pending",
  • "provider_key": "stub",
  • "provider_session_id": "string",
  • "provider_session_url": "string",
  • "stub_behaviour": "complete",
  • "expires_at": 0,
  • "resolved_at": 0,
  • "failure_reason": "string"
}

Provider callback — a payment session is open

The provider reporting that it has a session open and is waiting for the payer, moving the intent to awaiting_payment.

Separate from the outcome callback because a rail with hosted checkout needs somewhere to put the URL to send the payer to, and the client has to tell "not started" apart from "waiting on the payer".

Service-gated. Callable only by a registered billing-provider service or a superuser — deliberately stricter than the validator callbacks, which admit any admin. A forged call here is money, not provenance.

Authorizations:
BearerAuth
path Parameters
checkout_intent_id
required
string
Request Body schema: application/json
required
provider_session_id
required
string
provider_session_url
string or null

Where to send the payer, on a rail with hosted checkout.

Responses

Response Schema: application/json
checkout_intent_id
required
string
tenant_id
required
string
created_at
required
integer
updated_at
required
integer
required
object

The plan being bought, snapshotted at create.

plan_name
required
string
region_key
required
string

The tenant's frozen pricing region, so a checkout cannot be quoted in a cheaper market than the tenant belongs to.

required
MonetaryAmount (object) or null

What the tenant agreed to pay, snapshotted at create.

tax_display
required
string or null
Enum: "gross" "net" null
status
required
string
Enum: "pending" "awaiting_payment" "completed" "payment_failed" "canceled" "expired"

pending — created, provider not yet engaged. awaiting_payment — the provider has a session open and is waiting on the payer. completed, payment_failed, canceled and expired are terminal; retrying means creating a new intent, which keeps the failed attempt readable instead of overwriting the evidence.

provider_key
required
string
Enum: "stub" "stripe"

Stamped from per-stage config, never from the request.

provider_session_id
required
string or null
provider_session_url
required
string or null

Where to send the payer, on a rail with hosted checkout. Null on a rail without one — see provider_capabilities.supports_hosted_checkout.

stub_behaviour
required
string or null
Enum: "complete" "payment_failed" "canceled" "expire" null

Which outcome the stub should simulate. Accepted only when the stage's provider is the stub; a real rail decides its own outcome, and letting a request name one would be a way to fake a payment.

expires_at
required
integer

Unix seconds after which the intent is no longer live.

resolved_at
required
integer or null
failure_reason
required
string or null
ReferenceValue (object) or null

Request samples

Content type
application/json
{
  • "provider_session_id": "string",
  • "provider_session_url": "string"
}

Response samples

Content type
application/json
{
  • "checkout_intent_id": "chk_9f2c1a04b7e3",
  • "tenant_id": "string",
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    },
  • "plan": {
    },
  • "plan_name": "string",
  • "region_key": "ke",
  • "price": {
    },
  • "tax_display": "gross",
  • "status": "pending",
  • "provider_key": "stub",
  • "provider_session_id": "string",
  • "provider_session_url": "string",
  • "stub_behaviour": "complete",
  • "expires_at": 0,
  • "resolved_at": 0,
  • "failure_reason": "string"
}

Provider callback — a checkout outcome

The provider reporting what happened. Resolves the intent and moves the subscription with it, through the same settlement path the in-core stub drives — which is what makes "one FSM for every provider" structural rather than a convention.

A repeat of the state the intent is already in is accepted and changes nothing: provider delivery is at-least-once, and answering 409 to a retry would make every duplicate look like a failure.

Service-gated, for the same reason as the session callback: a forged checkout_completed grants a paid plan.

Authorizations:
BearerAuth
path Parameters
checkout_intent_id
required
string
Request Body schema: application/json
required
event
required
string
Enum: "checkout_completed" "payment_succeeded" "payment_failed" "subscription_updated" "subscription_ended" "checkout_canceled" "checkout_expired"
occurred_at
integer or null

The provider's clock, not ours. The subscription's staleness guard compares against it, so substituting our own would make an out-of-order redelivery look current and let a retried "payment failed" undo the recovery that fixed it.

failure_reason
string or null
provider_customer_id
string or null
provider_subscription_id
string or null

Responses

Response Schema: application/json
checkout_intent_id
required
string
tenant_id
required
string
created_at
required
integer
updated_at
required
integer
required
object

The plan being bought, snapshotted at create.

plan_name
required
string
region_key
required
string

The tenant's frozen pricing region, so a checkout cannot be quoted in a cheaper market than the tenant belongs to.

required
MonetaryAmount (object) or null

What the tenant agreed to pay, snapshotted at create.

tax_display
required
string or null
Enum: "gross" "net" null
status
required
string
Enum: "pending" "awaiting_payment" "completed" "payment_failed" "canceled" "expired"

pending — created, provider not yet engaged. awaiting_payment — the provider has a session open and is waiting on the payer. completed, payment_failed, canceled and expired are terminal; retrying means creating a new intent, which keeps the failed attempt readable instead of overwriting the evidence.

provider_key
required
string
Enum: "stub" "stripe"

Stamped from per-stage config, never from the request.

provider_session_id
required
string or null
provider_session_url
required
string or null

Where to send the payer, on a rail with hosted checkout. Null on a rail without one — see provider_capabilities.supports_hosted_checkout.

stub_behaviour
required
string or null
Enum: "complete" "payment_failed" "canceled" "expire" null

Which outcome the stub should simulate. Accepted only when the stage's provider is the stub; a real rail decides its own outcome, and letting a request name one would be a way to fake a payment.

expires_at
required
integer

Unix seconds after which the intent is no longer live.

resolved_at
required
integer or null
failure_reason
required
string or null
ReferenceValue (object) or null

Request samples

Content type
application/json
{
  • "event": "checkout_completed",
  • "occurred_at": 0,
  • "failure_reason": "string",
  • "provider_customer_id": "string",
  • "provider_subscription_id": "string"
}

Response samples

Content type
application/json
{
  • "checkout_intent_id": "chk_9f2c1a04b7e3",
  • "tenant_id": "string",
  • "created_at": 0,
  • "updated_at": 0,
  • "created_by": {
    },
  • "plan": {
    },
  • "plan_name": "string",
  • "region_key": "ke",
  • "price": {
    },
  • "tax_display": "gross",
  • "status": "pending",
  • "provider_key": "stub",
  • "provider_session_id": "string",
  • "provider_session_url": "string",
  • "stub_behaviour": "complete",
  • "expires_at": 0,
  • "resolved_at": 0,
  • "failure_reason": "string"
}

This tenant's plan history

Every plan period this tenant has been on, oldest first.

A plan change closes one period and opens the next, so these rows are the audit trail rather than a reconstruction of one — nothing is edited in place and nothing is lost.

Each period carries the allowances that were granted with it, frozen at the moment it opened. A later catalogue change cannot reach them, which is what makes grandfathering automatic; a custom period may carry numbers the catalogue never offered at all.

Authorizations:
BearerAuth

Responses

Response Schema: application/json
required
Array of objects (SubscriptionPeriod)
next_token
required
string or null

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_token": "string"
}

The subscription price list for one country

What a business in a given country pays Raccoon, in the currency they use. Served from the in-repo catalogue — no persistence, no third-party I/O.

Deliberately unauthenticated. The public marketing site puts a price on the homepage and must quote the same numbers the product charges; reading them from here is what stops the two drifting apart. Nothing tenant-specific is exposed — a price list is the most public document a company has.

Pass country to price a specific market, or region_key to price a pricing region directly — the form a signed-in tenant uses, since it carries a frozen region_key and no country. With neither, the response falls back to the dearest region rather than the cheapest, because overcharging is refundable and underpricing a market is not.

Authenticating does not change the answer: a subscription's pricing region is fixed when it is created and is never re-derived here from an address that can later be edited. The caller says which market it wants priced; the endpoint does not guess from tenant data.

Prices are stored as explicit local amounts and are never computed from a live FX rate — a tenant's bill must not move because the shilling did.

query Parameters
region_key
string
Example: region_key=ke

Price for a pricing region directly, for a caller that already has one. A signed-in tenant carries a frozen region_key on its subscription and has no country to offer — and the key does not invert, since eu spans twenty of them. Takes precedence over country when both are given. An unknown key is a 422.

country
string = 2 characters

ISO 3166-1 alpha-2 code. A country with no price row of its own resolves to the dearest region rather than the cheapest, so an unlisted market is never accidentally underpriced. A code that is not an officially-assigned country is a 422.

Responses

Response Schema: application/json
required
Array of objects (BillingPlan)
next_token
required
null

The catalogue is a fixed short list and is never paginated.

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_token": null
}

Changes

Poll the tenant change feed

Returns the changes recorded at or after since, oldest first, so a client can refresh whatever it has on screen that appears in the result. Each entry names a subject to re-read; none carries field values.

The response's next_token is a durable cursor, not a page marker: it is never null, and the client sends it back as since on the next poll. Omitting since subscribes from now and returns an empty page — a client that has just loaded has no backlog to catch up on.

Delivery is at-least-once. The cursor lags real time by a couple of seconds so a change written late within a second cannot be skipped, which means consecutive polls re-deliver a small overlap; de-duplicate on _id. Refetching is idempotent, so this is a deliberate trade.

Authorizations:
BearerAuth
query Parameters
since
string
Example: since=1785483570

Cursor from a previous response's next_token.

limit
integer [ 1 .. 200 ]
Default: 100

Responses

Response Schema: application/json
required
Array of objects (ChangeResponse)
next_token
required
string

The feed cursor, never null — unlike every other list endpoint, where next_token marks a page. Store it and send it back as since on the next poll. It deliberately lags real time by a couple of seconds, so consecutive polls re-deliver a small overlap; de-duplicate on _id.

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_token": "1785483570"
}

Read a single change row

Authorizations:
BearerAuth
path Parameters
change_id
required
string
Example: chg_a1b2c3d4e5f6

Responses

Response Schema: application/json
_id
required
string
_class
required
string
Value: "change"
required
object (Reference)

The aggregate the client should re-read. _name is always empty — the feed never resolves it, both to keep a 200-row page to a single query and because a deleted subject no longer exists to name.

resource_class
required
string

Aggregate root class, e.g. product.

subclass
required
string

Fully-qualified path of what changed within the aggregate, e.g. product.price or business_partner.identifier.

method
required
string
Enum: "created" "updated" "deleted"
actor_kind
required
string

Who caused it — user, agent, service, or system.

created_at
required
integer

Unix epoch seconds. Also the feed's ordering position.

updated_at
required
integer

Unix epoch seconds.

required
object (ReferenceValue)

Stored value for a field of type "reference"

required
ReferenceValue (object) or null
object

Response samples

Content type
application/json
{
  • "_id": "chg_a1b2c3d4e5f6",
  • "_class": "change",
  • "subject": {
    },
  • "resource_class": "product",
  • "subclass": "product.price",
  • "method": "created",
  • "actor_kind": "agent",
  • "created_at": 0,
  • "updated_at": 0,
  • "_links": {
    },
  • "tenant": {
    },
  • "created_by": {
    }
}