StabisoDocs

API reference

Recurring invoices

Templates of invoices that repeat (monthly, quarterly, yearly): created as drafts, activated by a human after a second person approves the terms, and made on demand by a run that is idempotent and never makes a period twice.

Operation Summary Scope Idempotency-Key
GET /v1/recurring-invoices List recurring invoices read
POST /v1/recurring-invoices Create a recurring invoice draft required
POST /v1/recurring-invoices/run Make the recurring invoices that are due draft required
GET /v1/recurring-invoices/{template_id} Get a recurring invoice read
PATCH /v1/recurring-invoices/{template_id} Change a recurring invoice draft
POST /v1/recurring-invoices/{template_id}/activate Activate a recurring invoice draft
POST /v1/recurring-invoices/{template_id}/pause Pause a recurring invoice draft
GET /v1/recurring-invoices/{template_id}/runs List the invoices a recurring invoice made read

GET /v1/recurring-invoices

List recurring invoices

Recurring invoice templates, the one that falls due first at the top (templates without a next due date, the ended ones, last). due_through is the due list: the templates a run through that day would make invoices for, the same rule as the Role Center cue (recurring_due in GET /v1/entities/{entity}/overview). Read-only.

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

Parameters

In Name Type Required Description
query entity string or null Only this entity's templates (code or id).
query status "active" | "draft" | "ended" | "paused" | "pending_approval" or null Only templates with this status: draft, pending_approval, active, paused or ended.
query party string or null Only this customer's templates (code or id); needs entity.
query due_through string (date) or null Only the templates a run through this day would make invoices for: active, with a next due date on or before it (ISO 8601). This is the list the Role Center's recurring_due counts.
query limit integer How many templates to return, the next to fall due first. (min 1; max 1000; default 100)

Example request

curl "$ERP_URL/v1/recurring-invoices" \
  -H "Authorization: Bearer $ERP_KEY"

Responses

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

Example response (shape)

[
  {
    "approved_at": "2026-10-28T09:12:44Z",
    "approved_by": {
      "id": "user:bob",
      "name": "Bob",
      "type": "human"
    },
    "auto_submit": false,
    "created_at": "2026-10-27T16:03:10Z",
    "created_by": {
      "id": "user:alice",
      "name": "Alice",
      "type": "human"
    },
    "currency": "CHF",
    "entity": "CH01",
    "frequency": "monthly",
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "invoices_made": 1,
    "lines": [
      {
        "description": "Hosting and support",
        "quantity": "1",
        "rate_type": "standard",
        "reverse_charge": false,
        "supply_type": "services",
        "unit_price": "450.00",
        "vat_scope": "in_scope"
      }
    ],
    "name": "Hosting and support, monthly",
    "net_total": "450.00",
    "next_due": "2026-12-01",
    "party_code": "C-CH",
    "party_id": "7d1e2f3a-4b5c-4d6e-8f70-91a2b3c4d5e6",
    "party_name": "Zürich Kunde AG",
    "payment_terms_days": 30,
    "start_date": "2026-11-01",
    "status": "active",
    "terms_approved": true,
    "terms_digest": "5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8"
  }
]

POST /v1/recurring-invoices

Create a recurring invoice

Create a recurring invoice as a draft: the customer, currency, lines, frequency, start and end dates and payment terms of an invoice that repeats. Nothing is invoiced until a person activates it (POST .../{template_id}/activate) and a second person approves its terms, and then a run makes the invoices (POST /v1/recurring-invoices/run). The first invoice is built without being kept, so a missing account, an unknown customer or a tax decision that cannot be made is refused now (404, 422) instead of at the first run. The next due date is the start date. Needs an Idempotency-Key.

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

Request body

application/json (required): RecurringInvoiceInput

