StabisoDocs

API reference

Subscriptions

Subscriptions: a committed term of recurring products billed monthly, quarterly or yearly, in advance or in arrears. A second person approves the terms, which activates the term's revenue contract; the run bills each period against the contract's obligations.

Operation Summary Scope Idempotency-Key
GET /v1/subscriptions List subscriptions read
POST /v1/subscriptions Create a subscription draft required
GET /v1/subscriptions/metrics Get MRR, ARR and churn read
POST /v1/subscriptions/run Bill the subscription periods that are due draft required
GET /v1/subscriptions/{subscription_id} Get a subscription read
POST /v1/subscriptions/{subscription_id}/activate Ask for a subscription's terms to be approved draft
GET /v1/subscriptions/{subscription_id}/amendments List a subscription's amendments read
POST /v1/subscriptions/{subscription_id}/amendments Ask for an amendment of an active subscription draft
POST /v1/subscriptions/{subscription_id}/amendments/preview Preview an amendment of a subscription read
POST /v1/subscriptions/{subscription_id}/cancel Cancel a subscription draft
GET /v1/subscriptions/{subscription_id}/invoices List a subscription's billed periods read

GET /v1/subscriptions

List subscriptions

Subscriptions, the newest first, each with its lines. Read-only.

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

Parameters

In Name Type Required Description
query entity string or null Only this entity's subscriptions (code or id).
query status "active" | "cancelled" | "draft" | "ended" | "pending_approval" or null Only subscriptions with this status.
query party_id string (uuid) or null Only this customer's subscriptions.
query limit integer How many, the newest first. (min 1; max 1000; default 100)

Example request

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

Responses

Status Meaning Body
200 OK array of SubscriptionOut
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)

[
  {
    "auto_renew": false,
    "auto_submit": false,
    "billing_frequency": "monthly",
    "billing_timing": "advance",
    "created_at": "2026-10-05T08:00:00Z",
    "created_by": {
      "id": "user:alice",
      "name": "Alice",
      "type": "human"
    },
    "currency": "CHF",
    "current_term_end": "2027-12-31",
    "current_term_start": "2027-01-01",
    "entity": "CH01",
    "id": "5a6b7c8d-9e0f-4a1b-8c2d-3e4f5a6b7c8d",
    "lines": [],
    "number": "SUB-000001",
    "party_code": "C-CH",
    "party_id": "1b2c3d4e-5f6a-4b7c-8d9e-0f1a2b3c4d5e",
    "party_name": "Zürich Kunde AG",
    "renewal_notice_days": 30,
    "start_date": "2027-01-01",
    "status": "draft",
    "term_amount": "6000.00",
    "term_months": 12,
    "title": "Platform, 10 seats"
  }
]

POST /v1/subscriptions

Create a subscription

Create a subscription as a draft: the customer, the start day, the committed term (a whole number of billing periods), how it is billed (monthly, quarterly or yearly; in advance or in arrears) and its lines, each a recurring product with a quantity, a price per month or year (by default the list price in force on the start day) and a discount. It bills in currency: by default the won opportunity's, else the customer's, else the entity's functional currency; prices are taken in that currency, never converted. Every product needs an SSP in it on the start day: it allocates the term's revenue contract. The first invoice is built (not kept) to check the customer's tax treatment and the accounts. Nothing is billed until a second person approves the terms (POST .../{subscription_id}/activate). A draft's terms are not edited: cancel it and create a new one. 404 for an unknown entity, customer, product or account; 422 for a product that is not recurring, inactive, goods, or without a price or SSP in the currency (subscription_currency_unpriced names the currencies it is priced in), or a term that is not a whole number of billing periods. Needs an Idempotency-Key.

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

Request body

application/json (required): SubscriptionInput

Field Type Required Description
entity string yes Code (or id) of the entity.
party string yes Code (or id) of the customer.
title string yes What is subscribed. (min length 1; max length 200)
currency string or null ISO 4217 code of the currency it bills in: its prices, invoices, credit notes and revenue contract are in it. Default: the won opportunity's currency, else the customer's, else the entity's functional currency. Each product needs a list price (or the line a unit_price) and an SSP in it: a price is never converted. (pattern ^[A-Z]{3}$)
start_date string (date) yes The first day of the first term.
term_months integer yes The committed term in months: a whole number of billing periods. (min 1; max 60)
billing_frequency "monthly" | "quarterly" | "yearly" How often it is invoiced; the periods are counted from the term start. (default "monthly")
billing_timing "advance" | "arrears" advance (invoiced on a period's first day) or arrears (on the day after its last). (default "advance")
auto_renew boolean Whether the approved terms renew themselves: the first billing run after the term's last day starts the next term, as long and with its own revenue contract, at the prices in force raised by renewal_uplift_percent. Without it a person asks for a renewal (POST .../amendments, kind renew) or the subscription ends. (default false)
renewal_notice_days integer Days before the term's last day the renewal is due (renewal_status due). (min 0; max 365; default 30)
renewal_uplift_percent decimal string or null By how much a renewal raises each line's price (each price rounded to the currency's minor unit); none keeps the prices. At most 4 decimals. (min 0; max 100)
payment_terms_days integer or null Days to pay an invoice; default the customer's. (min 0; max 365)
auto_submit boolean True: the system submits each invoice under the approved terms; false: the run leaves drafts for a person. (default false)
opportunity string (uuid) or null The won opportunity it came from (GET /v1/opportunities).
lines array of SubscriptionLineInput yes What is sold. (min items 1; max items 50)

Example request

