
openapi: 3.1.0
info:
  title: COMMITLY Public API
  version: "3.0.0"
  description: |
    Machine-readable description of this API: [openapi.yaml](https://commitly.com/developer/openapi.yaml)
    (OpenAPI 3.1). Import it into Postman, Insomnia or your AI coding tool.

    REST API for reading and writing cash-flow data in COMMITLY: categories, plans,
    bank accounts, invoices (open items), transactions and budgets.

    ## Access
    The API is included from Business Edition upwards. To get credentials, open
    COMMITLY and go to **Add-ons > COMMITLY Public API** (German UI: **Erweiterungen**)
    and connect the add-on. This generates a **Client ID** and a **Client Secret**.
    A credential pair is bound to one company; to work with several companies, create
    a credential pair per company.

    Group-level access (all companies of a group) requires the **Enterprise Edition**;
    credentials for group-level access are issued by support@commitly.com. See the
    tag *Group level (Enterprise)*.

    ## Authentication
    OAuth 2.0, client credentials grant.

    1. Request an access token from `POST https://app.commitly.com/api/auth/token/`,
       sending `grant_type=client_credentials`, `client_id` and `client_secret` in the
       request body (form-encoded, multipart or JSON). HTTP Basic authentication of
       the client is not supported.
    2. Send the token with every request: `Authorization: Bearer <access_token>`.
    3. An access token is valid for **5 minutes** (`expires_in: 300`). When it has
       expired, request a new one with the client credentials.

    | Situation | Status | Body |
    |---|---|---|
    | No `Authorization` header | 403 | `{"detail": "User must be authenticated to access this resource."}` |
    | Invalid or expired token | 401, header `WWW-Authenticate: Bearer` | `{"detail": "Invalid token."}` |

    ## Language of messages
    Error and validation messages are localized. Choose the language with the
    `Accept-Language` request header: `de`, `en` or `it`. Without the header, messages
    are in German. The 403 message for a missing token is always in English.

    ## Conventions
    - All paths end with a trailing slash (`/invoices/`, not `/invoices`). A request
      without the trailing slash is answered with a 301 redirect, and the request body
      is lost on the redirect.
    - Request and response bodies are JSON unless stated otherwise.
    - Dates are `YYYY-MM-DD`; timestamps are ISO 8601 in UTC.
    - Amounts are decimals with two decimal places. Requests accept JSON numbers or
      numeric strings; responses return JSON numbers.
    - A **leaf category** is a category without subcategories. Budgets, invoices and
      transactions can only be mapped to leaf categories.

    ## Pagination
    Paginated list endpoints accept `page` (starting at 1) and `page_size`
    (default 100, maximum 1000; larger values are reduced to 1000). The response
    contains `count`, `next` and `previous`.

    Always call `https://app.commitly.com/api`. If a `next` or `previous` link
    points to another host, read the `page` parameter from it and send the request to
    `https://app.commitly.com/api` with the same path and filters.

    `GET /categories/` and `GET /plans/` are not paginated and return a plain JSON
    array.

    ## Errors
    | Status | Body |
    |---|---|
    | 400 | Validation errors per field: `{"field": ["message", ...]}`. Errors not tied to a field are listed under `non_field_errors`. For request bodies that are arrays, the body is a list with one error object per item. |
    | 401, 403, 404, 405 | `{"detail": "..."}` |
    | 500 | Not JSON. Treat any 500 as an unexpected server error. |

    ## Rate limits
    Please avoid excessive request rates: cache
    the access token, use `page_size` and sync changes instead of full data sets.

    ## Status codes
    | Code | Meaning |
    |---|---|
    | 200 | OK |
    | 201 | Created |
    | 204 | Deleted, no content |
    | 301 | Path without trailing slash; the request body is dropped |
    | 400 | Bad request (invalid body or parameters) |
    | 401 | Invalid or expired access token |
    | 403 | No access token, or not allowed to call this endpoint |
    | 404 | Not found |
    | 500 | Unexpected server error (not JSON) |
  contact:
    email: support@commitly.com

servers:
  - url: https://app.commitly.com/api
    description: Production

security:
  - commitlyOAuth: []

tags:
  - name: Authentication
    description: Obtain an access token with the client credentials of your COMMITLY account.
  - name: Categories
    description: |
      Categories structure income and expenses. Budgets, transactions and invoices are
      mapped to categories by their `id`. Only leaf categories (categories without
      subcategories) can be used for mapping.
  - name: Plans
    description: Plans created in the company, including the forecast and scenarios.
  - name: Banks & Accounts
    description: |
      Banks and bank accounts connected to the company, both online (bank connection)
      and offline accounts, with balances.
  - name: Invoices (Open Items)
    description: |
      Invoices (open items) are considered in the accounts receivable / accounts
      payable (AR/AP) section of planning.
  - name: Transactions
    description: Bank transactions of the company's accounts.
  - name: Plan budgets
    description: Create, read, update and delete budgets in a selected plan.
  - name: Scenario budgets
    description: Create, read, update and delete budgets in a selected scenario. A scenario is a plan layer on top of the forecast; its budgets change only that scenario, not the forecast or other plans.
  - name: Forecast budgets
    description: |
      Create, read, update and delete budgets directly in the forecast. The forecast is
      the central planning view in COMMITLY; changes take effect there immediately.
  - name: Group level (Enterprise)
    description: |
      Group-level (tenant-level) access across the companies of a group. Requires the
      **Enterprise Edition**. With group credentials, `/banks/` and `/accounts/` return the
      banks and accounts of all companies in the group, and invoices of a specific
      company are addressed via `/companies/{company_id}/invoices/`. These invoice
      operations behave exactly like their company-level counterparts under
      *Invoices (Open Items)*.
      Group-level credentials are issued by support@commitly.com; they are not created
      under Add-ons in the app.

paths:
  /auth/token/:
    post:
      operationId: getAccessToken
      summary: Request an access token
      description: |
        Exchanges the Client ID and Client Secret for an access token (OAuth 2.0 client
        credentials grant). Send the credentials in the request body as
        `application/x-www-form-urlencoded`, `multipart/form-data` or
        `application/json`. HTTP Basic authentication of the client is not supported.

        The access token is valid for 5 minutes. When it has expired, request a new
        token with the client credentials.
      tags: [Authentication]
      security: []
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              $ref: "#/components/schemas/TokenRequest"
            example:
              grant_type: client_credentials
              client_id: YOUR_CLIENT_ID
              client_secret: YOUR_CLIENT_SECRET
          multipart/form-data:
            schema:
              $ref: "#/components/schemas/TokenRequest"
          application/json:
            schema:
              $ref: "#/components/schemas/TokenRequest"
            example:
              grant_type: client_credentials
              client_id: YOUR_CLIENT_ID
              client_secret: YOUR_CLIENT_SECRET
      responses:
        "200":
          description: Access token issued.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TokenResponse"
              example:
                access_token: YOUR_ACCESS_TOKEN
                token_type: bearer
                refresh_token: YOUR_REFRESH_TOKEN
                expires_in: 300
                refresh_expires_in: 1209600
                scope: all
        "400":
          description: |
            Invalid or missing client credentials. The message is localized (see
            `Accept-Language`).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TokenError"
              example:
                non_field_errors:
                  - Invalid client credentials.

  /categories/:
    get:
      operationId: listCategories
      summary: List categories
      description: |
        Returns the categories of the company as a plain JSON array. Not paginated.

        - `is_obsolete`: `false` = active, `true` = archived.
        - `is_category`: `true` = budgets and transactions can be mapped to this
          category. Use only such categories when creating budgets.
        - `is_default`: marks system categories that cannot be mapped. Do not use them
          for creating budgets.
        - `is_aggregating`: `true` for categories that aggregate other categories.
        - A category whose `id` is not the `parent` of any other category is a leaf
          category.
      tags: [Categories]
      responses:
        "200":
          description: List of categories.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/Category"
              example:
                - id: 1001
                  parent: null
                  name: Change in liquid funds
                  slug: change-in-liquid-funds
                  description: ""
                  order: 6
                  is_obsolete: false
                  is_category: false
                  is_aggregating: true
                  is_default: false
                - id: 1002
                  parent: 1001
                  name: Revenue
                  slug: revenue
                  description: ""
                  order: 1
                  is_obsolete: false
                  is_category: true
                  is_aggregating: false
                  is_default: false
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"

  /plans/:
    get:
      operationId: listPlans
      summary: List plans
      description: |
        Returns the plans of the company as a plain JSON array. Not paginated.

        The `id` is used as `plan_id` for plan budgets and as `scenario_id` for
        scenario budgets. `type` distinguishes regular plans, the forecast and
        scenarios.
      tags: [Plans]
      responses:
        "200":
          description: List of plans.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/Plan"
              example:
                - id: 2001
                  author:
                    id: "19"
                    username: jane.doe@example.com
                    full_name: Jane Doe
                  date_created: "2020-12-03T12:40:16.187671Z"
                  date_updated: "2021-09-03T18:32:19.682824Z"
                  name: Forecast
                  first_year: 2020
                  last_year: 2023
                  type: FORECAST
                  is_active: true
                  is_committed: false
                  update_status: READY
                  use_transactions: true
                  use_invoices: false
                  update_forecast: true
                  parent: null
                  comment_count: 0
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"

  /banks/:
    get:
      operationId: listBanks
      summary: List banks with their accounts
      description: |
        Returns the banks connected to the company, each with its bank accounts.
        Includes online and offline accounts.

        **Group level (Enterprise Edition):** called with group-level credentials, the response
        contains the banks of all companies in the group. The `tenant` field identifies
        the owner.
      tags: ["Banks & Accounts"]
      parameters:
        - $ref: "#/components/parameters/Page"
        - $ref: "#/components/parameters/PageSize"
      responses:
        "200":
          description: Paginated list of banks.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BankPage"
              example:
                count: 2
                next: null
                previous: null
                results:
                  - id: 3001
                    accounts:
                      - id: 4001
                        bank:
                          open_banking_connection_id: null
                          name: Offline Bank
                          bic: ""
                        transaction_count: 1988
                        currency: EUR
                        date_created: "2021-01-21T08:54:20.387933Z"
                        date_updated: "2021-03-03T16:32:25.553579Z"
                        name: Cash account
                        alias: ""
                        iban: ""
                        type_name: ""
                        balance: 12745
                        overdraft_limit: null
                        status: OFFLINE
                        last_successful_update: null
                        last_update_attempt: null
                        banking_api_account: null
                    date_created: "2021-01-21T08:54:20.380347Z"
                    date_updated: "2021-03-03T16:32:25.558301Z"
                    open_banking_connection_id: null
                    name: Offline Bank
                    bic: ""
                    tenant: 19
                  - id: 3002
                    accounts:
                      - id: 4002
                        bank:
                          open_banking_connection_id: 5001
                          name: Example Bank
                          bic: EXAMPLEXXXX
                        transaction_count: 1082
                        currency: EUR
                        date_created: "2022-05-05T13:10:13.354781Z"
                        date_updated: "2024-01-11T10:51:33.342193Z"
                        name: Checking account
                        alias: Main
                        iban: DE89370400440532013000
                        type_name: Checking
                        balance: 60360.68
                        overdraft_limit: 0
                        status: UPDATED
                        last_successful_update: "2024-01-11T10:50:47Z"
                        last_update_attempt: "2024-01-11T10:50:46Z"
                        banking_api_account: 6001
                    date_created: "2022-05-05T13:10:13.300000Z"
                    date_updated: "2022-05-05T13:10:13.300000Z"
                    open_banking_connection_id: 5001
                    name: Example Bank
                    bic: EXAMPLEXXXX
                    tenant: 19
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"

  /accounts/:
    get:
      operationId: getAccountsSummary
      summary: List bank accounts with balance summary
      description: |
        Returns all bank accounts of the company together with aggregated balances.
        Each account includes its most recent transaction.

        **Group level (Enterprise Edition):** called with group-level credentials, the response
        covers the bank accounts of all companies in the group.
      tags: ["Banks & Accounts"]
      responses:
        "200":
          description: Balance summary and accounts.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AccountsSummary"
              example:
                total: 98865.46
                available_limit: 0
                current_month_balance: 104158.74
                next_month_balance: 263990.44
                in_3_months_balance: 262083.61
                fiscal_year_ending_balance: 320992.93
                accounts:
                  - id: 4002
                    bank:
                      open_banking_connection_id: 5001
                      name: Example Bank
                      bic: EXAMPLEXXXX
                    transaction_count: 1082
                    last_transaction:
                      id: 7001
                      account:
                        id: 4002
                        name: Checking account
                        alias: Main
                      value_date: "2024-01-11T00:00:00Z"
                      amount: -104.64
                      purpose: Payment invoice 200763
                      counterpart_name: Example Supplier GmbH
                    currency: EUR
                    date_created: "2022-05-05T13:10:13.354781Z"
                    date_updated: "2024-01-11T10:51:33.342193Z"
                    name: Checking account
                    alias: Main
                    iban: DE89370400440532013000
                    type_name: Checking
                    balance: 60360.68
                    overdraft_limit: 0
                    status: UPDATED
                    last_successful_update: "2024-01-11T10:50:47Z"
                    last_update_attempt: "2024-01-11T10:50:46Z"
                    banking_api_account: 6001
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
    post:
      operationId: createOfflineAccount
      summary: Create an offline account
      description: |
        Creates an offline bank account, i.e. an account without a bank connection that
        you fill with transactions through the API (see *Create transactions*).

        - `bank.name` and `bank.bic` are required.
        - If an account with the same IBAN and currency already exists in the company,
          that account is returned instead of a new one.
        - The limits of your COMMITLY edition on the number of accounts apply; above the
          limit the API answers 403.
      tags: ["Banks & Accounts"]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [bank]
              properties:
                bank:
                  type: object
                  required: [name, bic]
                  properties:
                    name:
                      type: string
                      description: Name of the bank or of the source system.
                    bic:
                      type: string
                      description: BIC of the bank; may be an empty string for non-bank sources.
                name:
                  type: string
                  description: Account name shown in COMMITLY.
                iban:
                  type: string
                currency:
                  type: string
                  description: ISO 4217 code, e.g. EUR.
            example:
              bank:
                name: ERP cash accounts
                bic: ""
              name: Petty cash
              currency: EUR
      responses:
        "201":
          description: Account created (or the existing account with the same IBAN and currency).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Account"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"

  /accounts/{account_id}/transactions/:
    post:
      operationId: createTransactions
      summary: Create transactions in an account
      description: |
        Creates one or more transactions in the given bank account. The body is a
        single transaction object or an array of up to 1000 transactions.

        - Required: `value_date` (on or after 2000-01-01) and `amount`.
        - Optional: `purpose`, `counterpart_name`, `tags`.
        - `category` cannot be set on create; COMMITLY assigns a default category.
          Change it afterwards with `PATCH /transactions/` or
          `PATCH /transactions/{id}/`.

        Only offline accounts accept new transactions. Accounts linked to a bank
        connection and accounts with status `API` or `DISCONNECTED` are refused with 403.
      tags: [Transactions]
      parameters:
        - $ref: "#/components/parameters/AccountId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              oneOf:
                - $ref: "#/components/schemas/TransactionCreate"
                - type: array
                  maxItems: 1000
                  items:
                    $ref: "#/components/schemas/TransactionCreate"
            example:
              - counterpart_name: Example Provider GmbH
                purpose: Transaction created via API
                value_date: "2024-07-11"
                amount: -12500.01
                tags: [my-integration]
      responses:
        "201":
          description: |
            Transactions created. The response contains the created transaction, or a
            list of the created transactions if a list was sent.
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/Transaction"
                  - type: array
                    items:
                      $ref: "#/components/schemas/Transaction"
              example:
                - id: 8001
                  account:
                    id: 4003
                    bank: Offline
                    name: API account
                    alias: ""
                    currency: EUR
                  category:
                    id: 1004
                    name: Uncategorized outflows
                    description: ""
                  predicted_category: null
                  invoice: null
                  parent: null
                  value_date: "2024-07-11T00:00:00Z"
                  bank_booking_date: null
                  amount: -12500.01
                  purpose: Transaction created via API
                  counterpart_name: Example Provider GmbH
                  counterpart_iban: ""
                  is_adjusting_entry: false
                  is_payment: true
                  is_leaf_node: true
                  tags: [my-integration]
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          description: |
            No access token, or the account does not accept new transactions (linked to
            a bank connection, or status `API` or `DISCONNECTED`).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorDetail"
        "404":
          $ref: "#/components/responses/NotFound"

  /invoices/:
    get:
      operationId: listInvoices
      summary: List invoices
      description: |
        Returns the invoices (open items) of the company, together with the total
        amount and the total amount due.

        Note the response shape: `results` is an object containing the totals and the
        `invoices` array, not an array.
      tags: ["Invoices (Open Items)"]
      parameters:
        - $ref: "#/components/parameters/InvoiceReferenceFilter"
        - $ref: "#/components/parameters/InvoiceIds"
        - $ref: "#/components/parameters/InvoiceSourceIds"
        - $ref: "#/components/parameters/InvoiceTypeFilter"
        - $ref: "#/components/parameters/InvoicePaid"
        - $ref: "#/components/parameters/InvoiceOverdue"
        - $ref: "#/components/parameters/InvoiceMatched"
        - $ref: "#/components/parameters/InvoiceStatusFilter"
        - $ref: "#/components/parameters/InvoiceStatusInclude"
        - $ref: "#/components/parameters/InvoiceStatusExclude"
        - $ref: "#/components/parameters/InvoiceOrdering"
        - $ref: "#/components/parameters/Page"
        - $ref: "#/components/parameters/PageSize"
      responses:
        "200":
          description: Paginated invoice list with totals.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/InvoicePage"
              examples:
                InvoicePage:
                  $ref: "#/components/examples/InvoicePage"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
    post:
      operationId: createInvoice
      summary: Create an invoice
      description: |
        Creates an invoice (open item).

        Required: `reference` (invoice number), `name`, `amount`, `due_date`.

        If `type` is omitted, it is derived from the sign of `amount`: a positive amount
        creates a receivable (`RE`), any other amount a payable (`PA`).

        If `expected_date` is omitted, COMMITLY sets the expected payment date to the
        due date, or to the current date if the due date lies in the past.
      tags: ["Invoices (Open Items)"]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/InvoiceCreate"
            example:
              reference: AR-007
              name: Equipment
              notes: Special equipment
              type: RE
              date: "2022-04-16"
              amount: 7450
              due_date: "2022-04-28"
              paid_amount: 0
              category: 1002
      responses:
        "201":
          description: Invoice created.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Invoice"
              examples:
                InvoiceCreated:
                  $ref: "#/components/examples/InvoiceCreated"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
    patch:
      operationId: updateInvoices
      summary: Update invoices
      description: |
        Updates one or more invoices. The body must be a JSON **array**; a single
        object is rejected with 400.

        **Matching key.** The key used to find the invoices is chosen from the
        **first** item of the array, and applies to all items:
        1. `id` (internal invoice ID), if the first item contains `id`;
        2. otherwise `source_id` together with `source`, if the first item contains
           `source_id`;
        3. otherwise `reference`.

        Send the same key in every item.

        **Behaviour.**
        - Only the fields sent are changed. `name` is not required.
        - Items that match no invoice are skipped silently. The response contains only
          the invoices that were updated.
        - PATCH never creates an invoice.
        - `category` must be a leaf category of the company. `tags` and
          `expected_date` can be updated.
        - An `expected_date` in the past is set to the current date, unless the
          invoice status is `PD` (paid).
      tags: ["Invoices (Open Items)"]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: array
              items:
                $ref: "#/components/schemas/InvoiceUpdate"
            example:
              - reference: AR-007
                amount: 3000
              - reference: AR-008
                status: PD
                paid_amount: 500
      responses:
        "200":
          description: The invoices that were updated.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/Invoice"
              examples:
                InvoicesUpdated:
                  $ref: "#/components/examples/InvoicesUpdated"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
    delete:
      operationId: deleteInvoice
      summary: Delete invoices
      description: |
        > **Warning:** A DELETE without `id`, `reference` or `source_id` deletes
        > **every invoice these credentials can access** (narrowed only by list
        > filters sent in the same request) and still returns 204. Always send a
        > selector.

        Deletes the invoices selected by one of these query parameters:
        - `reference`: exact match on the invoice number. Repeat the parameter to
          delete several invoices (`?reference=AR-007&reference=AR-008`).
        - `id`: internal invoice ID.
        - `source_id` together with `source`.

        There is no path per reference; `/invoices/{id}/` uses the internal ID only.
      tags: ["Invoices (Open Items)"]
      parameters:
        - $ref: "#/components/parameters/InvoiceDeleteReference"
        - $ref: "#/components/parameters/InvoiceDeleteId"
        - $ref: "#/components/parameters/InvoiceDeleteSourceId"
        - $ref: "#/components/parameters/InvoiceDeleteSource"
      responses:
        "204":
          description: Selected invoices deleted.
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"

  /invoices/{id}/:
    parameters:
      - $ref: "#/components/parameters/InvoiceId"
    get:
      operationId: getInvoice
      summary: Get an invoice
      description: |
        Returns a single invoice by its internal ID (`id`). To look up an invoice by
        its number, use `GET /invoices/?reference=` (substring match) and check
        `reference` in the results.
      tags: ["Invoices (Open Items)"]
      responses:
        "200":
          description: The invoice.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Invoice"
              examples:
                InvoiceCreated:
                  $ref: "#/components/examples/InvoiceCreated"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"

  /transactions/:
    get:
      operationId: listTransactions
      summary: List transactions
      description: |
        Returns the transactions of all bank accounts of the company, with the sum of
        all matching amounts. Default order: newest value date first
        (`-value_date,id`).

        `predicted_unmapped_transactions` is a company-wide flag and does not depend
        on the filters of the request: it is `true` if at least one uncategorized
        transaction with a non-zero amount has a suggested category waiting for
        review.
      tags: [Transactions]
      parameters:
        - $ref: "#/components/parameters/TransactionAccount"
        - $ref: "#/components/parameters/TransactionCategory"
        - $ref: "#/components/parameters/TransactionMinDate"
        - $ref: "#/components/parameters/TransactionMaxDate"
        - $ref: "#/components/parameters/TransactionValueDateYear"
        - $ref: "#/components/parameters/TransactionValueDateMonth"
        - $ref: "#/components/parameters/TransactionFiscalYear"
        - $ref: "#/components/parameters/TransactionMinAmount"
        - $ref: "#/components/parameters/TransactionMaxAmount"
        - $ref: "#/components/parameters/TransactionType"
        - $ref: "#/components/parameters/TransactionIsNew"
        - $ref: "#/components/parameters/TransactionPredicted"
        - $ref: "#/components/parameters/TransactionMatched"
        - $ref: "#/components/parameters/TransactionIsPayment"
        - $ref: "#/components/parameters/TransactionTags"
        - $ref: "#/components/parameters/TransactionEmptyTags"
        - $ref: "#/components/parameters/TransactionIds"
        - $ref: "#/components/parameters/TransactionMinDateCreated"
        - $ref: "#/components/parameters/TransactionMaxDateCreated"
        - $ref: "#/components/parameters/TransactionMinDateUpdated"
        - $ref: "#/components/parameters/TransactionMaxDateUpdated"
        - $ref: "#/components/parameters/TransactionSearch"
        - $ref: "#/components/parameters/TransactionOrdering"
        - $ref: "#/components/parameters/Page"
        - $ref: "#/components/parameters/PageSize"
      responses:
        "200":
          description: Paginated list of transactions.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TransactionPage"
              example:
                count: 122
                sum: -123909.37
                next: https://app.commitly.com/api/transactions/?category=1003&page=2
                previous: null
                predicted_unmapped_transactions: false
                results:
                  - id: 7002
                    account:
                      id: 4002
                      name: Checking account
                      alias: Main
                    category:
                      id: 1003
                      name: Rent and leases
                      description: ""
                    predicted_category: null
                    invoice: null
                    parent: null
                    value_date: "2023-11-15T00:00:00Z"
                    bank_booking_date: "2023-11-15T00:00:00Z"
                    amount: -1350
                    purpose: Office rent November
                    counterpart_name: Example Property GmbH
                    counterpart_iban: ""
                    is_adjusting_entry: false
                    is_payment: true
                    is_leaf_node: true
                    tags: null
                    comment_count: 0
                  - id: 7003
                    account:
                      id: 4002
                      name: Checking account
                      alias: Main
                    category:
                      id: 1004
                      name: Uncategorized outflows
                      description: ""
                    predicted_category:
                      id: 1003
                      name: Rent and leases
                      slug: ""
                      description: ""
                      type: EX
                      order: 11
                      date_created: "2020-10-30T10:04:03.952090Z"
                      is_default: false
                      is_obsolete: false
                      lft: 104
                      rght: 105
                      tree_id: 156
                      level: 5
                      tenant: 19
                      company: 1423
                      parent: 1005
                      source: 129
                    invoice: null
                    parent: null
                    value_date: "2023-12-26T00:00:00Z"
                    bank_booking_date: "2023-12-26T00:00:00Z"
                    amount: -99.99
                    purpose: Invoice 5/12346
                    counterpart_name: Example Hosting AG
                    counterpart_iban: ""
                    is_adjusting_entry: false
                    is_payment: true
                    is_leaf_node: true
                    tags: null
                    comment_count: 0
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
    patch:
      operationId: updateTransactionCategories
      summary: Change the category of transactions
      description: |
        Changes the category of up to 1000 transactions in one call. The body is a JSON
        array of `{"id": <transaction id>, "category": <category id>}` objects.

        - `category` must be a leaf category of the same company.
        - Items with an unknown transaction `id` are skipped silently.
        - The response contains the updated transactions.
      tags: [Transactions]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: array
              maxItems: 1000
              items:
                $ref: "#/components/schemas/TransactionCategoryUpdateItem"
            example:
              - id: 7003
                category: 1003
      responses:
        "200":
          description: The updated transactions.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/Transaction"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"

  /transactions/{id}/:
    parameters:
      - $ref: "#/components/parameters/TransactionId"
    patch:
      operationId: updateTransactionCategory
      summary: Change the category of a transaction
      description: |
        Changes the category of a single transaction. `category` must be a leaf
        category of the same company.
      tags: [Transactions]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TransactionCategoryUpdate"
            example:
              category: 1003
      responses:
        "200":
          description: The updated transaction.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Transaction"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"

  /plans/{plan_id}/budgets/:
    parameters:
      - $ref: "#/components/parameters/PlanId"
    get:
      operationId: listPlanBudgets
      summary: List budgets of a plan
      description: |
        Returns the budgets of the given plan.

        Note the response shape: `results` is an object containing the total and the
        `budgets` array, not an array.
      tags: [Plan budgets]
      parameters:
        - $ref: "#/components/parameters/BudgetCategory"
        - $ref: "#/components/parameters/BudgetMinDate"
        - $ref: "#/components/parameters/BudgetMaxDate"
        - $ref: "#/components/parameters/BudgetMinValue"
        - $ref: "#/components/parameters/BudgetMaxValue"
        - $ref: "#/components/parameters/BudgetSource"
        - $ref: "#/components/parameters/BudgetType"
        - $ref: "#/components/parameters/BudgetRecurrent"
        - $ref: "#/components/parameters/BudgetOpen"
        - $ref: "#/components/parameters/BudgetContracts"
        - $ref: "#/components/parameters/BudgetEmptyTags"
        - $ref: "#/components/parameters/BudgetIds"
        - $ref: "#/components/parameters/BudgetTags"
        - $ref: "#/components/parameters/BudgetSearch"
        - $ref: "#/components/parameters/BudgetOrdering"
        - $ref: "#/components/parameters/Page"
        - $ref: "#/components/parameters/PageSize"
      responses:
        "200":
          description: Paginated list of budgets with total.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BudgetPage"
              examples:
                BudgetPage:
                  $ref: "#/components/examples/BudgetPage"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
    post:
      operationId: createPlanBudget
      summary: Create a budget in a plan
      description: |
        Creates a budget in the given plan.

        - Required: `name`, `category` (ID of a leaf category), `settlement_date`.
        - Set `external_id` and `external_source` to address the budget later by your
          own ID. `external_id` must be unique within the plan; a duplicate returns 400
          `{"external_id": ["This field must be unique."]}`. Create never updates an
          existing budget.
        - Recurring budgets can be created by setting `frequency`,
          `frequency_multiplier` and, for series, `sequence_type`,
          `sequence_term_size`, `d` and `r`.
      tags: [Plan budgets]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/BudgetCreate"
            example:
              category: 1002
              name: Consulting project
              description: ""
              amount: 125
              settlement_date: "2023-01-15"
              external_id: "123456"
              external_source: my-integration
      responses:
        "201":
          description: Budget created.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Budget"
              examples:
                Budget:
                  $ref: "#/components/examples/Budget"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"

  /plans/{plan_id}/budgets/{id}/:
    parameters:
      - $ref: "#/components/parameters/PlanId"
      - $ref: "#/components/parameters/BudgetId"
    put:
      operationId: replacePlanBudgetById
      summary: Replace a plan budget by internal ID
      description: |
        Replaces the budget with the given internal `id` in the given plan. Send the
        complete budget; `name`, `category` and `settlement_date` are required. Fields
        not sent are reset to their defaults. Prefer PATCH to change single fields.

        **Note:** Budgets of a committed plan cannot be updated or deleted (403). Other
        checks that the COMMITLY app applies may not apply to the API. Only change
        budgets that your integration created, for example by filtering on
        `external_source` (list parameter `source`).
      tags: [Plan budgets]
      requestBody:
        $ref: "#/components/requestBodies/BudgetReplace"
      responses:
        "200":
          $ref: "#/components/responses/BudgetReplaced"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
    patch:
      operationId: updatePlanBudgetById
      summary: Update a plan budget by internal ID
      description: |
        Updates the budget with the given internal `id` in the given plan. Send only
        the fields to change; see *BudgetUpdate* for how empty strings are handled. The
        response contains `id` and the fields that were sent.

        **Note:** Budgets of a committed plan cannot be updated or deleted (403). Other
        checks that the COMMITLY app applies may not apply to the API. Only change
        budgets that your integration created, for example by filtering on
        `external_source` (list parameter `source`).
      tags: [Plan budgets]
      requestBody:
        $ref: "#/components/requestBodies/BudgetPatch"
      responses:
        "200":
          $ref: "#/components/responses/BudgetPatched"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"

    delete:
      operationId: deletePlanBudgetById
      summary: Delete a plan budget by internal ID
      description: |
        Deletes the budget with the given internal `id`.

        **Note:** Budgets of a committed plan cannot be updated or deleted (403). Other
        checks that the COMMITLY app applies may not apply to the API. Only delete
        budgets that your integration created.
      tags: [Plan budgets]
      responses:
        "204":
          description: Budget deleted.
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"

  /plans/{plan_id}/budgets/external/{external_id}/:
    parameters:
      - $ref: "#/components/parameters/PlanId"
      - $ref: "#/components/parameters/ExternalId"
    get:
      operationId: getPlanBudget
      summary: Get a plan budget by external ID
      description: Returns the budget with the given `external_id` in the given plan.
      tags: [Plan budgets]
      responses:
        "200":
          description: The budget.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Budget"
              examples:
                Budget:
                  $ref: "#/components/examples/Budget"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
    put:
      operationId: replacePlanBudget
      summary: Replace a plan budget by external ID
      description: |
        Replaces the budget with the given `external_id` in the given plan. Send the
        complete budget; `name`, `category` and `settlement_date` are required. Fields
        not sent are reset to their defaults. Prefer PATCH to change single fields.

        `external_id` in the body is not required. If it differs from the path, the
        budget's `external_id` is changed to the new value.

        **Note:** Budgets of a committed plan cannot be updated or deleted (403). Other
        checks that the COMMITLY app applies may not apply to the API. Only change
        budgets that your integration created, for example by filtering on
        `external_source` (list parameter `source`).
      tags: [Plan budgets]
      requestBody:
        $ref: "#/components/requestBodies/BudgetReplace"
      responses:
        "200":
          $ref: "#/components/responses/BudgetReplaced"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
    patch:
      operationId: updatePlanBudget
      summary: Update a plan budget by external ID
      description: |
        Updates the budget with the given `external_id` in the given plan. Send only
        the fields to change; see *BudgetUpdate* for how empty strings are handled. The
        response contains `id` and the fields that were sent.

        `external_id` in the body is not required. If it differs from the path, the
        budget's `external_id` is changed to the new value.

        **Note:** Budgets of a committed plan cannot be updated or deleted (403). Other
        checks that the COMMITLY app applies may not apply to the API. Only change
        budgets that your integration created, for example by filtering on
        `external_source` (list parameter `source`).
      tags: [Plan budgets]
      requestBody:
        $ref: "#/components/requestBodies/BudgetPatch"
      responses:
        "200":
          $ref: "#/components/responses/BudgetPatched"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
    delete:
      operationId: deletePlanBudget
      summary: Delete a plan budget by external ID
      description: |
        Deletes the budget with the given `external_id` from the given plan.

        **Note:** Budgets of a committed plan cannot be updated or deleted (403). Other
        checks that the COMMITLY app applies may not apply to the API. Only delete
        budgets that your integration created, for example by filtering on
        `external_source` (list parameter `source`).
      tags: [Plan budgets]
      responses:
        "204":
          description: Budget deleted.
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"

  /scenarios/{scenario_id}/budgets/:
    parameters:
      - $ref: "#/components/parameters/ScenarioId"
    get:
      operationId: listScenarioBudgets
      summary: List budgets of a scenario
      description: |
        Returns the budgets of the given scenario. The list contains only the
        scenario's own budgets, not the forecast budgets it is based on.

        Note the response shape: `results` is an object containing the total and the
        `budgets` array, not an array.
      tags: [Scenario budgets]
      parameters:
        - $ref: "#/components/parameters/BudgetCategory"
        - $ref: "#/components/parameters/BudgetMinDate"
        - $ref: "#/components/parameters/BudgetMaxDate"
        - $ref: "#/components/parameters/BudgetMinValue"
        - $ref: "#/components/parameters/BudgetMaxValue"
        - $ref: "#/components/parameters/BudgetSource"
        - $ref: "#/components/parameters/BudgetType"
        - $ref: "#/components/parameters/BudgetRecurrent"
        - $ref: "#/components/parameters/BudgetOpen"
        - $ref: "#/components/parameters/BudgetContracts"
        - $ref: "#/components/parameters/BudgetEmptyTags"
        - $ref: "#/components/parameters/BudgetIds"
        - $ref: "#/components/parameters/BudgetTags"
        - $ref: "#/components/parameters/BudgetSearch"
        - $ref: "#/components/parameters/BudgetOrdering"
        - $ref: "#/components/parameters/Page"
        - $ref: "#/components/parameters/PageSize"
      responses:
        "200":
          description: Paginated list of budgets with total.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BudgetPage"
              examples:
                BudgetPage:
                  $ref: "#/components/examples/BudgetPage"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
    post:
      operationId: createScenarioBudget
      summary: Create a budget in a scenario
      description: |
        Creates a budget in the given scenario. The scenario is taken from the path;
        do not send `scenario_id` in the body.

        - Required: `name`, `category` (ID of a leaf category), `settlement_date`.
        - `external_id` must be unique within the scenario; a duplicate returns 400
          `{"external_id": ["This field must be unique."]}`. The forecast and each
          scenario count separately, so the same `external_id` may exist in the
          forecast and in a scenario. Create never updates an existing budget.
      tags: [Scenario budgets]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/BudgetCreate"
            example:
              name: Budget in scenario
              description: Budget created in a scenario
              category: 1002
              amount: 12500
              settlement_date: "2024-12-24"
              external_id: new123456
              external_source: my-integration
      responses:
        "201":
          description: Budget created.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Budget"
              examples:
                Budget:
                  $ref: "#/components/examples/Budget"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"

  /scenarios/{scenario_id}/budgets/{id}/:
    parameters:
      - $ref: "#/components/parameters/ScenarioId"
      - $ref: "#/components/parameters/BudgetId"
    put:
      operationId: replaceScenarioBudgetById
      summary: Replace a scenario budget by internal ID
      description: |
        Replaces the budget with the given internal `id` in the given scenario. Send
        the complete budget; `name`, `category` and `settlement_date` are required.
        Fields not sent are reset to their defaults. Prefer PATCH to change single
        fields.

        **Note:** Budgets of a committed plan cannot be updated or deleted (403). Other
        checks that the COMMITLY app applies may not apply to the API. Only change
        budgets that your integration created, for example by filtering on
        `external_source` (list parameter `source`).
      tags: [Scenario budgets]
      requestBody:
        $ref: "#/components/requestBodies/BudgetReplace"
      responses:
        "200":
          $ref: "#/components/responses/BudgetReplaced"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
    patch:
      operationId: updateScenarioBudgetById
      summary: Update a scenario budget by internal ID
      description: |
        Updates the budget with the given internal `id` in the given scenario. Send
        only the fields to change; see *BudgetUpdate* for how empty strings are
        handled. The response contains `id` and the fields that were sent.

        **Note:** Budgets of a committed plan cannot be updated or deleted (403). Other
        checks that the COMMITLY app applies may not apply to the API. Only change
        budgets that your integration created, for example by filtering on
        `external_source` (list parameter `source`).
      tags: [Scenario budgets]
      requestBody:
        $ref: "#/components/requestBodies/BudgetPatch"
      responses:
        "200":
          $ref: "#/components/responses/BudgetPatched"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"

    delete:
      operationId: deleteScenarioBudgetById
      summary: Delete a scenario budget by internal ID
      description: |
        Deletes the budget with the given internal `id`.

        **Note:** Budgets of a committed plan cannot be updated or deleted (403). Other
        checks that the COMMITLY app applies may not apply to the API. Only delete
        budgets that your integration created.
      tags: [Scenario budgets]
      responses:
        "204":
          description: Budget deleted.
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"

  /scenarios/{scenario_id}/budgets/external/{external_id}/:
    parameters:
      - $ref: "#/components/parameters/ScenarioId"
      - $ref: "#/components/parameters/ExternalId"
    get:
      operationId: getScenarioBudget
      summary: Get a scenario budget by external ID
      description: Returns the budget with the given `external_id` in the given scenario.
      tags: [Scenario budgets]
      responses:
        "200":
          description: The budget.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Budget"
              examples:
                Budget:
                  $ref: "#/components/examples/Budget"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
    put:
      operationId: replaceScenarioBudget
      summary: Replace a scenario budget by external ID
      description: |
        Replaces the budget with the given `external_id` in the given scenario. Send
        the complete budget; `name`, `category` and `settlement_date` are required.
        Fields not sent are reset to their defaults. Prefer PATCH to change single
        fields.

        `external_id` in the body is not required. If it differs from the path, the
        budget's `external_id` is changed to the new value. Do not send `scenario_id`
        in the body; the scenario is taken from the path.

        **Note:** Budgets of a committed plan cannot be updated or deleted (403). Other
        checks that the COMMITLY app applies may not apply to the API. Only change
        budgets that your integration created, for example by filtering on
        `external_source` (list parameter `source`).
      tags: [Scenario budgets]
      requestBody:
        $ref: "#/components/requestBodies/BudgetReplace"
      responses:
        "200":
          $ref: "#/components/responses/BudgetReplaced"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
    patch:
      operationId: updateScenarioBudget
      summary: Update a scenario budget by external ID
      description: |
        Updates the budget with the given `external_id` in the given scenario. Send
        only the fields to change; see *BudgetUpdate* for how empty strings are
        handled. The response contains `id` and the fields that were sent.

        `external_id` in the body is not required. If it differs from the path, the
        budget's `external_id` is changed to the new value. Do not send `scenario_id`
        in the body; the scenario is taken from the path.

        **Note:** Budgets of a committed plan cannot be updated or deleted (403). Other
        checks that the COMMITLY app applies may not apply to the API. Only change
        budgets that your integration created, for example by filtering on
        `external_source` (list parameter `source`).
      tags: [Scenario budgets]
      requestBody:
        $ref: "#/components/requestBodies/BudgetPatch"
      responses:
        "200":
          $ref: "#/components/responses/BudgetPatched"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
    delete:
      operationId: deleteScenarioBudget
      summary: Delete a scenario budget by external ID
      description: |
        Deletes the budget with the given `external_id` from the given scenario.

        **Note:** Budgets of a committed plan cannot be updated or deleted (403). Other
        checks that the COMMITLY app applies may not apply to the API. Only delete
        budgets that your integration created, for example by filtering on
        `external_source` (list parameter `source`).
      tags: [Scenario budgets]
      responses:
        "204":
          description: Budget deleted.
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"

  /forecast/budgets/:
    get:
      operationId: listForecastBudgets
      summary: List forecast budgets
      description: |
        Returns the budgets in the forecast. With `all_budgets=true`, the list contains
        the budgets of all plans of the company.

        Note the response shape: `results` is an object containing the total and the
        `budgets` array, not an array.
      tags: [Forecast budgets]
      parameters:
        - $ref: "#/components/parameters/BudgetCategory"
        - $ref: "#/components/parameters/BudgetMinDate"
        - $ref: "#/components/parameters/BudgetMaxDate"
        - $ref: "#/components/parameters/BudgetMinValue"
        - $ref: "#/components/parameters/BudgetMaxValue"
        - $ref: "#/components/parameters/BudgetSource"
        - $ref: "#/components/parameters/BudgetType"
        - $ref: "#/components/parameters/BudgetRecurrent"
        - $ref: "#/components/parameters/BudgetOpen"
        - $ref: "#/components/parameters/BudgetContracts"
        - $ref: "#/components/parameters/BudgetEmptyTags"
        - $ref: "#/components/parameters/BudgetIds"
        - $ref: "#/components/parameters/BudgetTags"
        - $ref: "#/components/parameters/BudgetSearch"
        - $ref: "#/components/parameters/ForecastBudgetOrdering"
        - $ref: "#/components/parameters/ForecastAllBudgets"
        - $ref: "#/components/parameters/Page"
        - $ref: "#/components/parameters/PageSize"
      responses:
        "200":
          description: Paginated list of budgets with total.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BudgetPage"
              examples:
                ForecastBudgetPage:
                  $ref: "#/components/examples/ForecastBudgetPage"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
    post:
      operationId: createForecastBudget
      summary: Create a forecast budget
      description: |
        Creates a budget directly in the forecast. The forecast is the central planning
        view in COMMITLY; use with care.

        - Required: `name`, `category` (ID of a leaf category), `settlement_date`.
        - `external_id` must be unique within the forecast; a duplicate returns 400
          `{"external_id": ["This field must be unique."]}`. Create never updates an
          existing budget.
      tags: [Forecast budgets]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/BudgetCreate"
            example:
              category: 1006
              settlement_date: "2024-04-08"
              tags: [my-integration, direct-API]
              name: Tax prepayment
              description: Created via API
              amount: -78700
              external_id: ext-123457
              external_source: my-integration
      responses:
        "201":
          description: Budget created.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Budget"
              examples:
                ForecastBudget:
                  $ref: "#/components/examples/ForecastBudget"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"

  /forecast/budgets/{id}/:
    parameters:
      - $ref: "#/components/parameters/BudgetId"
    put:
      operationId: replaceForecastBudgetById
      summary: Replace a forecast budget by internal ID
      description: |
        Replaces the forecast budget with the given internal `id`. Send the complete
        budget; `name`, `category` and `settlement_date` are required. Fields not sent
        are reset to their defaults. Prefer PATCH to change single fields.

        **Note:** Budgets of a committed plan cannot be updated or deleted (403). Other
        checks that the COMMITLY app applies may not apply to the API. Only change
        budgets that your integration created, for example by filtering on
        `external_source` (list parameter `source`).
      tags: [Forecast budgets]
      requestBody:
        $ref: "#/components/requestBodies/BudgetReplace"
      responses:
        "200":
          $ref: "#/components/responses/BudgetReplaced"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
    patch:
      operationId: updateForecastBudgetById
      summary: Update a forecast budget by internal ID
      description: |
        Updates the forecast budget with the given internal `id`. Send only the fields
        to change; see *BudgetUpdate* for how empty strings are handled. The response
        contains `id` and the fields that were sent.

        **Note:** Budgets of a committed plan cannot be updated or deleted (403). Other
        checks that the COMMITLY app applies may not apply to the API. Only change
        budgets that your integration created, for example by filtering on
        `external_source` (list parameter `source`).
      tags: [Forecast budgets]
      requestBody:
        $ref: "#/components/requestBodies/BudgetPatch"
      responses:
        "200":
          $ref: "#/components/responses/BudgetPatched"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"

    delete:
      operationId: deleteForecastBudgetById
      summary: Delete a forecast budget by internal ID
      description: |
        Deletes the budget with the given internal `id`.

        **Note:** Budgets of a committed plan cannot be updated or deleted (403). Other
        checks that the COMMITLY app applies may not apply to the API. Only delete
        budgets that your integration created.
      tags: [Forecast budgets]
      responses:
        "204":
          description: Budget deleted.
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"

  /forecast/budgets/external/{external_id}/:
    parameters:
      - $ref: "#/components/parameters/ExternalId"
    get:
      operationId: getForecastBudget
      summary: Get a forecast budget by external ID
      description: Returns the forecast budget with the given `external_id`.
      tags: [Forecast budgets]
      responses:
        "200":
          description: The budget.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Budget"
              examples:
                ForecastBudget:
                  $ref: "#/components/examples/ForecastBudget"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
    put:
      operationId: replaceForecastBudget
      summary: Replace a forecast budget by external ID
      description: |
        Replaces the forecast budget with the given `external_id`. Send the complete
        budget; `name`, `category` and `settlement_date` are required. Fields not sent
        are reset to their defaults. Prefer PATCH to change single fields.

        `external_id` in the body is not required. If it differs from the path, the
        budget's `external_id` is changed to the new value.

        **Note:** Budgets of a committed plan cannot be updated or deleted (403). Other
        checks that the COMMITLY app applies may not apply to the API. Only change
        budgets that your integration created, for example by filtering on
        `external_source` (list parameter `source`).
      tags: [Forecast budgets]
      requestBody:
        $ref: "#/components/requestBodies/BudgetReplace"
      responses:
        "200":
          $ref: "#/components/responses/BudgetReplaced"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
    patch:
      operationId: updateForecastBudget
      summary: Update a forecast budget by external ID
      description: |
        Updates the forecast budget with the given `external_id`. Send only the fields
        to change; see *BudgetUpdate* for how empty strings are handled. The response
        contains `id` and the fields that were sent.

        `external_id` in the body is not required. If it differs from the path, the
        budget's `external_id` is changed to the new value.

        **Note:** Budgets of a committed plan cannot be updated or deleted (403). Other
        checks that the COMMITLY app applies may not apply to the API. Only change
        budgets that your integration created, for example by filtering on
        `external_source` (list parameter `source`).
      tags: [Forecast budgets]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/BudgetUpdate"
            example:
              settlement_date: "2024-02-08"
              description: Updated via API
              amount: 78701
      responses:
        "200":
          description: |
            Budget updated. The response contains `id` and the fields that were sent.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Budget"
              example:
                id: 9003
                settlement_date: "2024-02-08"
                description: Updated via API
                amount: 78701
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
    delete:
      operationId: deleteForecastBudget
      summary: Delete a forecast budget by external ID
      description: |
        Deletes the forecast budget with the given `external_id`.

        **Note:** Budgets of a committed plan cannot be updated or deleted (403). Other
        checks that the COMMITLY app applies may not apply to the API. Only delete
        budgets that your integration created, for example by filtering on
        `external_source` (list parameter `source`).
      tags: [Forecast budgets]
      responses:
        "204":
          description: Budget deleted.
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"

  /companies/{company_id}/invoices/:
    parameters:
      - $ref: "#/components/parameters/CompanyId"
    get:
      operationId: listCompanyInvoices
      summary: List invoices of a company (group level)
      description: |
        **Group level, requires Enterprise Edition.** Returns the invoices of the given company in the
        group. Same filters and response shape as `GET /invoices/`: `results` is an
        object containing the totals and the `invoices` array.
      tags: ["Group level (Enterprise)"]
      parameters:
        - $ref: "#/components/parameters/InvoiceReferenceFilter"
        - $ref: "#/components/parameters/InvoiceIds"
        - $ref: "#/components/parameters/InvoiceSourceIds"
        - $ref: "#/components/parameters/InvoiceTypeFilter"
        - $ref: "#/components/parameters/InvoicePaid"
        - $ref: "#/components/parameters/InvoiceOverdue"
        - $ref: "#/components/parameters/InvoiceMatched"
        - $ref: "#/components/parameters/InvoiceStatusFilter"
        - $ref: "#/components/parameters/InvoiceStatusInclude"
        - $ref: "#/components/parameters/InvoiceStatusExclude"
        - $ref: "#/components/parameters/InvoiceOrdering"
        - $ref: "#/components/parameters/Page"
        - $ref: "#/components/parameters/PageSize"
      responses:
        "200":
          description: Paginated invoice list with totals.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/InvoicePage"
              examples:
                InvoicePage:
                  $ref: "#/components/examples/InvoicePage"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
    post:
      operationId: createCompanyInvoice
      summary: Create an invoice for a company (group level)
      description: |
        **Group level, requires Enterprise Edition.** Creates an invoice for the given company.
        Same body and behaviour as `POST /invoices/`.

        Required: `reference`, `name`, `amount`, `due_date`. If `type` is omitted, it is
        derived from the sign of `amount` (positive: `RE`, otherwise `PA`).
      tags: ["Group level (Enterprise)"]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/InvoiceCreate"
            example:
              reference: AR-007
              name: Equipment
              notes: Special equipment
              type: RE
              date: "2022-04-16"
              amount: 7450
              due_date: "2022-04-28"
              paid_amount: 0
      responses:
        "201":
          description: Invoice created.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Invoice"
              examples:
                InvoiceCreated:
                  $ref: "#/components/examples/InvoiceCreated"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
    patch:
      operationId: updateCompanyInvoices
      summary: Update invoices of a company (group level)
      description: |
        **Group level, requires Enterprise Edition.** Updates one or more invoices of
        the given company. Same rules as `PATCH /invoices/`.

        The body must be a JSON **array**; a single object is rejected with 400.

        **Matching key.** The key used to find the invoices is chosen from the
        **first** item of the array, and applies to all items:
        1. `id` (internal invoice ID), if the first item contains `id`;
        2. otherwise `source_id` together with `source`, if the first item contains
           `source_id`;
        3. otherwise `reference`.

        Send the same key in every item.

        **Behaviour.**
        - Only the fields sent are changed. `name` is not required.
        - Items that match no invoice are skipped silently. The response contains only
          the invoices that were updated.
        - PATCH never creates an invoice.
        - `category` must be a leaf category of the company. `tags` and
          `expected_date` can be updated.
        - An `expected_date` in the past is set to the current date, unless the
          invoice status is `PD` (paid).
      tags: ["Group level (Enterprise)"]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: array
              items:
                $ref: "#/components/schemas/InvoiceUpdate"
            example:
              - reference: AR-007
                amount: 3000
      responses:
        "200":
          description: The invoices that were updated.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/Invoice"
              examples:
                InvoicesUpdated:
                  $ref: "#/components/examples/InvoicesUpdated"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
    delete:
      operationId: deleteCompanyInvoice
      summary: Delete invoices of a company (group level)
      description: |
        > **Warning:** A DELETE without `id`, `reference` or `source_id` deletes
        > **every invoice of the company these credentials can access** (narrowed only
        > by list filters sent in the same request) and still returns 204. Always send
        > a selector.

        **Group level, requires Enterprise Edition.** Deletes the selected invoices of
        the given company. Same selectors as `DELETE /invoices/`:
        - `reference`: exact match on the invoice number. Repeat the parameter to
          delete several invoices (`?reference=AR-007&reference=AR-008`).
        - `id`: internal invoice ID.
        - `source_id` together with `source`.
      tags: ["Group level (Enterprise)"]
      parameters:
        - $ref: "#/components/parameters/InvoiceDeleteReference"
        - $ref: "#/components/parameters/InvoiceDeleteId"
        - $ref: "#/components/parameters/InvoiceDeleteSourceId"
        - $ref: "#/components/parameters/InvoiceDeleteSource"
      responses:
        "204":
          description: Selected invoices deleted.
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"

  /companies/{company_id}/invoices/{id}/:
    parameters:
      - $ref: "#/components/parameters/CompanyId"
      - $ref: "#/components/parameters/InvoiceId"
    get:
      operationId: getCompanyInvoice
      summary: Get an invoice of a company (group level)
      description: |
        **Group level, requires Enterprise Edition.** Returns a single invoice of the
        given company by its internal ID (`id`).
      tags: ["Group level (Enterprise)"]
      responses:
        "200":
          description: The invoice.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Invoice"
              examples:
                InvoiceCreated:
                  $ref: "#/components/examples/InvoiceCreated"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"

components:
  securitySchemes:
    commitlyOAuth:
      type: oauth2
      description: |
        OAuth 2.0 client credentials. Send `client_id` and `client_secret` in the body of
        the token request (form-encoded, multipart or JSON; HTTP Basic is not
        supported). Use the returned token as `Authorization: Bearer <access_token>`.
        Access tokens are valid for 5 minutes.
      flows:
        clientCredentials:
          tokenUrl: https://app.commitly.com/api/auth/token/
          scopes: {}
          # Tells the API explorer (Scalar) to send client_id/client_secret in the
          # request body; its default is HTTP Basic, which the API rejects.
          x-scalar-credentials-location: body

  parameters:
    Page:
      name: page
      in: query
      required: false
      description: |
        Page number, starting at 1. When paging through results, read this value
        from the `next` link and send it to the documented base URL.
      schema:
        type: integer
        minimum: 1
    PageSize:
      name: page_size
      in: query
      required: false
      description: Number of records per page. Default 100, maximum 1000; larger values are reduced to 1000.
      schema:
        type: integer
        minimum: 1
        maximum: 1000
        default: 100
    AccountId:
      name: account_id
      in: path
      required: true
      description: Bank account ID (`id` from `GET /accounts/`).
      schema:
        type: integer
      example: 4003
    PlanId:
      name: plan_id
      in: path
      required: true
      description: Plan ID (`id` from `GET /plans/`).
      schema:
        type: integer
      example: 2001
    ScenarioId:
      name: scenario_id
      in: path
      required: true
      description: Scenario ID (`id` of a plan with `type` `SCENARIO` from `GET /plans/`).
      schema:
        type: integer
      example: 12459
    BudgetId:
      name: id
      in: path
      required: true
      description: Internal budget ID (`id` in budget responses).
      schema:
        type: integer
      example: 9001
    ExternalId:
      name: external_id
      in: path
      required: true
      description: |
        The `external_id` set when the budget was created. Budgets whose
        `external_id` contains `.` or `/` cannot be addressed by this path. The
        trailing slash after the `external_id` is required.
      schema:
        type: string
      example: ext-123457
    CompanyId:
      name: company_id
      in: path
      required: true
      description: ID of a company in the group.
      schema:
        type: integer
      example: 117
    InvoiceId:
      name: id
      in: path
      required: true
      description: Internal invoice ID (`id` in invoice responses), not the invoice number.
      schema:
        type: integer
      example: 3102
    TransactionId:
      name: id
      in: path
      required: true
      description: Transaction ID (`id` in transaction responses).
      schema:
        type: integer
      example: 7003

    InvoiceReferenceFilter:
      name: reference
      in: query
      required: false
      description: |
        Substring match on the invoice number (case-insensitive): `RE-1` also matches
        `RE-10`. For exact selection use `ids` or `source_ids`.
      schema:
        type: string
    InvoiceIds:
      name: ids
      in: query
      required: false
      description: Comma-separated internal invoice IDs (exact match).
      schema:
        type: string
    InvoiceSourceIds:
      name: source_ids
      in: query
      required: false
      description: Comma-separated `source_id` values (exact match).
      schema:
        type: string
    InvoiceTypeFilter:
      name: type
      in: query
      required: false
      description: "`receivable` (type `RE`) or `payable` (type `PA`)."
      schema:
        type: string
        enum: [receivable, payable]
    InvoicePaid:
      name: paid
      in: query
      required: false
      description: "`true` returns paid invoices only, `false` unpaid invoices only."
      schema:
        type: boolean
    InvoiceOverdue:
      name: overdue
      in: query
      required: false
      description: Filter on overdue invoices.
      schema:
        type: boolean
    InvoiceMatched:
      name: matched
      in: query
      required: false
      description: Filter on invoices matched to bank transactions.
      schema:
        type: boolean
    InvoiceStatusFilter:
      name: status
      in: query
      required: false
      description: Invoice status code (see `status` in *Invoice*).
      schema:
        $ref: "#/components/schemas/InvoiceStatus"
    InvoiceStatusInclude:
      name: status_include
      in: query
      required: false
      description: Status codes to include (see `status` in *Invoice*).
      schema:
        type: string
    InvoiceStatusExclude:
      name: status_exclude
      in: query
      required: false
      description: Status codes to exclude (see `status` in *Invoice*).
      schema:
        type: string
    InvoiceOrdering:
      name: ordering
      in: query
      required: false
      description: |
        Sort order. Allowed fields: `amount`, `date`, `due_date`, `status`,
        `status_predefined`, `expected_date`, `name`, `reference`. Prefix a field with
        `-` for descending order; separate several fields with commas. Default:
        `status_predefined,-expected_date`.
      schema:
        type: string
    InvoiceDeleteReference:
      name: reference
      in: query
      required: false
      description: |
        Invoice number, exact match. Repeat the parameter to select several invoices.
      schema:
        type: array
        items:
          type: string
      style: form
      explode: true
    InvoiceDeleteId:
      name: id
      in: query
      required: false
      description: Internal invoice ID.
      schema:
        type: integer
    InvoiceDeleteSourceId:
      name: source_id
      in: query
      required: false
      description: "`source_id` of the invoice. Send together with `source`."
      schema:
        type: string
    InvoiceDeleteSource:
      name: source
      in: query
      required: false
      description: "`source` of the invoice. Send together with `source_id`."
      schema:
        type: string

    TransactionAccount:
      name: account
      in: query
      required: false
      description: Bank account ID. Repeat the parameter for several accounts.
      schema:
        type: array
        items:
          type: integer
      style: form
      explode: true
    TransactionCategory:
      name: category
      in: query
      required: false
      description: Category ID; includes its subcategories. Repeat the parameter for several categories.
      schema:
        type: array
        items:
          type: integer
      style: form
      explode: true
    TransactionMinDate:
      name: min_date
      in: query
      required: false
      description: Earliest value date.
      schema:
        type: string
        format: date
    TransactionMaxDate:
      name: max_date
      in: query
      required: false
      description: Latest value date.
      schema:
        type: string
        format: date
    TransactionValueDateYear:
      name: value_date_year
      in: query
      required: false
      description: Year of the value date.
      schema:
        type: integer
    TransactionValueDateMonth:
      name: value_date_month
      in: query
      required: false
      description: Month of the value date (1 to 12).
      schema:
        type: integer
        minimum: 1
        maximum: 12
    TransactionFiscalYear:
      name: fiscal_year
      in: query
      required: false
      description: Fiscal year of the company.
      schema:
        type: integer
    TransactionMinAmount:
      name: min_amount
      in: query
      required: false
      description: Minimum reporting amount.
      schema:
        type: number
    TransactionMaxAmount:
      name: max_amount
      in: query
      required: false
      description: Maximum reporting amount.
      schema:
        type: number
    TransactionType:
      name: type
      in: query
      required: false
      description: "`incomes` or `expenses`."
      schema:
        type: string
        enum: [incomes, expenses]
    TransactionIsNew:
      name: is_new
      in: query
      required: false
      description: "`true`: transactions still in a default (uncategorized) category."
      schema:
        type: boolean
    TransactionPredicted:
      name: predicted
      in: query
      required: false
      description: "`true`: uncategorized transactions that have a suggested category."
      schema:
        type: boolean
    TransactionMatched:
      name: matched
      in: query
      required: false
      description: "`true`: transactions linked to an invoice."
      schema:
        type: boolean
    TransactionIsPayment:
      name: is_payment
      in: query
      required: false
      description: Filter on the transaction field `is_payment`.
      schema:
        type: boolean
    TransactionTags:
      name: tags
      in: query
      required: false
      description: Filter on tags.
      schema:
        type: string
    TransactionEmptyTags:
      name: empty_tags
      in: query
      required: false
      description: "`true`: transactions without tags."
      schema:
        type: boolean
    TransactionIds:
      name: ids
      in: query
      required: false
      description: Comma-separated transaction IDs.
      schema:
        type: string
    TransactionMinDateCreated:
      name: min_date_created
      in: query
      required: false
      description: Earliest creation time (ISO 8601).
      schema:
        type: string
    TransactionMaxDateCreated:
      name: max_date_created
      in: query
      required: false
      description: Latest creation time (ISO 8601).
      schema:
        type: string
    TransactionMinDateUpdated:
      name: min_date_updated
      in: query
      required: false
      description: Earliest time of the last update (ISO 8601).
      schema:
        type: string
    TransactionMaxDateUpdated:
      name: max_date_updated
      in: query
      required: false
      description: Latest time of the last update (ISO 8601).
      schema:
        type: string
    TransactionSearch:
      name: search
      in: query
      required: false
      description: Full-text search.
      schema:
        type: string
    TransactionOrdering:
      name: ordering
      in: query
      required: false
      description: |
        Sort order. Allowed fields: `value_date`, `counterpart_name`,
        `reporting_amount`. Prefix a field with `-` for descending order. Default:
        `-value_date,id`.
      schema:
        type: string

    BudgetCategory:
      name: category
      in: query
      required: false
      description: Category ID; includes its subcategories. Repeat the parameter for several categories.
      schema:
        type: array
        items:
          type: integer
      style: form
      explode: true
    BudgetMinDate:
      name: min_date
      in: query
      required: false
      description: Earliest `settlement_date`.
      schema:
        type: string
        format: date
    BudgetMaxDate:
      name: max_date
      in: query
      required: false
      description: Latest `settlement_date`.
      schema:
        type: string
        format: date
    BudgetMinValue:
      name: min_value
      in: query
      required: false
      description: Minimum amount.
      schema:
        type: number
    BudgetMaxValue:
      name: max_value
      in: query
      required: false
      description: Maximum amount.
      schema:
        type: number
    BudgetSource:
      name: source
      in: query
      required: false
      description: |
        Substring match on `external_source`. Several comma-separated values are
        combined with OR.
      schema:
        type: string
    BudgetType:
      name: budget_type
      in: query
      required: false
      description: Kind of budget.
      schema:
        type: string
        enum: [single, recurring, dynamic, contract]
    BudgetRecurrent:
      name: recurrent
      in: query
      required: false
      description: Boolean filter on recurring budgets.
      schema:
        type: boolean
    BudgetOpen:
      name: open
      in: query
      required: false
      description: Boolean filter on open budgets.
      schema:
        type: boolean
    BudgetContracts:
      name: contracts
      in: query
      required: false
      description: Boolean filter on contract budgets.
      schema:
        type: boolean
    BudgetEmptyTags:
      name: empty_tags
      in: query
      required: false
      description: "`true`: budgets without tags."
      schema:
        type: boolean
    BudgetIds:
      name: ids
      in: query
      required: false
      description: Comma-separated internal budget IDs.
      schema:
        type: string
    BudgetTags:
      name: tags
      in: query
      required: false
      description: Comma-separated tags.
      schema:
        type: string
    BudgetSearch:
      name: search
      in: query
      required: false
      description: Searches name, description and tags.
      schema:
        type: string
    BudgetOrdering:
      name: ordering
      in: query
      required: false
      description: |
        Sort order. Allowed fields: `start_date`, `settlement_date`, `amount`,
        `end_date`. Prefix a field with `-` for descending order.
      schema:
        type: string
    ForecastBudgetOrdering:
      name: ordering
      in: query
      required: false
      description: |
        Sort order. Allowed fields: `start_date`, `settlement_date`, `amount`,
        `end_date`, `name`. Prefix a field with `-` for descending order.
      schema:
        type: string
    ForecastAllBudgets:
      name: all_budgets
      in: query
      required: false
      description: "`true` returns the budgets of all plans of the company, not only the forecast."
      schema:
        type: boolean

  requestBodies:
    BudgetReplace:
      required: true
      description: The complete budget. `name`, `category` and `settlement_date` are required.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/BudgetCreate"
          example:
            category: 1002
            name: Consulting project
            description: ""
            amount: 150
            settlement_date: "2024-01-20"
            external_id: "123456"
            external_source: my-integration
    BudgetPatch:
      required: true
      description: Only the fields to change.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/BudgetUpdate"
          example:
            amount: 777000

  responses:
    BadRequest:
      description: |
        Invalid request body or parameters. The body lists the errors per field; errors
        not tied to a field are listed under `non_field_errors`. For request bodies
        that are arrays, the body is a list with one error object per item. Messages
        are localized (see `Accept-Language`).
      content:
        application/json:
          schema:
            oneOf:
              - $ref: "#/components/schemas/ValidationError"
              - type: array
                items:
                  $ref: "#/components/schemas/ValidationError"
          example:
            settlement_date:
              - This field is required.
    Unauthorized:
      description: |
        The access token is invalid or has expired. Request a new token. The message is
        localized (see `Accept-Language`).
      headers:
        WWW-Authenticate:
          description: Authentication scheme, `Bearer`.
          schema:
            type: string
          example: Bearer
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorDetail"
          example:
            detail: Invalid token.
    Forbidden:
      description: |
        No access token was sent, or the credentials are not allowed to call this
        endpoint. If the company's COMMITLY edition does not include the requested
        function, the body additionally contains
        `"code": "billing_plan_permission_denied"`.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorDetail"
          example:
            detail: User must be authenticated to access this resource.
    NotFound:
      description: The resource does not exist or is not accessible with these credentials.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorDetail"
    BudgetReplaced:
      description: Budget updated. The response contains the complete budget.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Budget"
          examples:
            Budget:
              $ref: "#/components/examples/Budget"
    BudgetPatched:
      description: Budget updated. The response contains `id` and the fields that were sent.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Budget"
          example:
            id: 9002
            amount: 777000

  schemas:
    TokenRequest:
      type: object
      required: [grant_type, client_id, client_secret]
      properties:
        grant_type:
          type: string
          const: client_credentials
        client_id:
          type: string
          description: Client ID from Add-ons > COMMITLY Public API.
        client_secret:
          type: string
          description: Client Secret from Add-ons > COMMITLY Public API.
    TokenResponse:
      type: object
      required: [access_token, token_type, expires_in]
      properties:
        access_token:
          type: string
          description: "Send as `Authorization: Bearer <access_token>`."
        token_type:
          type: string
          const: bearer
          description: Always `bearer` (lowercase).
        refresh_token:
          type: string
          description: Refresh token.
        expires_in:
          type: integer
          description: Lifetime of the access token in seconds (`300`, i.e. 5 minutes).
          example: 300
        refresh_expires_in:
          type: integer
          description: Lifetime of the refresh token in seconds (`1209600`, i.e. 14 days).
          example: 1209600
        scope:
          type: string
          description: Space-separated scopes of the token, or `all`.
    TokenError:
      type: object
      properties:
        non_field_errors:
          type: array
          description: Error messages. Localized (see `Accept-Language`).
          items:
            type: string
    ErrorDetail:
      type: object
      required: [detail]
      properties:
        detail:
          type: string
          description: Error message.
        code:
          type: string
          description: Present only if the company's edition does not include the function; value `billing_plan_permission_denied`.
    ValidationError:
      type: object
      description: |
        Validation errors. Each key is a field of the request body; its value is the
        list of messages for that field. Errors not tied to a field are listed under
        `non_field_errors`.
      properties:
        non_field_errors:
          type: array
          items:
            type: string
      additionalProperties:
        type: array
        items:
          type: string

    Pagination:
      type: object
      required: [count, next, previous]
      properties:
        count:
          type: integer
          description: Total number of matching records.
        next:
          type: [string, "null"]
          format: uri
          description: |
            URL of the next page, or null on the last page. The host name may differ
            from the documented base URL; read `page` from it and call the documented
            base URL.
        previous:
          type: [string, "null"]
          format: uri
          description: URL of the previous page, or null on the first page.

    Category:
      type: object
      properties:
        id:
          type: integer
        parent:
          type: [integer, "null"]
          description: ID of the parent category.
        name:
          type: string
        slug:
          type: string
        description:
          type: string
        order:
          type: integer
          description: Display position.
        is_obsolete:
          type: boolean
          description: "`false` = active, `true` = archived."
        is_category:
          type: boolean
          description: "`true` = budgets and transactions can be mapped to this category."
        is_aggregating:
          type: boolean
          description: "`true` for categories that aggregate other categories."
        is_default:
          type: boolean
          description: Marks system categories that cannot be mapped. Do not use them for creating budgets.
    CategoryRef:
      type: object
      description: Short form of a category, embedded in transactions and budgets.
      properties:
        id:
          type: integer
        name:
          type: string
        description:
          type: string
    CategoryDetail:
      type: object
      description: Extended category representation, embedded as `predicted_category` in transactions.
      properties:
        id:
          type: integer
        name:
          type: string
        slug:
          type: string
        description:
          type: string
        type:
          type: string
          enum: [AG, EX, IN]
          description: "`AG` aggregating, `EX` expenses, `IN` incomes."
        order:
          type: integer
          description: Display position.
        date_created:
          type: string
          format: date-time
        is_default:
          type: boolean
        is_obsolete:
          type: boolean
        lft:
          type: integer
          description: Internal; can be ignored.
        rght:
          type: integer
          description: Internal; can be ignored.
        tree_id:
          type: integer
          description: Internal; can be ignored.
        level:
          type: integer
          description: Internal; can be ignored.
        tenant:
          type: integer
        company:
          type: integer
        parent:
          type: [integer, "null"]
          description: ID of the parent category.
        source:
          type: [integer, "null"]

    PlanAuthor:
      type: object
      properties:
        id:
          type: string
        username:
          type: string
        full_name:
          type: string
    Plan:
      type: object
      properties:
        id:
          type: integer
        author:
          $ref: "#/components/schemas/PlanAuthor"
        date_created:
          type: string
          format: date-time
        date_updated:
          type: string
          format: date-time
        name:
          type: string
        first_year:
          type: integer
        last_year:
          type: integer
        type:
          type: string
          enum: [REGULAR, FORECAST, SCENARIO]
          description: "`REGULAR` plan, the `FORECAST`, or a `SCENARIO`."
        is_active:
          type: boolean
        is_committed:
          type: boolean
          description: Budgets of a committed plan cannot be updated or deleted.
        update_status:
          type: string
          enum: [IN_PROGRESS, READY]
          readOnly: true
          description: "`IN_PROGRESS` while the plan is being recalculated, otherwise `READY`."
        use_transactions:
          type: boolean
        use_invoices:
          type: boolean
        update_forecast:
          type: boolean
        parent:
          type: [integer, "null"]
          description: ID of the parent plan.
        comment_count:
          type: integer

    BankRef:
      type: object
      description: Bank data embedded in an account.
      properties:
        open_banking_connection_id:
          type: [integer, "null"]
        name:
          type: string
        bic:
          type: string
    AccountRef:
      type: object
      description: Short form of a bank account, embedded in transactions.
      properties:
        id:
          type: integer
        name:
          type: string
        alias:
          type: string
        bank:
          type: string
          description: Bank name. Present in the create-transactions response only.
        currency:
          type: string
          description: Present in the create-transactions response only.
    LastTransaction:
      type: object
      properties:
        id:
          type: integer
        account:
          $ref: "#/components/schemas/AccountRef"
        value_date:
          type: string
          format: date-time
        amount:
          type: number
        purpose:
          type: string
        counterpart_name:
          type: string
    Account:
      type: object
      properties:
        id:
          type: integer
        bank:
          $ref: "#/components/schemas/BankRef"
        transaction_count:
          type: integer
        last_transaction:
          $ref: "#/components/schemas/LastTransaction"
          description: Present in `GET /accounts/` only.
        currency:
          type: string
          description: ISO 4217 currency code.
        date_created:
          type: string
          format: date-time
        date_updated:
          type: string
          format: date-time
        name:
          type: string
        alias:
          type: string
        iban:
          type: string
        type_name:
          type: string
          description: Account type as supplied by the bank provider (free text, e.g. `Checking`). Empty for offline accounts.
        balance:
          type: number
        overdraft_limit:
          type: [number, "null"]
        status:
          type: string
          readOnly: true
          enum:
            - UPDATED
            - UPDATED_FIXED
            - DOWNLOAD_IN_PROGRESS
            - DOWNLOAD_FAILED
            - DEPRECATED
            - DISCONNECTED
            - API
            - OFFLINE
          description: |
            Account status. Only offline accounts accept transactions via
            `POST /accounts/{account_id}/transactions/`; accounts with status `API` or
            `DISCONNECTED`, and accounts linked to a bank connection, do not.
        last_successful_update:
          type: [string, "null"]
          format: date-time
        last_update_attempt:
          type: [string, "null"]
          format: date-time
        banking_api_account:
          type: [integer, "null"]
    Bank:
      type: object
      properties:
        id:
          type: integer
        accounts:
          type: array
          items:
            $ref: "#/components/schemas/Account"
        date_created:
          type: string
          format: date-time
        date_updated:
          type: string
          format: date-time
        open_banking_connection_id:
          type: [integer, "null"]
        name:
          type: string
        bic:
          type: string
        tenant:
          type: integer
    BankPage:
      allOf:
        - $ref: "#/components/schemas/Pagination"
        - type: object
          properties:
            results:
              type: array
              items:
                $ref: "#/components/schemas/Bank"
    AccountsSummary:
      type: object
      properties:
        total:
          type: number
          description: Sum of all account balances, in the company currency.
        available_limit:
          type: number
          description: Sum of overdraft limits as of today.
        current_month_balance:
          type: [number, "null"]
          description: Forecast balance at the end of the current month; `null` if the company has no current forecast.
        current_month_limit:
          type: [number, "null"]
          description: Limits at the end of the current month.
        next_month_balance:
          type: [number, "null"]
          description: Forecast balance at the end of next month; `null` without a current forecast.
        next_month_limit:
          type: [number, "null"]
          description: Limits at the end of next month.
        in_3_months_balance:
          type: [number, "null"]
          description: Forecast balance three months ahead; `null` without a current forecast.
        in_3_months_limit:
          type: [number, "null"]
          description: Limits three months ahead.
        fiscal_year_ending_balance:
          type: [number, "null"]
          description: Forecast balance at the end of the fiscal year; `null` without a current forecast.
        fiscal_year_ending_limit:
          type: [number, "null"]
          description: Limits at the end of the fiscal year.
        accounts:
          type: array
          items:
            $ref: "#/components/schemas/Account"

    InvoiceType:
      type: string
      enum: [RE, PA]
      description: |
        `RE` receivable, `PA` payable. Optional on create: if omitted, a positive
        `amount` sets `RE`, any other amount `PA`.
    InvoiceStatus:
      type: string
      enum: [OD, PL, OR, PD, PP, IN, HD, PR, DP, CA, IP]
      description: |
        | Code | Label |
        |---|---|
        | `OD` | Overdue |
        | `PL` | Expected |
        | `OR` | Lead/Order |
        | `PD` | Paid |
        | `PP` | Partially Paid |
        | `IN` | Installments |
        | `HD` | On Hold |
        | `PR` | Promised |
        | `DP` | In Dispute |
        | `CA` | Cancelled |
        | `IP` | In Progress |
    InvoiceAmount:
      type: [number, string]
      description: |
        Decimal with at most 10 digits and 2 decimal places. A JSON number or a
        numeric string (`"7450.00"`).
    InvoiceTransaction:
      type: object
      description: A bank transaction matched to the invoice.
      readOnly: true
      properties:
        id:
          type: integer
        account:
          description: Bank account of the transaction.
        value_date:
          type: string
        amount:
          type: number
        purpose:
          type: string
        counterpart_name:
          type: string
        reporting_amount:
          type: number
    Invoice:
      type: object
      properties:
        id:
          type: integer
          readOnly: true
          description: Internal invoice ID. Used in `/invoices/{id}/` and as `id` selector.
        transactions:
          type: array
          readOnly: true
          description: Bank transactions matched to the invoice.
          items:
            $ref: "#/components/schemas/InvoiceTransaction"
        paid_amount:
          type: number
        amount_due:
          type: number
          readOnly: true
        date_created:
          type: string
          format: date-time
          readOnly: true
        date_updated:
          type: string
          format: date-time
          readOnly: true
        name:
          type: string
        notes:
          type: string
        amount:
          type: number
          description: Decimal with at most 10 digits and 2 decimal places.
        type:
          $ref: "#/components/schemas/InvoiceType"
        reference:
          type: string
          description: Invoice number.
        date:
          type: string
          format: date
          description: Invoice date.
        due_date:
          type: string
          format: date
        expected_date:
          type: string
          format: date
          description: Expected payment date.
        source:
          type: string
          description: Origin of the invoice, e.g. `direct_api` or `Manually Added`.
        source_id:
          type: string
          description: ID of the invoice in its source.
        status:
          $ref: "#/components/schemas/InvoiceStatus"
        status_label:
          type: string
          readOnly: true
          description: Label of `status`, e.g. `Expected`.
        is_archived:
          type: boolean
        category:
          type: [integer, "null"]
          description: Category ID.
        comment_count:
          type: integer
          readOnly: true
          description: Present in list responses.
    InvoiceCreate:
      type: object
      required: [reference, name, amount, due_date]
      properties:
        reference:
          type: string
          description: Invoice number.
        name:
          type: string
        amount:
          $ref: "#/components/schemas/InvoiceAmount"
        due_date:
          type: string
          format: date
        date:
          type: string
          format: date
          description: Invoice date.
        notes:
          type: string
          description: Description of the invoice.
        type:
          $ref: "#/components/schemas/InvoiceType"
        tags:
          type: string
          description: Additional information for mapping rules, e.g. the category.
        paid_amount:
          $ref: "#/components/schemas/InvoiceAmount"
        category:
          type: integer
          description: ID of a leaf category of the company (from `GET /categories/`).
        expected_date:
          type: string
          format: date
          description: |
            Expected payment date, if different from the due date. If omitted, it is
            set to the due date, or to the current date if the due date lies in the past.
    InvoiceUpdate:
      type: object
      description: |
        One item of a PATCH body. Identify the invoice by `id`, by `source_id`
        together with `source`, or by `reference`; the key is chosen from the first
        item of the array (see the operation). All other fields are optional; only the
        fields sent are changed.
      properties:
        id:
          type: integer
          description: Internal invoice ID (matching key).
        source_id:
          type: string
          description: ID of the invoice in its source (matching key, together with `source`).
        source:
          type: string
          description: Source of the invoice (matching key, together with `source_id`).
        reference:
          type: string
          description: Invoice number (matching key, exact match).
        name:
          type: string
        notes:
          type: string
        type:
          $ref: "#/components/schemas/InvoiceType"
        status:
          $ref: "#/components/schemas/InvoiceStatus"
        date:
          type: string
          format: date
        amount:
          $ref: "#/components/schemas/InvoiceAmount"
        due_date:
          type: string
          format: date
        expected_date:
          type: string
          format: date
          description: A date in the past is set to the current date, unless the status is `PD`.
        paid_amount:
          $ref: "#/components/schemas/InvoiceAmount"
        category:
          type: integer
          description: ID of a leaf category of the company.
        tags:
          type: string
          description: Additional information for mapping rules.
    InvoicePage:
      allOf:
        - $ref: "#/components/schemas/Pagination"
        - type: object
          properties:
            sum:
              type: number
              description: Sum of the amounts of all matching invoices.
            results:
              type: object
              description: Totals and the invoices of the current page.
              properties:
                amount_due:
                  type: number
                  description: Total amount due of all matching invoices.
                total:
                  type: number
                  description: Total amount of all matching invoices.
                invoices:
                  type: array
                  items:
                    $ref: "#/components/schemas/Invoice"

    Transaction:
      type: object
      properties:
        id:
          type: integer
        account:
          $ref: "#/components/schemas/AccountRef"
        category:
          oneOf:
            - $ref: "#/components/schemas/CategoryRef"
            - type: "null"
        predicted_category:
          description: Suggested category for an uncategorized transaction, or null.
          oneOf:
            - $ref: "#/components/schemas/CategoryDetail"
            - type: "null"
        invoice:
          description: Invoice linked to the transaction, or null.
        parent:
          description: Parent transaction (same shape), or null.
          oneOf:
            - $ref: "#/components/schemas/Transaction"
            - type: "null"
        value_date:
          type: string
          format: date-time
        bank_booking_date:
          type: [string, "null"]
          format: date-time
        amount:
          type: number
          description: Negative for outflows, positive for inflows.
        purpose:
          type: string
        counterpart_name:
          type: string
        counterpart_iban:
          type: string
        is_adjusting_entry:
          type: boolean
        is_payment:
          type: boolean
        is_leaf_node:
          type: boolean
        tags:
          type: [array, "null"]
          items:
            type: string
        comment_count:
          type: integer
          description: Present in list responses.
    TransactionCreate:
      type: object
      required: [value_date, amount]
      properties:
        value_date:
          type: string
          format: date
          description: Value date, on or after 2000-01-01.
        amount:
          type: [number, string]
          description: Negative for outflows, positive for inflows. A JSON number or a numeric string.
        purpose:
          type: string
        counterpart_name:
          type: string
        tags:
          type: array
          items:
            type: string
    TransactionCategoryUpdate:
      type: object
      required: [category]
      properties:
        category:
          type: integer
          description: ID of a leaf category of the same company.
    TransactionCategoryUpdateItem:
      type: object
      required: [id, category]
      properties:
        id:
          type: integer
          description: Transaction ID. Unknown IDs are skipped.
        category:
          type: integer
          description: ID of a leaf category of the same company.
    TransactionPage:
      allOf:
        - $ref: "#/components/schemas/Pagination"
        - type: object
          properties:
            sum:
              type: number
              description: Sum of the amounts of all matching transactions.
            predicted_unmapped_transactions:
              type: boolean
              description: |
                Company-wide, independent of the request's filters: `true` if at least
                one uncategorized transaction with a non-zero amount has a suggested
                category waiting for review.
            results:
              type: array
              items:
                $ref: "#/components/schemas/Transaction"

    BudgetFields:
      type: object
      description: Writable budget fields.
      properties:
        category:
          type: integer
          description: ID of a leaf category (a category without subcategories). A numeric string is also accepted.
        name:
          type: string
        description:
          type: string
        amount:
          type: [number, string]
          description: |
            Decimal with at most 12 digits and 2 decimal places. Negative for outflows,
            positive for inflows. A JSON number or a numeric string.
        settlement_date:
          type: string
          format: date
        tags:
          type: array
          items:
            type: string
        external_id:
          type: string
          description: |
            Your own ID for the budget; used in the `/external/{external_id}/` paths.
            Unique per plan: the forecast and each scenario count as separate plans.
            Creating a budget with an `external_id` that already exists in the same
            plan returns 400. Do not use `.` or `/`. In an update, a value different
            from the current one changes the budget's `external_id`.
        external_source:
          type: string
          description: Free-text name of the system that created the budget. Filter with the list parameter `source`.
        account:
          type: [integer, "null"]
          description: Bank account ID.
        rate:
          type: [number, string]
          description: Decimal rate with 4 decimal places. Default 0.
        base:
          type: string
          enum: [FS, S, F]
          description: "`FS` forecast and scenario (default), `S` scenario, `F` forecast."
        frequency:
          type: string
          enum: [daily, weekly, monthly, quarterly, yearly, ""]
          description: Recurrence period. Empty for a one-time budget.
        frequency_multiplier:
          type: integer
          description: Repeat every N periods. Default 1.
        sequence_type:
          type: string
          enum: [AP, GP, HP, ""]
          description: Series type of a recurring budget; `AP` arithmetic, `GP` geometric, `HP` harmonic, empty for none.
        sequence_term_size:
          type: integer
          description: Term size of the series. Default 1.
        d:
          type: number
          description: Common difference of an arithmetic series. Default 0.
        r:
          type: number
          description: Common ratio of a geometric series. Default 1.
        time_lags:
          type: array
          items:
            type: integer
        depending_categories:
          type: array
          description: IDs of the categories the budget depends on.
          items:
            type: integer
        is_adjusted:
          type: boolean
        is_indefinite:
          type: boolean
          description: The recurring budget runs indefinitely.
        inter_company:
          type: [integer, "null"]
          description: Company ID for an intercompany budget.
        inter_category:
          type: [integer, "null"]
          description: Category of the linked budget in the other company. Only valid together with `inter_company`.
        integration_tags:
          type: [array, "null"]
          description: Tag list for integrations.
          items:
            type: string
    BudgetCreate:
      description: |
        Budget for create (POST) and full update (PUT). `name`, `category` and
        `settlement_date` are required; all other fields are optional or have a
        default. Recurring budgets are set with `frequency`, `frequency_multiplier`
        and, for series, `sequence_type`, `sequence_term_size`, `d` and `r`.
      allOf:
        - $ref: "#/components/schemas/BudgetFields"
        - type: object
          required: [name, category, settlement_date]
    BudgetUpdate:
      description: |
        Budget for partial update (PATCH). Send only the fields to change. Empty strings
        do not mean "unchanged":

        | Field | Effect of `""` |
        |---|---|
        | `description`, `external_source`, `frequency`, `sequence_type` | Clears the field |
        | `account`, `inter_company`, `inter_category` | Sets the field to null |
        | `name`, `category`, `external_id` | 400 |
        | Numbers (`amount`, `rate`, `d`, `r`, ...), dates, booleans, lists | 400 |

        Leave out fields you want to keep.
      allOf:
        - $ref: "#/components/schemas/BudgetFields"
    Budget:
      type: object
      description: Budget as returned by the API.
      properties:
        id:
          type: integer
          readOnly: true
          description: Internal budget ID; used in the `/budgets/{id}/` paths.
        category:
          $ref: "#/components/schemas/CategoryRef"
        inter_category:
          type: [integer, "null"]
          description: Category of the linked budget in the other company.
        inter_category_name:
          type: [string, "null"]
          readOnly: true
          description: Name of `inter_category`.
        depending_categories:
          type: array
          description: Categories the budget depends on.
          items: {}
        base_categories:
          type: array
          readOnly: true
          description: Stored dependency categories (set via `depending_categories`).
          items: {}
        recurring_budget:
          type: [object, "null"]
          readOnly: true
          description: Summary of the recurrence settings; null if the budget is not recurring.
          properties:
            frequency:
              type: string
            frequency_multiplier:
              type: integer
            sequence_type:
              type: string
            sequence_term_size:
              type: integer
            d:
              type: number
            r:
              type: number
        settlement_date:
          type: string
          format: date
        start_date:
          type: [string, "null"]
          format: date
        end_date:
          type: [string, "null"]
          format: date
        account:
          type: [integer, "null"]
          description: Bank account ID.
        tags:
          type: array
          items:
            type: string
        integration_tags:
          type: [array, "null"]
          items:
            type: string
        is_adjusted:
          type: boolean
        inter_budget:
          readOnly: true
          description: The linked budget in the other company of an intercompany budget, or null.
        name:
          type: string
        description:
          type: string
        amount:
          type: number
          description: Negative for outflows, positive for inflows.
        rate:
          type: number
        base:
          type: string
          enum: [FS, S, F]
          description: "`FS` forecast and scenario, `S` scenario, `F` forecast."
        time_lags:
          type: array
          items:
            type: integer
        frequency:
          type: string
          enum: [daily, weekly, monthly, quarterly, yearly, ""]
        frequency_multiplier:
          type: integer
        sequence_type:
          type: string
          enum: [AP, GP, HP, ""]
        sequence_term_size:
          type: integer
        d:
          type: number
        r:
          type: number
        is_indefinite:
          type: boolean
        external_id:
          type: [string, "null"]
          description: Your own ID for the budget.
        external_source:
          type: [string, "null"]
          description: Free-text name of the system that created the budget.
        inter_company:
          type: [integer, "null"]
          description: Company ID for an intercompany budget.
        source:
          type: [integer, "null"]
          readOnly: true
          description: ID of the budget this one was copied from, or null. Not related to `external_source`.
    BudgetPage:
      allOf:
        - $ref: "#/components/schemas/Pagination"
        - type: object
          properties:
            sum:
              type: number
              description: Sum of the amounts of all matching budgets.
            results:
              type: object
              description: Total and the budgets of the current page.
              properties:
                total:
                  type: number
                  description: Total amount of all matching budgets.
                budgets:
                  type: array
                  items:
                    $ref: "#/components/schemas/Budget"

  examples:
    Budget:
      summary: Plan budget
      value:
        id: 9001
        category:
          id: 1002
          name: Revenue
          description: ""
        inter_category: null
        inter_category_name: null
        depending_categories: []
        recurring_budget: null
        settlement_date: "2023-01-15"
        start_date: null
        end_date: null
        account: null
        tags: []
        integration_tags: null
        is_adjusted: false
        inter_budget: null
        name: Consulting project
        description: ""
        amount: 125
        rate: 0
        base: FS
        time_lags: []
        frequency: ""
        frequency_multiplier: 1
        sequence_type: ""
        sequence_term_size: 1
        d: 0
        r: 1
        is_indefinite: false
        external_id: "123456"
        external_source: my-integration
        inter_company: null
        source: null
    ForecastBudget:
      summary: Forecast budget
      value:
        id: 9003
        category:
          id: 1006
          name: Other taxes and fees
          description: ""
        inter_category_name: null
        depending_categories: []
        recurring_budget: null
        settlement_date: "2024-04-08"
        start_date: null
        end_date: null
        account: null
        tags: [my-integration, direct-API]
        integration_tags: null
        is_adjusted: false
        name: Tax prepayment
        description: Created via API
        amount: -78700
        rate: 0
        base: FS
        time_lags: []
        frequency: ""
        frequency_multiplier: 1
        sequence_type: ""
        sequence_term_size: 1
        d: 0
        r: 1
        is_indefinite: false
        external_id: ext-123457
        external_source: my-integration
        inter_company: null
        source: null
        inter_budget: null
        base_categories: []
    InvoiceCreated:
      summary: Invoice
      value:
        id: 3102
        transactions: []
        paid_amount: 0
        amount_due: 7450
        date_created: "2022-04-16T09:53:03.919398Z"
        date_updated: "2022-04-16T09:53:03.919422Z"
        name: Equipment
        notes: Special equipment
        amount: 7450
        type: RE
        reference: AR-007
        date: "2022-04-16"
        due_date: "2022-04-28"
        source: direct_api
        source_id: 00000000-0000-4000-8000-000000000002
        status: PL
        status_label: Expected
        is_archived: false
        category: 1002
    InvoicesUpdated:
      summary: Updated invoices (list)
      value:
        - id: 3102
          transactions: []
          paid_amount: 0
          amount_due: 3000
          date_created: "2022-04-16T09:53:03.919398Z"
          date_updated: "2022-04-16T10:02:11.000000Z"
          name: Equipment
          notes: Special equipment
          amount: 3000
          type: RE
          reference: AR-007
          date: "2022-04-16"
          due_date: "2022-04-28"
          source: direct_api
          source_id: 00000000-0000-4000-8000-000000000002
          status: PL
          status_label: Expected
          is_archived: false
          category: 1002
    InvoicePage:
      summary: Invoice list
      value:
        count: 2
        sum: 8650
        next: null
        previous: null
        results:
          amount_due: 7650
          total: 8650
          invoices:
            - id: 3101
              transactions: []
              paid_amount: 1000
              amount_due: 200
              date_created: "2022-04-16T09:36:37.718400Z"
              date_updated: "2022-04-16T10:12:17.687693Z"
              name: First invoice
              notes: ""
              amount: 1200
              type: RE
              reference: "1"
              date: "2022-04-16"
              due_date: "2022-04-30"
              source: Manually Added
              source_id: 00000000-0000-4000-8000-000000000001
              status: PP
              status_label: Partially Paid
              is_archived: false
              category: 1002
              comment_count: 0
            - id: 3102
              transactions: []
              paid_amount: 0
              amount_due: 7450
              date_created: "2022-04-16T09:53:03.919398Z"
              date_updated: "2022-04-16T09:53:03.919422Z"
              name: Equipment
              notes: Special equipment
              amount: 7450
              type: RE
              reference: AR-007
              date: "2022-04-16"
              due_date: "2022-04-28"
              source: direct_api
              source_id: 00000000-0000-4000-8000-000000000002
              status: PL
              status_label: Expected
              is_archived: false
              category: 1002
              comment_count: 0
    BudgetPage:
      summary: Budget list
      value:
        count: 1
        sum: 125
        next: null
        previous: null
        results:
          total: 125
          budgets:
            - id: 9001
              category:
                id: 1002
                name: Revenue
                description: ""
              inter_category: null
              inter_category_name: null
              depending_categories: []
              recurring_budget: null
              settlement_date: "2023-01-15"
              start_date: null
              end_date: null
              account: null
              tags: []
              integration_tags: null
              is_adjusted: false
              inter_budget: null
              name: Consulting project
              description: ""
              amount: 125
              rate: 0
              base: FS
              time_lags: []
              frequency: ""
              frequency_multiplier: 1
              sequence_type: ""
              sequence_term_size: 1
              d: 0
              r: 1
              is_indefinite: false
              external_id: "123456"
              external_source: my-integration
              inter_company: null
              source: null
    ForecastBudgetPage:
      summary: Forecast budget list
      value:
        count: 1
        sum: -78700
        next: null
        previous: null
        results:
          total: -78700
          budgets:
            - id: 9003
              category:
                id: 1006
                name: Other taxes and fees
                description: ""
              inter_category_name: null
              depending_categories: []
              recurring_budget: null
              settlement_date: "2024-04-08"
              start_date: null
              end_date: null
              account: null
              tags: [my-integration, direct-API]
              integration_tags: null
              is_adjusted: false
              name: Tax prepayment
              description: Created via API
              amount: -78700
              rate: 0
              base: FS
              time_lags: []
              frequency: ""
              frequency_multiplier: 1
              sequence_type: ""
              sequence_term_size: 1
              d: 0
              r: 1
              is_indefinite: false
              external_id: ext-123457
              external_source: my-integration
              inter_company: null
              source: null
              inter_budget: null
              base_categories: []