Field Type Required Description
entity string yes Code (or id) of the entity that issues the invoices.
party string yes Code (or id) of the customer the invoices are for.
name string yes What the template is called in lists, e.g. Hosting and support, monthly. It is not printed on the invoices. (min length 1; max length 200)
frequency "monthly" | "quarterly" | "yearly" yes monthly, quarterly (every 3 months) or yearly. The due dates are counted from start_date and keep its day of the month; a month that is too short takes its last day (start on the 31st: 31 Jan, 28 Feb, 31 Mar, 30 Apr).
start_date string (date) yes The first due date (ISO 8601): the date of the first invoice. It may lie in the past; a run then makes the invoices that are due, a year of monthly ones at most per run.
end_date string (date) or null The last day an invoice may fall due, inclusive (ISO 8601); null for no end. After the last due date the template ends by itself.
currency string or null ISO 4217 currency of the invoices; default: the customer's currency. (pattern ^[A-Z]{3}$)
payment_terms_days integer or null Days from the invoice date to its due date; default: the customer's own payment terms. (min 0; max 365)
auto_submit boolean false (default): each due invoice is created as a draft for a person to submit. true: the system submits it through the approval policy as it is made; the approval of the terms says so on its face. (default false)
notes string or null Free text printed on every invoice (the invoice's notes).
lines array of DocumentLineInput yes The lines of every invoice, as on POST /v1/documents: tax is determined for each invoice on its own date by the same rules as for an invoice entered by hand. (min items 1)

Example request

curl -X POST "$ERP_URL/v1/recurring-invoices" \
  -H "Authorization: Bearer $ERP_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "entity": "CH01",
  "frequency": "monthly",
  "lines": [
    {
      "description": "Hosting and support",
      "quantity": "1",
      "unit_price": "450.00"
    }
  ],
  "name": "Hosting and support, monthly",
  "party": "C-CH",
  "payment_terms_days": 30,
  "start_date": "2026-11-01"
}'

Responses

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

Example response (shape)

{
  "approved_at": "2026-10-28T09:12:44Z",
  "approved_by": {
    "id": "user:bob",
    "name": "Bob",
    "type": "human"
  },
  "auto_submit": false,
  "created_at": "2026-10-27T16:03:10Z",
  "created_by": {
    "id": "user:alice",
    "name": "Alice",
    "type": "human"
  },
  "currency": "CHF",
  "entity": "CH01",
  "frequency": "monthly",
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "invoices_made": 1,
  "lines": [
    {
      "description": "Hosting and support",
      "quantity": "1",
      "rate_type": "standard",
      "reverse_charge": false,
      "supply_type": "services",
      "unit_price": "450.00",
      "vat_scope": "in_scope"
    }
  ],
  "name": "Hosting and support, monthly",
  "net_total": "450.00",
  "next_due": "2026-12-01",
  "party_code": "C-CH",
  "party_id": "7d1e2f3a-4b5c-4d6e-8f70-91a2b3c4d5e6",
  "party_name": "Zürich Kunde AG",
  "payment_terms_days": 30,
  "start_date": "2026-11-01",
  "status": "active",
  "terms_approved": true,
  "terms_digest": "5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8"
}

POST /v1/recurring-invoices/run

Make the recurring invoices that are due

Make the invoices of every active recurring invoice that has a due date on or before through (never after today where the template's company is), in date order, as ordinary invoices dated their due date.

Each is a draft for a person to submit, or, when the template has auto_submit, is submitted by the system through the normal path (the approval policy, the open period, the numbering), which the approval of the terms authorised. It is idempotent: a repeated, retried or simultaneous run makes nothing twice (one invoice per template and due date, enforced by the database); an Idempotency-Key replays the first answer. A period that cannot be made (a closed period, an inactive customer) is failed with its error, stops that template at that date and leaves the others running; the next run tries it again. At most twelve due dates per template are made in one run: more_due says to run again. A person or a script calls this, and the scheduled close job runs it daily for the entities whose close calendar names recurring_invoices (PUT /v1/entities/{entity}/close-schedule). Needs an Idempotency-Key.

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

Request body

application/json (required): RecurringRunInput

Field Type Required Description
through string (date) yes Make the invoices that fall due on or before this day (ISO 8601, inclusive). It may not lie more than a day after the server's date (UTC), so your own date is always accepted.
entity string or null Only the templates of this entity (code or id); default: all.
template string (uuid) or null Only this template (it must be active); default: every one.

Example request

curl -X POST "$ERP_URL/v1/recurring-invoices/run" \
  -H "Authorization: Bearer $ERP_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "entity": "CH01",
  "through": "2026-11-30"
}'

Responses

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

Example response (shape)