curl -X POST "$ERP_URL/v1/subscriptions" \
  -H "Authorization: Bearer $ERP_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "auto_renew": false,
  "billing_frequency": "monthly",
  "billing_timing": "advance",
  "entity": "CH01",
  "lines": [
    {
      "product": "PLATFORM",
      "quantity": "10"
    }
  ],
  "party": "C-CH",
  "start_date": "2027-01-01",
  "term_months": 12,
  "title": "Platform, 10 seats"
}'

Responses

Status Meaning Body
201 Created SubscriptionOut
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)

{
  "auto_renew": false,
  "auto_submit": false,
  "billing_frequency": "monthly",
  "billing_timing": "advance",
  "created_at": "2026-10-05T08:00:00Z",
  "created_by": {
    "id": "user:alice",
    "name": "Alice",
    "type": "human"
  },
  "currency": "CHF",
  "current_term_end": "2027-12-31",
  "current_term_start": "2027-01-01",
  "entity": "CH01",
  "id": "5a6b7c8d-9e0f-4a1b-8c2d-3e4f5a6b7c8d",
  "lines": [],
  "number": "SUB-000001",
  "party_code": "C-CH",
  "party_id": "1b2c3d4e-5f6a-4b7c-8d9e-0f1a2b3c4d5e",
  "party_name": "Zürich Kunde AG",
  "renewal_notice_days": 30,
  "start_date": "2027-01-01",
  "status": "draft",
  "term_amount": "6000.00",
  "term_months": 12,
  "title": "Platform, 10 seats"
}

GET /v1/subscriptions/metrics

Get MRR, ARR and churn

Monthly recurring revenue (MRR) at each month's end (today for the current month), read from the subscription lines in force that day, never from the ledger: quantity x the price per month (a yearly price over 12), less the discount. ARR is 12 x MRR. Each month explains the change from the month before with each customer's movement: new, expansion, contraction, churn and reactivation, naming the subscriptions that moved, and gives the customer churn rate, the gross MRR churn rate and the net MRR retention. Amounts are kept in each subscription's currency and converted into the entity's functional currency at the ECB fixing of each month's day; the change of rates is the month's currency_effect. At most 36 months; a month to come is refused (422 month_in_future). Read-only.

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

Parameters

In Name Type Required Description
query entity string yes Entity code (or id).
query from_month string or null First month YYYY-MM; default the twelfth month back from to_month. (pattern `^\d{4}-(0[1-9]
query to_month string or null Last month YYYY-MM, at the latest the current one; default the current one. (pattern `^\d{4}-(0[1-9]

Example request

curl "$ERP_URL/v1/subscriptions/metrics?entity=CH01" \
  -H "Authorization: Bearer $ERP_KEY"

Responses

Status Meaning Body
200 OK RecurringRevenueReport
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)

{
  "currency": "CHF",
  "entity": "CH01",
  "from_month": "2026-10",
  "months": [
    {
      "arr": {
        "amount": "140424.00",
        "by_currency": [
          {
            "amount": "90000.00",
            "currency": "CHF"
          },
          {
            "amount": "54360.00",
            "currency": "EUR"
          }
        ]
      },
      "as_of": "2026-10-23",
      "churn": {
        "amount": "-1000.00",
        "by_currency": [
          {
            "amount": "-1000.00",
            "currency": "CHF"
          }
        ]
      },
      "churned_customers": 1,
      "closing": {
        "amount": "11702.00",
        "by_currency": [
          {
            "amount": "7500.00",
            "currency": "CHF"
          },
          {
            "amount": "4530.00",
            "currency": "EUR"
          }
        ]
      },
      "contraction": {
        "amount": "0.00",
        "by_currency": []
      },
      "currency_effect": "-12.00",
      "customer_churn_rate": "8.3",
      "customers": 12,
      "customers_opening": 12,
      "expansion": {
        "amount": "214.00",
        "by_currency": [
          {
            "amount": "230.00",
            "currency": "EUR"
          }
        ]
      },
      "fixing_date": "2026-10-23",
      "month": "2026-10",
      "movements": [
        {
          "amount": "-1000.00",
          "converted": "-1000.00",
          "currency": "CHF",
          "kind": "churn",
          "party_code": "C-1002",
          "party_id": "0b9c2f4e-3d1a-4c55-9a77-6f1e2d3c4b5a",
          "party_name": "Alpen Logistik AG",
          "subscriptions": [
            {
              "id": "5a6b7c8d-9e0f-4a1b-8c2d-3e4f5a6b7c8d",
              "number": "SUB-000004"
            }
          ]
        }
      ],
      "mrr_churn_rate": "8.4",
      "net_mrr_retention": "93.4",
      "new": {
        "amount": "500.00",
        "by_currency": [
          {
            "amount": "500.00",
            "currency": "CHF"
          }
        ]
      },
      "new_customers": 1,
      "opening": {
        "amount": "12000.00",
        "by_currency": [
          {
            "amount": "8000.00",
            "currency": "CHF"
          },
          {
            "amount": "4300.00",
            "currency": "EUR"
          }
        ]
      },
      "reactivated_customers": 0,
      "reactivation": {
        "amount": "0.00",
        "by_currency": []
      }
    }
  ],
  "to_month": "2026-10",
  "today": "2026-10-23",
  "unconverted": []
}

POST /v1/subscriptions/run

Bill the subscription periods that are due

Make the invoice of every billing period of an active subscription whose invoice date is on or before through (never after today where the subscription's company is): in advance on the period's first day, in arrears on the day after its last; when that day is in a closed period, on the first day of the next open one. Each line bills its obligation in the term's revenue contract (to the contract liability) at the period's part of the term's amount, so the periods of a term add up to its transaction price exactly.

Each invoice is a draft for a person to submit, or, with 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. A draft's customer, products, quantities, prices and obligations cannot be changed (409); a draft voided instead is made again by the next run. It is idempotent: one invoice per subscription and period, enforced by the database; an Idempotency-Key replays the first answer. A period that cannot be made (an inactive customer, the contract's billing cap, terms that no longer match the approved digest) is failed with its error and stops that subscription at that period, not the run; the next run tries it again. At most twelve periods per subscription are billed in one run: more_due says to run again. The first run after a fully billed term's last day renews it when its terms renew it (auto_renew) or a renewal was approved (the next term with its own revenue contract, whose due periods it bills too), and otherwise marks it ended. A person's request: 403 for an agent. Needs an Idempotency-Key.

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

Request body

application/json (required): SubscriptionRunInput

Field Type Required Description
through string (date) yes Bill the periods whose invoice date is on or before this day (inclusive). It may not lie more than a day after the server's date (UTC).
entity string or null Only this entity (code or id).
subscription string (uuid) or null Only this subscription (it must be active).

Example request

curl -X POST "$ERP_URL/v1/subscriptions/run" \
  -H "Authorization: Bearer $ERP_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "entity": "CH01",
  "through": "2027-01-31"
}'

Responses

Status Meaning Body
200 OK SubscriptionRunResult
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)

{
  "through": "2026-09-21",
  "made": 1,
  "failed": 1,
  "items": [
    {
      "subscription_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "number": "string",
      "period_start": "2026-09-21",
      "period_end": "2026-09-21",
      "invoice_date": "2026-09-21",
      "status": "drafted",
      "document_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "net": "string",
      "error": "string"
    }
  ],
  "more_due": true
}

GET /v1/subscriptions/{subscription_id}

Get a subscription

One subscription with its lines, who approved its terms, its term's revenue contract and each line's obligation in it. 404 when it does not exist.

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

Parameters

In Name Type Required Description
path subscription_id string (uuid) yes Id of the subscription (GET /v1/subscriptions).

Example request

curl "$ERP_URL/v1/subscriptions/5a6b7c8d-9e0f-4a1b-8c2d-3e4f5a6b7c8d" \
  -H "Authorization: Bearer $ERP_KEY"

Responses

Status Meaning Body
200 OK SubscriptionOut
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)

{
  "auto_renew": false,
  "auto_submit": false,
  "billing_frequency": "monthly",
  "billing_timing": "advance",
  "created_at": "2026-10-05T08:00:00Z",
  "created_by": {
    "id": "user:alice",
    "name": "Alice",
    "type": "human"
  },
  "currency": "CHF",
  "current_term_end": "2027-12-31",
  "current_term_start": "2027-01-01",
  "entity": "CH01",
  "id": "5a6b7c8d-9e0f-4a1b-8c2d-3e4f5a6b7c8d",
  "lines": [],
  "number": "SUB-000001",
  "party_code": "C-CH",
  "party_id": "1b2c3d4e-5f6a-4b7c-8d9e-0f1a2b3c4d5e",
  "party_name": "Zürich Kunde AG",
  "renewal_notice_days": 30,
  "start_date": "2027-01-01",
  "status": "draft",
  "term_amount": "6000.00",
  "term_months": 12,
  "title": "Platform, 10 seats"
}

POST /v1/subscriptions/{subscription_id}/activate

Ask for a subscription's terms to be approved

Ask for the terms of a draft subscription to be approved. A second person always decides (POST /v1/approvals/{approval_id}/approve): never the person who drafted the subscription or asked, and never an agent (the policy action approve_subscription; an agent's request is refused with 403, naming the rule). The approval is bound to the digest of the terms. Approving creates and activates the term's revenue contract in the same transaction: one over-time obligation per line, recognised month by month, its SSP the product's over the term; the transaction price is what the term bills. The subscription is then active and the run bills it from its first period. 409 when it is not a draft or the customer is not active.

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

Parameters

In Name Type Required Description
path subscription_id string (uuid) yes Id of the subscription (GET /v1/subscriptions).

Example request

curl -X POST "$ERP_URL/v1/subscriptions/5a6b7c8d-9e0f-4a1b-8c2d-3e4f5a6b7c8d/activate" \
  -H "Authorization: Bearer $ERP_KEY"

Responses

Status Meaning Body
200 OK SubscriptionActivationOut
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)