{
  "failed": 0,
  "items": [
    {
      "document_id": "9b2f6a3e-1c4d-4e8a-8f10-5d7c2a9e4b11",
      "due_date": "2026-11-01",
      "status": "drafted",
      "template_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "template_name": "Hosting and support, monthly"
    },
    {
      "document_id": "0f1e2d3c-4b5a-4968-8776-655443322110",
      "due_date": "2026-12-01",
      "status": "drafted",
      "template_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "template_name": "Hosting and support, monthly"
    }
  ],
  "made": 2,
  "more_due": false,
  "through": "2026-12-01"
}

GET /v1/recurring-invoices/{template_id}

Get a recurring invoice

One recurring invoice: its terms, status, next due date, whether the current terms are the approved ones (terms_approved), the pending approval request and how many invoices it has made. 404 when it does not exist.

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

Parameters

In Name Type Required Description
path template_id string (uuid) yes Id of the recurring invoice (GET /v1/recurring-invoices).

Example request

curl "$ERP_URL/v1/recurring-invoices/3fa85f64-5717-4562-b3fc-2c963f66afa6" \
  -H "Authorization: Bearer $ERP_KEY"

Responses

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

Example response (shape)

{
  "approved_at": "2026-10-28T09:12:44Z",
  "approved_by": {
    "id": "user:bob",
    "name": "Bob",
    "type": "human"
  },
  "auto_submit": false,
  "created_at": "2026-10-27T16:03:10Z",
  "created_by": {
    "id": "user:alice",
    "name": "Alice",
    "type": "human"
  },
  "currency": "CHF",
  "entity": "CH01",
  "frequency": "monthly",
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "invoices_made": 1,
  "lines": [
    {
      "description": "Hosting and support",
      "quantity": "1",
      "rate_type": "standard",
      "reverse_charge": false,
      "supply_type": "services",
      "unit_price": "450.00",
      "vat_scope": "in_scope"
    }
  ],
  "name": "Hosting and support, monthly",
  "net_total": "450.00",
  "next_due": "2026-12-01",
  "party_code": "C-CH",
  "party_id": "7d1e2f3a-4b5c-4d6e-8f70-91a2b3c4d5e6",
  "party_name": "Zürich Kunde AG",
  "payment_terms_days": 30,
  "start_date": "2026-11-01",
  "status": "active",
  "terms_approved": true,
  "terms_digest": "5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8"
}

PATCH /v1/recurring-invoices/{template_id}

Change a recurring invoice

Change a recurring invoice. Omitted fields keep their value; lines replaces every line. Only name changes without consequence: any change to the terms pauses an active template (so it makes no invoice until a second person approves the new terms) and takes one waiting for approval back to draft, withdrawing the request. frequency and start_date cannot change once an invoice was made (409): end the template and create a new one. 409 for an ended template; 404, 422 as when creating.

Required scope draft (admin implies every scope)
Idempotency-Key not used
Operation id update_recurring_invoice_v1_recurring_invoices__template_id__patch

Parameters

In Name Type Required Description
path template_id string (uuid) yes Id of the recurring invoice (GET /v1/recurring-invoices).

Request body

application/json (required): RecurringInvoiceUpdate

Field Type Required Description
party string or null Code (or id) of the customer.
name string or null What the template is called. (min length 1; max length 200)
frequency "monthly" | "quarterly" | "yearly" or null monthly, quarterly or yearly; before the first invoice.
start_date string (date) or null The first due date (ISO 8601); before the first invoice.
end_date string (date) or null The last due date, inclusive; null removes the end.
currency string or null ISO 4217 currency of the invoices. (pattern ^[A-Z]{3}$)
payment_terms_days integer or null Days to pay; null takes the customer's own terms. (min 0; max 365)
auto_submit boolean or null Whether the system submits each invoice as it is made.
notes string or null Free text on every invoice.
lines array of DocumentLineInput or null The new lines; they replace all existing lines (at least one). (min items 1)

Example request

curl -X PATCH "$ERP_URL/v1/recurring-invoices/3fa85f64-5717-4562-b3fc-2c963f66afa6" \
  -H "Authorization: Bearer $ERP_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "end_date": "2027-10-31",
  "payment_terms_days": 14
}'

Responses

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

Example response (shape)

{
  "approved_at": "2026-10-28T09:12:44Z",
  "approved_by": {
    "id": "user:bob",
    "name": "Bob",
    "type": "human"
  },
  "auto_submit": false,
  "created_at": "2026-10-27T16:03:10Z",
  "created_by": {
    "id": "user:alice",
    "name": "Alice",
    "type": "human"
  },
  "currency": "CHF",
  "entity": "CH01",
  "frequency": "monthly",
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "invoices_made": 1,
  "lines": [
    {
      "description": "Hosting and support",
      "quantity": "1",
      "rate_type": "standard",
      "reverse_charge": false,
      "supply_type": "services",
      "unit_price": "450.00",
      "vat_scope": "in_scope"
    }
  ],
  "name": "Hosting and support, monthly",
  "net_total": "450.00",
  "next_due": "2026-12-01",
  "party_code": "C-CH",
  "party_id": "7d1e2f3a-4b5c-4d6e-8f70-91a2b3c4d5e6",
  "party_name": "Zürich Kunde AG",
  "payment_terms_days": 30,
  "start_date": "2026-11-01",
  "status": "active",
  "terms_approved": true,
  "terms_digest": "5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8"
}

POST /v1/recurring-invoices/{template_id}/activate

Activate a recurring invoice

Ask for the terms of a draft or paused recurring invoice to be approved, so that runs make its invoices. A human decision: the policy action activate_recurring_invoice denies agents (403, naming the rule). By default a second person approves (POST /v1/approvals/{approval_id}/approve; never the person who created the template or asked), and the approval is bound to the digest of the terms: it activates only these terms, and says on its face whether the invoices are submitted automatically (auto_submit). A paused template whose terms are the ones already approved runs again at once. An organization with one person allows the action in its approval policy. 409 when the customer is not active, the template is not a draft or paused, or no due date is left before its end date.

Required scope draft (admin implies every scope)
Idempotency-Key not used
Operation id activate_recurring_invoice_v1_recurring_invoices__template_id__activate_post

Parameters

In Name Type Required Description
path template_id string (uuid) yes Id of the recurring invoice (GET /v1/recurring-invoices).

Example request

curl -X POST "$ERP_URL/v1/recurring-invoices/3fa85f64-5717-4562-b3fc-2c963f66afa6/activate" \
  -H "Authorization: Bearer $ERP_KEY"

Responses

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

Example response (shape)

{
  "approval_id": "c2d4e6f8-0a1b-4c3d-9e5f-7a8b9c0d1e2f",
  "policy": "require_approval",
  "template": {
    "approval_id": "c2d4e6f8-0a1b-4c3d-9e5f-7a8b9c0d1e2f",
    "auto_submit": false,
    "created_at": "2026-10-27T16:03:10Z",
    "created_by": {
      "id": "user:alice",
      "name": "Alice",
      "type": "human"
    },
    "currency": "CHF",
    "entity": "CH01",
    "frequency": "monthly",
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "invoices_made": 1,
    "lines": [
      {
        "description": "Hosting and support",
        "quantity": "1",
        "rate_type": "standard",
        "reverse_charge": false,
        "supply_type": "services",
        "unit_price": "450.00",
        "vat_scope": "in_scope"
      }
    ],
    "name": "Hosting and support, monthly",
    "net_total": "450.00",
    "next_due": "2026-12-01",
    "party_code": "C-CH",
    "party_id": "7d1e2f3a-4b5c-4d6e-8f70-91a2b3c4d5e6",
    "party_name": "Zürich Kunde AG",
    "payment_terms_days": 30,
    "start_date": "2026-11-01",
    "status": "pending_approval",
    "terms_approved": false
  }
}

POST /v1/recurring-invoices/{template_id}/pause

Pause a recurring invoice

Stop an active recurring invoice from making invoices, with a reason kept in the audit trail. It makes none until it is activated again (at once if its terms are unchanged since the approval). Pausing is open to anyone who may draft: it makes nothing happen. 409 unless the template is active.

Required scope draft (admin implies every scope)
Idempotency-Key not used
Operation id pause_recurring_invoice_v1_recurring_invoices__template_id__pause_post

Parameters

In Name Type Required Description
path template_id string (uuid) yes Id of the recurring invoice (GET /v1/recurring-invoices).