{
  "subscription": {
    "auto_renew": false,
    "auto_submit": false,
    "billing_frequency": "monthly",
    "billing_timing": "advance",
    "created_at": "2026-10-05T08:00:00Z",
    "created_by": {
      "id": "user:alice",
      "name": "Alice",
      "type": "human"
    },
    "currency": "CHF",
    "current_term_end": "2027-12-31",
    "current_term_start": "2027-01-01",
    "entity": "CH01",
    "id": "5a6b7c8d-9e0f-4a1b-8c2d-3e4f5a6b7c8d",
    "lines": [],
    "number": "SUB-000001",
    "party_code": "C-CH",
    "party_id": "1b2c3d4e-5f6a-4b7c-8d9e-0f1a2b3c4d5e",
    "party_name": "Zürich Kunde AG",
    "renewal_notice_days": 30,
    "start_date": "2027-01-01",
    "status": "draft",
    "term_amount": "6000.00",
    "term_months": 12,
    "title": "Platform, 10 seats"
  },
  "decision": "allow",
  "approval_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}

GET /v1/subscriptions/{subscription_id}/amendments

List a subscription's amendments

The amendments asked for the subscription, the oldest first, each with the amounts it named and whether it was applied or rejected. 404 when the subscription does not exist.

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

Parameters

In Name Type Required Description
path subscription_id string (uuid) yes Id of the subscription (GET /v1/subscriptions).

Example request

curl "$ERP_URL/v1/subscriptions/5a6b7c8d-9e0f-4a1b-8c2d-3e4f5a6b7c8d/amendments" \
  -H "Authorization: Bearer $ERP_KEY"

Responses

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

Example response (shape)

[
  {
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "subscription_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "kind": "cancel",
    "end_date": "2026-09-21",
    "effective_date": "2026-09-21",
    "proration": "none",
    "reason": "string",
    "term_start": "2026-09-21",
    "contract_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "amounts": {
      "old_price": "string",
      "new_price": "string",
      "earned": "string",
      "credit_total": "string",
      "credits": [
        null
      ],
      "next_term_start": "2026-09-21",
      "next_term_end": "2026-09-21",
      "next_price": "string",
      "first_period_start": "2026-09-21",
      "first_period_end": "2026-09-21",
      "first_charge": "string"
    },
    "status": "applied",
    "modification_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "lines": [
      {
        "product_id": null,
        "product_code": null,
        "description": null,
        "quantity": null,
        "unit_price": null,
        "price_period": null,
        "discount_percent": null,
        "monthly_amount": null
      }
    ],
    "new_contract_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "approved_by": {},
    "approved_at": "2026-09-21T09:30:00Z",
    "created_by": {},
    "created_at": "2026-09-21T09:30:00Z"
  }
]

POST /v1/subscriptions/{subscription_id}/amendments

Ask for an amendment of an active subscription

Ask a second person to approve an amendment of an active subscription's current term. cancel ends it early: end_date is the last day of service (the term's last day ends it there without renewing). A second person always decides (POST /v1/approvals/{approval_id}/approve), never the person who asked, and never an agent (403, the policy action approve_subscription). The request names the amounts, worked out from what is posted now; approving refuses (409 terms_changed) when they changed in between. Approving, in one transaction: billing stops after end_date (the period it falls in is billed by its days if not billed yet); the term's revenue contract is modified prospectively (its price becomes what the customer now pays, the schedule from the first day not recognised is rewritten, recognised revenue stays); with proration prorate, the system posts a credit note per period billed past end_date for its unused part, naming the obligations; with none, what was billed is kept and its part for service not given is revenue on end_date. The run then ends the subscription after end_date. 409 when the subscription is not active or already ends, has an invoice not posted, revenue recognised after end_date, or an amendment waiting; 422 when end_date is outside the current term or leaves nothing to bill.

renew (for a subscription whose terms do not renew it, auto_renew false) renews it into the next term: as long as the term, its months counted from the start day, at the prices in force raised by renewal_uplift_percent, with its own revenue contract activated in the approver's name. Approved before the term ends, the first billing run after its last day starts the next term; approved after, it starts at once. 409 when the subscription is not active, ends early, renews itself (renewal_automatic) or its renewal is approved already.

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

Parameters

In Name Type Required Description
path subscription_id string (uuid) yes Id of the subscription (GET /v1/subscriptions).

Request body

application/json (required): AmendmentInput

Field Type Required Description
kind "cancel" | "change" | "renew" cancel: end the current term early, on end_date. renew: renew it into the next term (for a subscription whose terms do not renew it, auto_renew false). change: bill lines instead of the current lines from effective_date to the term's end (an upgrade or a downgrade). (default "cancel")
end_date string (date) or null cancel: the last day of service, from the current term's first day to its last (its last day ends it at the term end without renewing).
proration "none" | "prorate" cancel: prorate credits the unused part of every period billed past end_date (a credit note per invoice); none keeps what was billed, a non-refundable prepayment, recognised as revenue on end_date. (default "prorate")
effective_date string (date) or null change: the first day of the new lines, after the first day of the terms in force and at the latest the term's last day (today, the first day of the next billing period, or another day). Every period billed past the day before is credited for the part the new lines replace, and the first period of the new lines bills its days from this day.
lines array of SubscriptionLineInput or null change: every line from effective_date (a line that stays as it is is named again), each priced as given or at the product's list price in force that day, and with an SSP that day. (min items 1; max items 50)
reason string yes Why, kept with the change. (min length 3; max length 1000)

Example request

curl -X POST "$ERP_URL/v1/subscriptions/5a6b7c8d-9e0f-4a1b-8c2d-3e4f5a6b7c8d/amendments" \
  -H "Authorization: Bearer $ERP_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "end_date": "2026-06-20",
  "kind": "cancel",
  "proration": "prorate",
  "reason": "The customer closes its Zürich office"
}'

Responses

Status Meaning Body
201 Created AmendmentRequestOut
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)

{
  "amendment": {
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "subscription_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "kind": "cancel",
    "end_date": "2026-09-21",
    "effective_date": "2026-09-21",
    "proration": "none",
    "reason": "string",
    "term_start": "2026-09-21",
    "contract_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "amounts": {
      "old_price": "string",
      "new_price": "string",
      "earned": "string",
      "credit_total": "string",
      "credits": [
        null
      ],
      "next_term_start": null,
      "next_term_end": null,
      "next_price": null,
      "first_period_start": null,
      "first_period_end": null,
      "first_charge": null
    },
    "status": "applied",
    "modification_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "lines": [
      null
    ],
    "new_contract_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "approved_by": {},
    "approved_at": "2026-09-21T09:30:00Z",
    "created_by": {},
    "created_at": "2026-09-21T09:30:00Z"
  },
  "approval_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "subscription": {
    "auto_renew": false,
    "auto_submit": false,
    "billing_frequency": "monthly",
    "billing_timing": "advance",
    "created_at": "2026-10-05T08:00:00Z",
    "created_by": {
      "id": "user:alice",
      "name": "Alice",
      "type": "human"
    },
    "currency": "CHF",
    "current_term_end": "2027-12-31",
    "current_term_start": "2027-01-01",
    "entity": "CH01",
    "id": "5a6b7c8d-9e0f-4a1b-8c2d-3e4f5a6b7c8d",
    "lines": [],
    "number": "SUB-000001",
    "party_code": "C-CH",
    "party_id": "1b2c3d4e-5f6a-4b7c-8d9e-0f1a2b3c4d5e",
    "party_name": "Zürich Kunde AG",
    "renewal_notice_days": 30,
    "start_date": "2027-01-01",
    "status": "draft",
    "term_amount": "6000.00",
    "term_months": 12,
    "title": "Platform, 10 seats"
  }
}