Request body

application/json (required): RecurringPause

Field Type Required Description
reason string yes Why; kept with the template and in the audit. (min length 1; max length 500)

Example request

curl -X POST "$ERP_URL/v1/recurring-invoices/3fa85f64-5717-4562-b3fc-2c963f66afa6/pause" \
  -H "Authorization: Bearer $ERP_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "reason": "Price list under renegotiation: hold invoices"
}'

Responses

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

Example response (shape)

{
  "approved_at": "2026-10-28T09:12:44Z",
  "approved_by": {
    "id": "user:bob",
    "name": "Bob",
    "type": "human"
  },
  "auto_submit": false,
  "created_at": "2026-10-27T16:03:10Z",
  "created_by": {
    "id": "user:alice",
    "name": "Alice",
    "type": "human"
  },
  "currency": "CHF",
  "entity": "CH01",
  "frequency": "monthly",
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "invoices_made": 1,
  "lines": [
    {
      "description": "Hosting and support",
      "quantity": "1",
      "rate_type": "standard",
      "reverse_charge": false,
      "supply_type": "services",
      "unit_price": "450.00",
      "vat_scope": "in_scope"
    }
  ],
  "name": "Hosting and support, monthly",
  "net_total": "450.00",
  "next_due": "2026-12-01",
  "party_code": "C-CH",
  "party_id": "7d1e2f3a-4b5c-4d6e-8f70-91a2b3c4d5e6",
  "party_name": "Zürich Kunde AG",
  "payment_terms_days": 30,
  "start_date": "2026-11-01",
  "status": "active",
  "terms_approved": true,
  "terms_digest": "5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8"
}

GET /v1/recurring-invoices/{template_id}/runs

List the invoices a recurring invoice made

Every period a run has made or tried for the template, oldest first: the due date, what came of it (drafted, posted, pending_approval, failed), the invoice with its number and its status now, the error of a failed period, and who ran it. 404 when the template does not exist.

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

Parameters

In Name Type Required Description
path template_id string (uuid) yes Id of the recurring invoice (GET /v1/recurring-invoices).

Example request

curl "$ERP_URL/v1/recurring-invoices/3fa85f64-5717-4562-b3fc-2c963f66afa6/runs" \
  -H "Authorization: Bearer $ERP_KEY"

Responses

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

Example response (shape)

[
  {
    "created_at": "2026-11-02T08:30:00Z",
    "created_by": {
      "id": "user:alice",
      "name": "Alice",
      "type": "human"
    },
    "document_id": "9b2f6a3e-1c4d-4e8a-8f10-5d7c2a9e4b11",
    "document_number": "INV-2026-000042",
    "document_status": "posted",
    "due_date": "2026-11-01",
    "id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
    "status": "posted",
    "template_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
  }
]

Schemas

RecurringActivation

Field Type Required Description
template RecurringInvoiceOut yes The template after the request.
policy string yes allow (it is active now, or runs again with the terms already approved) or require_approval (a second person decides: approval_id).
approval_id string (uuid) or null yes The approval request when a second person must decide; else null.

RecurringInvoiceInput

Field Type Required Description
entity string yes Code (or id) of the entity that issues the invoices.
party string yes Code (or id) of the customer the invoices are for.
name string yes What the template is called in lists, e.g. Hosting and support, monthly. It is not printed on the invoices. (min length 1; max length 200)
frequency "monthly" | "quarterly" | "yearly" yes monthly, quarterly (every 3 months) or yearly. The due dates are counted from start_date and keep its day of the month; a month that is too short takes its last day (start on the 31st: 31 Jan, 28 Feb, 31 Mar, 30 Apr).
start_date string (date) yes The first due date (ISO 8601): the date of the first invoice. It may lie in the past; a run then makes the invoices that are due, a year of monthly ones at most per run.
end_date string (date) or null The last day an invoice may fall due, inclusive (ISO 8601); null for no end. After the last due date the template ends by itself.
currency string or null ISO 4217 currency of the invoices; default: the customer's currency. (pattern ^[A-Z]{3}$)
payment_terms_days integer or null Days from the invoice date to its due date; default: the customer's own payment terms. (min 0; max 365)
auto_submit boolean false (default): each due invoice is created as a draft for a person to submit. true: the system submits it through the approval policy as it is made; the approval of the terms says so on its face. (default false)
notes string or null Free text printed on every invoice (the invoice's notes).
lines array of DocumentLineInput yes The lines of every invoice, as on POST /v1/documents: tax is determined for each invoice on its own date by the same rules as for an invoice entered by hand. (min items 1)

RecurringInvoiceOut

Field Type Required Description
id string (uuid) yes Id of the template.
entity string yes Code of the entity that issues the invoices.
party_id string (uuid) yes Id of the customer.
party_code string yes Code of the customer, e.g. C-CH.
party_name string yes Name of the customer.
name string yes What the template is called in lists.
status "active" | "draft" | "ended" | "paused" | "pending_approval" yes draft, pending_approval (a second person is to approve the terms), active (runs generate invoices), paused or ended (the end date has passed).
frequency "monthly" | "quarterly" | "yearly" yes monthly, quarterly or yearly.
start_date string (date) yes The first due date.
end_date string (date) or null yes The last due date, inclusive; null for none.
next_due string (date) or null yes The next due date an invoice is to be made for; null once the template has ended. It is due when it is on or before the day of the run.
currency string yes ISO 4217 currency of the invoices.
payment_terms_days integer or null yes Days from the invoice date to its due date; null: the customer's terms.
auto_submit boolean yes Whether the system submits each invoice as it is made (through the approval policy) instead of leaving a draft.
notes string or null yes Free text printed on every invoice.
net_total string yes Net total of one invoice (the sum of the line amounts, rounded to the currency), before VAT.
lines array of RecurringLineOut yes The lines of every invoice.
terms_digest string or null yes SHA-256 of the terms a second person approved; null before the first approval.
terms_approved boolean yes Whether the current terms are the approved ones. False after a change to an approved template: it stays paused until the new terms are approved.
approval_id string (uuid) or null yes The pending approval request (POST /v1/approvals/{approval_id}/approve) while status is pending_approval; else null.
approved_by object or null yes Who approved the terms (type, id, name); null before.
approved_at string (date-time) or null yes When the terms were approved (UTC).
paused_by object or null yes Who paused it, or changed its terms.
paused_at string (date-time) or null yes When it was paused (UTC).
pause_reason string or null yes Why it was paused.
created_by object yes Who created the template.
created_at string (date-time) yes When it was created (UTC).
invoices_made integer yes How many invoices its runs have made so far.

RecurringInvoiceUpdate

Changes to a template. Omitted fields keep their value; null clears an optional one.

A change to anything but name is a change of the terms: it pauses an active template (and takes a template waiting for approval back to draft), and the template runs again only after a second person has approved the new terms. frequency and start_date can change only before the first invoice was made.

Field Type Required Description
party string or null Code (or id) of the customer.
name string or null What the template is called. (min length 1; max length 200)
frequency "monthly" | "quarterly" | "yearly" or null monthly, quarterly or yearly; before the first invoice.
start_date string (date) or null The first due date (ISO 8601); before the first invoice.
end_date string (date) or null The last due date, inclusive; null removes the end.
currency string or null ISO 4217 currency of the invoices. (pattern ^[A-Z]{3}$)
payment_terms_days integer or null Days to pay; null takes the customer's own terms. (min 0; max 365)
auto_submit boolean or null Whether the system submits each invoice as it is made.
notes string or null Free text on every invoice.
lines array of DocumentLineInput or null The new lines; they replace all existing lines (at least one). (min items 1)

RecurringLineOut

A line of a recurring invoice as stored: the fields of an invoice line as entered. It is a model of its own so that DocumentLineInput, an input everywhere else, is not an output.

Field Type Required Description
description string or null Text of the line, e.g. Consulting September 2026. Required, except on a line that names a product (default: the product's name) or credits a line (credits_line; default: that line's text). (min length 1)
quantity string Quantity as a decimal string, greater than 0; default 1. (default "1")
unit_price string or null Net unit price; negative only on vendor documents (e.g. a discount line). Required, except on a line that names a product: left out, it takes the customer's price from its price list in the document's currency, else the product's list price on the document date (422 product_unpriced when it has neither); and on a credit note line with credits_line: left out, the line credits that line as it was, at its price and (unless the line gives its own) its discount.
discount_percent string or null A discount on the line, in percent of quantity times unit price (at most 100; 0 for none). Give this or discount_amount, not both.
discount_amount string or null A discount on the line, as an amount off quantity times unit price in the document currency, at most what the line is worth (0 for none). Give this or discount_percent.
credits_line integer or null Credit notes with credits: the number of the line of the credited document this line credits. Without a unit_price the line credits it as it was: it takes that line's price and its discount with its share of the document discount, in proportion to the quantity it credits (what is left of them when it credits the rest of the line), and returns them as its own unit_price and discount, with as_credited true when it gave no discount either. To change the quantity of such a line later, send it again without price and discount; sent with them, it credits them as given. With a unit_price only the discount the line gives applies; where the credited line had a discount (its own or a share of the document discount) the line must give one (0 for none), else 422 credit_discount_required. The line also takes that line's tax decision as stored (supply_type, rate_type, tax_category, vat_scope and the tax: treatment, taxing jurisdiction, rates), so it reverses exactly the tax charged; one it states that differs is not taken, with the warning credit_takes_credited_vat. (min 1)
account string or null Defaults to the product's revenue account, else the party or entity default.
product string or null Sales documents: code (or id) of an active product of the entity (GET /v1/products). The line takes the product's revenue account, supply_type and rate_type unless it names its own, and its name and price when it gives none (the price in force: GET /v1/products/{product_id}/price); the document's provenance.filled says what was taken.
obligation string (uuid) or null Sales documents: id of the performance obligation of an active revenue contract of the same customer and currency that the line bills (GET /v1/revenue-contracts/{contract_id}). The line is booked to the contract liability account (the role contract_liability), not to revenue: the recognition run makes the revenue (POST /v1/periods/{period_id}/revenue-recognition). It takes no account of its own, is not a supply of goods, and the net billed of the contract never exceeds its transaction price (posting is refused, 422).
sales_line string (uuid) or null Sales invoices: id of the sales order line the line invoices (GET /v1/sales-documents/{document}), of an open order of the same customer and currency. The invoices of an order line never add up to more than was ordered less what was closed (422); a draft reserves its quantity until it is voided. Usually set by POST /v1/sales-documents/{document}/invoice.
delivery_line string (uuid) or null Sales invoices: id of the line of a posted delivery note of the same customer that the line invoices (GET /v1/delivery-notes/{note_id}), of the same product. What the invoices of a delivery note line add up to, less what credit notes took back, never exceeds what was delivered less what came back (422). The goods left with the delivery note, so the invoice moves no stock. Usually set by POST /v1/delivery-notes/{note_id}/invoice. A line of a stock item that names none ships with the invoice: posting the invoice issues the goods (Dr cost of goods sold / Cr inventory).
goods_returned boolean Sales credit notes: the goods of this line came back. The line names the line it credits (credits_line), of a stock item; posting the credit note brings the quantity back into stock at the cost it left with (Dr inventory / Cr cost of goods sold). Without it no stock moves. (default false)
warehouse string or null Code (or id) of the warehouse the goods come back to (goods_returned) or ship from with the invoice (a stock item's invoice line that names no delivery note line); default the entity's default warehouse.
supply_type "electronic_services" | "goods" | "services" goods, services (default) or electronic_services; part of the place-of-supply decision. (default "services")
rate_type "exempt" | "parking" | "reduced" | "special" | "standard" | "super_reduced" | "zero" Which VAT rate of the jurisdiction applies: standard (default), reduced, super_reduced, parking, zero, special or exempt. (default "standard")
tax_category string or null One of the categories of the rate (GET /v1/entities/{entity}/vat-rate-types), e.g. books: it chooses the rate where the rate type has several (France reduced: 5.5 % or 10 %) and names the kind of an exempt supply (insurance). Omit for none. (max length 200)
printed_tax_amount string or null VAT shown on a vendor bill; compared with the determined VAT. (pattern ^(?!^[-+.]*$)[+-]?0*\d*\.?\d*$)
vat_recovery_percent string or null Purchase bills and their credit notes: how much of the line's input VAT the company may deduct, in percent (0 to 100, two places), e.g. 0 for a gift whose VAT the law excludes or 50 for a car used half privately. The rest is booked as part of the cost on the line's own account, not as input VAT, so no VAT return reports it. Omit (null) to take the account's vat_recovery_percent, else 100. A self-assessed line still owes all of its VAT. Below 100 it is refused (422) on a line without input VAT of our own: a sales line, one outside VAT, foreign VAT booked as cost, US sales or use tax, a Canadian line of several taxes.
reverse_charge boolean Vendor documents: the vendor invoices under reverse charge (EN 16931 VAT category AE, e.g. UStG §13b), so the VAT is self-assessed at the determined rate. (default false)
use_tax boolean A US company's bill line the vendor charged no sales tax on, for something used in the company's own state: the company self-assesses use tax on it at the state's rate and its use tax area's (ADR-0064). Refused on sales documents and for companies outside the US. (default false)
vat_scope "in_scope" | "outside" in_scope (default): the tax is determined as usual. outside: the line is outside the scope of VAT, like interest and fees for late payment (compensation for damage), a supply within a VAT group or an Organschaft. It needs vat_scope_reason, has no tax determination and rate 0, carries no tax tag (so no VAT report or return has it) and prints the remark of its reason in the document's language. It takes no tax_category, printed_tax_amount or reverse_charge, and a document with such a line carries no cash discount. (default "in_scope")
vat_scope_reason "damages" | "organschaft" | "vat_group" or null Required with vat_scope outside, refused otherwise: damages (compensation for damage), vat_group (a supply within a Swiss VAT group, Art. 13 MWSTG) or organschaft (an Innenumsatz of a German Organschaft, § 2 para. 2 no. 2 UStG).
dimensions map of string Dimension code to value code, e.g. {"cost_centre": "CC-200", "project": "P-17"} (GET /v1/dimensions): each an active value, valid on the document date in its company. They go onto the journal lines of the line's amount (not its VAT or the receivable or payable). An account may require a dimension (required_accounts of the dimension): the document is then refused at submission without it.
until string (date) or null The last due date the line is invoiced on: the last day of the revenue contract it bills, after that contract was ended early or shortened; null for every due date.

RecurringPause

Field Type Required Description
reason string yes Why; kept with the template and in the audit. (min length 1; max length 500)

RecurringRunInput

Field Type Required Description
through string (date) yes Make the invoices that fall due on or before this day (ISO 8601, inclusive). It may not lie more than a day after the server's date (UTC), so your own date is always accepted.
entity string or null Only the templates of this entity (code or id); default: all.
template string (uuid) or null Only this template (it must be active); default: every one.

RecurringRunItem

Field Type Required Description
template_id string (uuid) yes The template the invoice was made from.
template_name string yes Its name.
due_date string (date) yes The due date of the period; also the invoice date.
status "drafted" | "failed" | "pending_approval" | "posted" yes drafted (left for a person to submit), posted, pending_approval (the approval policy asked for a second person) or failed (nothing was made; error says why, and the next run tries again).
document_id string (uuid) or null yes The invoice; null when it failed.
document_number string or null yes Its number once posted; null for a draft or a failure.
error string or null yes Why a failed attempt failed; null otherwise.

RecurringRunOut

Field Type Required Description
id string (uuid) yes Id of the run record.
template_id string (uuid) yes The template.
due_date string (date) yes The due date of the period.
status "drafted" | "failed" | "pending_approval" | "posted" yes What came of the period; see the run result.
document_id string (uuid) or null yes The invoice; null when it failed.
document_number string or null yes Its number once posted.
document_status string or null yes The invoice's status now (draft, pending_approval, posted, void...).
error string or null yes Why a failed attempt failed.
created_by object yes Who ran it (type, id, name).
created_at string (date-time) yes When (UTC).

RecurringRunResult

Field Type Required Description
through string (date) yes The day the run went through.
made integer yes Invoices made by this run (drafts and posted ones).
failed integer yes Periods that failed; they stay due.
items array of RecurringRunItem yes One entry per period made or failed, by template and due date.
more_due boolean yes True when a template still has due dates after this run (a run makes twelve per template at most, and stops at a failed period): run again.
Stabiso, the Enterprise System of Record · generated from the code and its guides · release 20b7dd5b0c4d