POST /v1/subscriptions/{subscription_id}/amendments/preview

Preview an amendment of a subscription

What an amendment would change, worked out from what is posted now; nothing is written. For cancel: the term's new price (the revenue contract's transaction price), the part earned by the service through end_date, and a credit per period billed past it. For renew: the next term's days and price (new_price). The errors are those of the request below.

Required scope read (admin implies every scope)
Idempotency-Key not used
Operation id preview_subscription_amendment_v1_subscriptions__subscription_id__amendments_preview_post

Parameters

In Name Type Required Description
path subscription_id string (uuid) yes Id of the subscription (GET /v1/subscriptions).

Request body

application/json (required): AmendmentInput

Field Type Required Description
kind "cancel" | "change" | "renew" cancel: end the current term early, on end_date. renew: renew it into the next term (for a subscription whose terms do not renew it, auto_renew false). change: bill lines instead of the current lines from effective_date to the term's end (an upgrade or a downgrade). (default "cancel")
end_date string (date) or null cancel: the last day of service, from the current term's first day to its last (its last day ends it at the term end without renewing).
proration "none" | "prorate" cancel: prorate credits the unused part of every period billed past end_date (a credit note per invoice); none keeps what was billed, a non-refundable prepayment, recognised as revenue on end_date. (default "prorate")
effective_date string (date) or null change: the first day of the new lines, after the first day of the terms in force and at the latest the term's last day (today, the first day of the next billing period, or another day). Every period billed past the day before is credited for the part the new lines replace, and the first period of the new lines bills its days from this day.
lines array of SubscriptionLineInput or null change: every line from effective_date (a line that stays as it is is named again), each priced as given or at the product's list price in force that day, and with an SSP that day. (min items 1; max items 50)
reason string yes Why, kept with the change. (min length 3; max length 1000)

Example request

curl -X POST "$ERP_URL/v1/subscriptions/5a6b7c8d-9e0f-4a1b-8c2d-3e4f5a6b7c8d/amendments/preview" \
  -H "Authorization: Bearer $ERP_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "end_date": "2026-06-20",
  "kind": "cancel",
  "proration": "prorate",
  "reason": "The customer closes its Zürich office"
}'

Responses

Status Meaning Body
200 OK AmendmentAmounts
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)

{
  "old_price": "string",
  "new_price": "string",
  "earned": "string",
  "credit_total": "string",
  "credits": [
    {
      "period_start": "2026-09-21",
      "period_end": "2026-09-21",
      "invoice_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "amount": "string"
    }
  ],
  "next_term_start": "2026-09-21",
  "next_term_end": "2026-09-21",
  "next_price": "string",
  "first_period_start": "2026-09-21",
  "first_period_end": "2026-09-21",
  "first_charge": "string"
}

POST /v1/subscriptions/{subscription_id}/cancel

Cancel a subscription

Cancel a draft subscription, or one waiting for approval (its request is withdrawn; only a person withdraws it), with a reason kept in the audit trail. An active subscription is not cancelled here: it is ended early by an amendment (POST .../{subscription_id}/amendments). 409 otherwise.

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

Parameters

In Name Type Required Description
path subscription_id string (uuid) yes Id of the subscription (GET /v1/subscriptions).

Request body

application/json (required): SubscriptionCancelInput

Field Type Required Description
reason string yes Why it is cancelled. (min length 3; max length 1000)

Example request

curl -X POST "$ERP_URL/v1/subscriptions/5a6b7c8d-9e0f-4a1b-8c2d-3e4f5a6b7c8d/cancel" \
  -H "Authorization: Bearer $ERP_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "reason": "The customer chose another plan"
}'

Responses

Status Meaning Body
200 OK SubscriptionOut
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)

{
  "auto_renew": false,
  "auto_submit": false,
  "billing_frequency": "monthly",
  "billing_timing": "advance",
  "created_at": "2026-10-05T08:00:00Z",
  "created_by": {
    "id": "user:alice",
    "name": "Alice",
    "type": "human"
  },
  "currency": "CHF",
  "current_term_end": "2027-12-31",
  "current_term_start": "2027-01-01",
  "entity": "CH01",
  "id": "5a6b7c8d-9e0f-4a1b-8c2d-3e4f5a6b7c8d",
  "lines": [],
  "number": "SUB-000001",
  "party_code": "C-CH",
  "party_id": "1b2c3d4e-5f6a-4b7c-8d9e-0f1a2b3c4d5e",
  "party_name": "Zürich Kunde AG",
  "renewal_notice_days": 30,
  "start_date": "2027-01-01",
  "status": "draft",
  "term_amount": "6000.00",
  "term_months": 12,
  "title": "Platform, 10 seats"
}

GET /v1/subscriptions/{subscription_id}/invoices

List a subscription's billed periods

The billing periods the run billed, in order, each with its invoice or, for a failed attempt, the error (the next run tries it again). 404 when the subscription does not exist.

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

Parameters

In Name Type Required Description
path subscription_id string (uuid) yes Id of the subscription (GET /v1/subscriptions).

Example request

curl "$ERP_URL/v1/subscriptions/5a6b7c8d-9e0f-4a1b-8c2d-3e4f5a6b7c8d/invoices" \
  -H "Authorization: Bearer $ERP_KEY"

Responses

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

Example response (shape)

[
  {
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "period_start": "2026-09-21",
    "period_end": "2026-09-21",
    "kind": "credit",
    "amendment_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "status": "drafted",
    "document_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "document_status": "string",
    "error": "string",
    "created_at": "2026-09-21T09:30:00Z"
  }
]

Schemas

AmendmentAmounts

Field Type Required Description
old_price string yes The term's revenue contract price before, net of VAT.
new_price string yes Its price after: what the customer pays for the term as amended (renew: what the next term costs, the next contract's price).
earned string yes The part of the new price for the service through the last day, recognised month by month; the rest (a kept prepayment) is recognised on the last day.
credit_total string yes What the credit notes credit in all, net of VAT.
credits array of AmendmentCredit yes A credit note per billed period.
next_term_start string (date) or null renew: the next term's first day.
next_term_end string (date) or null renew: its last day.
next_price string or null change: what the new lines bill from the effective date to the term's end, net of VAT: the price of their revenue contract.
first_period_start string (date) or null change: the first day of the new lines' first billing period.
first_period_end string (date) or null change: its last day.
first_charge string or null change: what that first period bills, net of VAT: the new lines for the rest of the period the change falls in (old_price less new_price is what the current lines no longer bill, credit_total of it credited on invoices already made).

AmendmentCredit

Field Type Required Description
period_start string (date) yes The billed period credited: its first day.
period_end string (date) yes Its last day.
invoice_id string (uuid) yes The period's invoice.
amount string yes What is credited, net of VAT.

AmendmentInput

Field Type Required Description
kind "cancel" | "change" | "renew" cancel: end the current term early, on end_date. renew: renew it into the next term (for a subscription whose terms do not renew it, auto_renew false). change: bill lines instead of the current lines from effective_date to the term's end (an upgrade or a downgrade). (default "cancel")
end_date string (date) or null cancel: the last day of service, from the current term's first day to its last (its last day ends it at the term end without renewing).
proration "none" | "prorate" cancel: prorate credits the unused part of every period billed past end_date (a credit note per invoice); none keeps what was billed, a non-refundable prepayment, recognised as revenue on end_date. (default "prorate")
effective_date string (date) or null change: the first day of the new lines, after the first day of the terms in force and at the latest the term's last day (today, the first day of the next billing period, or another day). Every period billed past the day before is credited for the part the new lines replace, and the first period of the new lines bills its days from this day.
lines array of SubscriptionLineInput or null change: every line from effective_date (a line that stays as it is is named again), each priced as given or at the product's list price in force that day, and with an SSP that day. (min items 1; max items 50)
reason string yes Why, kept with the change. (min length 3; max length 1000)

AmendmentLineOut

Field Type Required Description
product_id string (uuid) yes The product.
product_code string yes Its code.
description string yes What is sold.
quantity string yes How many units.
unit_price string yes Net price of one unit per price_period.
price_period "month" | "year" yes month or year.
discount_percent string yes Discount in percent.
monthly_amount string yes What a month of service costs.

AmendmentOut

Field Type Required Description
id string (uuid) yes Amendment id.
subscription_id string (uuid) yes The subscription.
kind "cancel" | "change" | "renew" yes What it changes.
end_date string (date) or null yes The last day of service of the term amended (renew: the term's last day).
effective_date string (date) yes The first day the change applies.
proration "none" | "prorate" or null yes cancel: prorate or none.
reason string yes Why.
term_start string (date) yes The first day of the term it amends.
contract_id string (uuid) yes That term's revenue contract.
amounts AmendmentAmounts yes The amounts the second person approves.
status "applied" | "pending_approval" | "rejected" yes pending_approval (a second person decides in /v1/approvals), applied or rejected.
modification_id string (uuid) or null yes The modification of the revenue contract it made, once applied.
lines array of AmendmentLineOut or null change: the lines from effective_date.
new_contract_id string (uuid) or null change: the revenue contract of the new lines, once applied.
approved_by object or null yes Who approved it.
approved_at string (date-time) or null yes When.
created_by object yes Who asked for it.
created_at string (date-time) yes When.

AmendmentRequestOut

Field Type Required Description
amendment AmendmentOut yes The amendment, waiting for approval.
approval_id string (uuid) yes The approval request a second person decides (POST /v1/approvals/{approval_id}/approve).
subscription SubscriptionOut yes The subscription, unchanged until then.

MrrMonth

Field Type Required Description
month string yes YYYY-MM.
as_of string (date) yes The day MRR is read: the month's last day, or today for the current month.
fixing_date string (date) or null yes The ECB fixing that converts the month's amounts (the latest on or before as_of); null when there is none (other currencies are then in unconverted).
opening Figure yes MRR at the end of the month before.
new Figure yes MRR of customers who had none ever before.
expansion Figure yes More MRR from customers who had some at both ends.
contraction Figure yes Negative.
churn Figure yes Negative.
reactivation Figure yes MRR of customers who had some before, but not at the month before's end.
currency_effect string yes In the report's currency: the opening MRR's change of value from the month before's fixing to this month's (and the cents of converting each customer on its own), so that opening + movements + currency effect = closing.
closing Figure yes MRR at as_of: every subscription line in force that day.
arr Figure yes Annual recurring revenue: 12 x the closing MRR.
customers_opening integer yes Customers with MRR at the month before's end.
customers integer yes Customers with MRR at as_of.
new_customers integer yes Customers with a new movement.
churned_customers integer yes Customers with a churn movement.
reactivated_customers integer yes Customers with a reactivation movement.
customer_churn_rate string or null yes Churned customers over the opening customers, in percent (one decimal); null without opening customers. (pattern ^(?!^[-+.]*$)[+-]?0*\d*\.?\d*$)
mrr_churn_rate string or null yes Gross MRR churn: churn and contraction over the opening MRR (both at this month's fixing), in percent; null without opening MRR. (pattern ^(?!^[-+.]*$)[+-]?0*\d*\.?\d*$)
net_mrr_retention string or null yes The opening MRR kept, with expansion and less contraction and churn, over the opening MRR (both at this month's fixing), in percent; new and reactivated customers are not counted. Null without opening MRR. (pattern ^(?!^[-+.]*$)[+-]?0*\d*\.?\d*$)
movements array of MrrMovement yes Each customer's movement, the largest first.

MrrMovement

Field Type Required Description
kind "churn" | "contraction" | "expansion" | "new" | "reactivation" yes new: the customer's first MRR; expansion / contraction: more or less MRR from a customer who had some at the month before's end and still has; churn: all of it gone; reactivation: MRR again from a customer who had some before, but not at the month before's end.
party_id string (uuid) yes The customer's id.
party_code string yes The customer's code.
party_name string yes The customer's name.
currency string yes The currency the customer's subscriptions bill in.
amount string yes The change of MRR in that currency (negative: less).
converted string yes The change in the report's currency, at the month's fixing.
subscriptions array of SubscriptionRef yes The subscriptions whose MRR changed.

RecurringRevenueReport

Field Type Required Description
entity string yes Entity code.
currency string yes The report's currency: the entity's functional currency.
from_month string yes The first month, YYYY-MM.
to_month string yes The last month, YYYY-MM (the current month by default).
today string (date) yes Today where the entity is.
unconverted array of string yes Currencies without an ECB rate on a month's day, left out of the converted amounts of that month.
months array of MrrMonth yes Each month, oldest first.

SubscriptionActivationOut

Field Type Required Description
subscription SubscriptionOut yes The subscription after the request.
decision "allow" | "require_approval" yes allow: active now; require_approval: a second person decides (POST /v1/approvals/{approval_id}/approve).
approval_id string (uuid) or null yes The approval request, when one is open.

SubscriptionCancelInput

Field Type Required Description
reason string yes Why it is cancelled. (min length 3; max length 1000)

SubscriptionInput

Field Type Required Description
entity string yes Code (or id) of the entity.
party string yes Code (or id) of the customer.
title string yes What is subscribed. (min length 1; max length 200)
currency string or null ISO 4217 code of the currency it bills in: its prices, invoices, credit notes and revenue contract are in it. Default: the won opportunity's currency, else the customer's, else the entity's functional currency. Each product needs a list price (or the line a unit_price) and an SSP in it: a price is never converted. (pattern ^[A-Z]{3}$)
start_date string (date) yes The first day of the first term.
term_months integer yes The committed term in months: a whole number of billing periods. (min 1; max 60)
billing_frequency "monthly" | "quarterly" | "yearly" How often it is invoiced; the periods are counted from the term start. (default "monthly")
billing_timing "advance" | "arrears" advance (invoiced on a period's first day) or arrears (on the day after its last). (default "advance")
auto_renew boolean Whether the approved terms renew themselves: the first billing run after the term's last day starts the next term, as long and with its own revenue contract, at the prices in force raised by renewal_uplift_percent. Without it a person asks for a renewal (POST .../amendments, kind renew) or the subscription ends. (default false)
renewal_notice_days integer Days before the term's last day the renewal is due (renewal_status due). (min 0; max 365; default 30)
renewal_uplift_percent decimal string or null By how much a renewal raises each line's price (each price rounded to the currency's minor unit); none keeps the prices. At most 4 decimals. (min 0; max 100)
payment_terms_days integer or null Days to pay an invoice; default the customer's. (min 0; max 365)
auto_submit boolean True: the system submits each invoice under the approved terms; false: the run leaves drafts for a person. (default false)
opportunity string (uuid) or null The won opportunity it came from (GET /v1/opportunities).
lines array of SubscriptionLineInput yes What is sold. (min items 1; max items 50)

SubscriptionInvoiceOut

Field Type Required Description
id string (uuid) yes Row id.
period_start string (date) yes The billing period's first day.
period_end string (date) yes Its last day.
kind "credit" | "period" yes period: the invoice of a billing period; credit: the credit note an amendment made for the unused part of a billed period.
amendment_id string (uuid) or null credit: the amendment that made it.
status "drafted" | "failed" | "pending_approval" | "posted" yes What the run (or amendment) did: drafted (left for a person), posted, pending_approval or failed. It is not updated afterwards: the invoice's own status is document_status.
document_id string (uuid) or null yes The invoice or credit note; null when it failed.
document_status string or null The invoice's status now (draft, pending_approval, posted, rejected or void; a voided draft is made again by the next run); null when it failed.
error string or null yes Why a failed attempt failed.
created_at string (date-time) yes When.

SubscriptionLineInput

Field Type Required Description
product string yes Code (or id) of a recurring product of the entity (GET /v1/products). It gives the description, the revenue account, the VAT supply and rate type, the price in force on the start day when unit_price is omitted, and the standalone selling price of the term's revenue contract.
description string or null Default: the product's name. (min length 1; max length 500)
quantity decimal string How many units (seats), at most 6 decimals. (greater than 0; default "1")
unit_price decimal string or null Net price of one unit per the product's price period (month or year), at most 6 decimals; default: the product's list price in force on the start day. (min 0)
discount_percent decimal string Discount on the price, in percent, at most 4 decimals. (min 0; default "0")
revenue_account string or null Revenue account (code or id); default the product's.

SubscriptionLineOut

Field Type Required Description
id string (uuid) yes Line id.
line_no integer yes Position, from 1.
product_id string (uuid) yes The product.
product_code string yes Its code.
description string yes What is sold.
quantity string yes How many units.
unit_price string yes Net price of one unit per price_period.
price_period "month" | "year" yes month or year.
discount_percent string yes Discount in percent.
monthly_amount string yes What a month of service costs, rounded to the currency (quantity times the monthly price, less the discount).
revenue_account string yes Code of the revenue account.
valid_from string (date) yes The first day it applies.
valid_to string (date) or null yes The last day it applies; null while open.
obligation_id string (uuid) or null yes Its performance obligation in the current term's revenue contract; null before the subscription is active.

SubscriptionOut

Field Type Required Description
id string (uuid) yes Subscription id.
entity string yes Entity code.
number string yes SUB-000001, per entity.
party_id string (uuid) yes The customer's id.
party_code string yes The customer's code.
party_name string yes The customer's name.
title string yes What is subscribed.
currency string yes The currency it bills in (prices, invoices, credit notes, its revenue contracts); revenue is booked in the entity's functional currency (ADR-0050).
start_date string (date) yes The first day of the first term.
term_months integer yes The committed term in months.
current_term_start string (date) yes The current term's first day.
current_term_end string (date) yes The current term's last day.
billing_frequency "monthly" | "quarterly" | "yearly" yes How often it is invoiced.
billing_timing "advance" | "arrears" yes advance or arrears.
auto_renew boolean yes Whether the approved terms renew themselves.
renewal_notice_days integer yes Days before the term end a renewal is due.
renewal_uplift_percent string or null yes What a renewal adds to the prices.
payment_terms_days integer or null yes Days to pay; null: the customer's.
auto_submit boolean yes Whether the system submits each invoice.
term_amount string yes What the current term costs, net of VAT: the transaction price of its revenue contract (less, once the term is ended early).
ends_on string (date) or null The last day of service of a term ended early (an applied cancel amendment): nothing after it is billed; null otherwise.
changes_on string (date) or null The first day of new lines an applied change amendment approved, until they are in force (the billing run moves to them once the current lines are billed through the day before); null otherwise.
terms_from string (date) or null The first day of the lines in force: the current term's, or the day a change started them.
next_bill string (date) or null yes The first day of the next billing period to invoice; null unless active.
term_no integer The current term's number, the first being 1. (default 1)
renewal_status "approved" | "auto" | "due" | "ends" | "requested" | "upcoming" or null An active subscription's next term: auto (its terms renew it), approved (a second person approved its renewal), requested (a renewal waits for approval), due (the notice day has come and nothing renews it), upcoming (not yet due), ends (it ends on ends_on); for a subscription the billing run ended with its term (not early), due or requested: it may still be renewed, and the approval starts the next term at once; null otherwise.
renewal_due_on string (date) or null The day the renewal is due: renewal_notice_days before the term's last day.
renews_on string (date) or null The next term's first day, when it renews (auto, approved).
status "active" | "cancelled" | "draft" | "ended" | "pending_approval" yes The status.
terms_digest string or null yes SHA-256 of the approved terms.
approved_by object or null yes Who approved the terms.
approved_at string (date-time) or null yes When.
contract_id string (uuid) or null yes The current term's revenue contract.
contract_number string or null yes Its number.
opportunity_id string (uuid) or null yes The opportunity it came from.
renewal_deal_id string (uuid) or null The renewal deal of the current term, opened when its renewal fell due.
renewal_deal_number string or null Its number.
cancel_reason string or null yes Why it was cancelled.
lines array of SubscriptionLineOut yes What is sold.
created_by object yes Who created it.
created_at string (date-time) yes When.

SubscriptionRef

Field Type Required Description
id string (uuid) yes The subscription's id.
number string yes The subscription's number, such as SUB-000004.

SubscriptionRunInput

Field Type Required Description
through string (date) yes Bill the periods whose invoice date is on or before this day (inclusive). It may not lie more than a day after the server's date (UTC).
entity string or null Only this entity (code or id).
subscription string (uuid) or null Only this subscription (it must be active).

SubscriptionRunItem

Field Type Required Description
subscription_id string (uuid) yes The subscription.
number string yes Its number.
period_start string (date) yes The billing period's first day.
period_end string (date) yes Its last day.
invoice_date string (date) yes The invoice's date.
status "drafted" | "failed" | "pending_approval" | "posted" yes drafted (left for a person to submit), posted, pending_approval 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.
net string or null yes Its net amount; null when it failed.
error string or null yes Why it failed; null otherwise.

SubscriptionRunResult

Field Type Required Description
through string (date) yes The day billed through.
made integer yes Invoices made.
failed integer yes Periods that failed (tried again by the next run).
items array of SubscriptionRunItem yes What was made, in order.
more_due boolean yes True when a run stopped early (at most a year of periods per subscription) and more is due: run again.
Stabiso, the Enterprise System of Record · generated from the code and its guides · release 20b7dd5b0c4d