{
  "openapi": "3.1.0",
  "info": {
    "title": "Essentio Public API",
    "version": "1",
    "summary": "Read and change one Business's Clients, documents, Payments and Products.",
    "description": "The Public API's contract (ADR-0015). Every request acts for exactly one Business, the one the credential belongs to, with the Role the credential carries. Within v1 this file only grows: fields, endpoints and codes may be added; nothing is renamed or removed. A response field the server sends is declared here, and the test suite validates every Public API response against this file."
  },
  "servers": [
    { "url": "https://api.essentio.pro" }
  ],
  "security": [
    { "apiKey": [] }
  ],
  "paths": {
    "/v1/clients": {
      "get": {
        "operationId": "listClients",
        "summary": "List Clients",
        "description": "The Business's Clients, a page at a time, oldest first. Any Role may read them. To walk them all, ask again with each page's `next_cursor` until it is null: every Client that exists from the first page to the last is met exactly once, whatever is created or deleted in between.",
        "tags": ["Clients"],
        "parameters": [
          { "$ref": "#/components/parameters/Limit" },
          { "$ref": "#/components/parameters/Cursor" },
          {
            "name": "search",
            "in": "query",
            "required": false,
            "description": "Only the Clients whose name, legal name, email, VAT number or account code contains this text, in any case. Send the same `search` with every page.",
            "schema": { "type": "string" }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of the Business's Clients.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ClientList" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthenticated" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "422": { "$ref": "#/components/responses/InvalidRequest" }
        }
      },
      "post": {
        "operationId": "createClient",
        "summary": "Create a Client",
        "description": "Creates a Client of the Business, held to the same rules as the Client form in Essentio: a blank `name` takes the `legal_name`, and a Client with neither is refused. With no `currency` the Client takes the Business's; with no `payment_terms`, 30 days; with no `account_code`, the next one of the Business's pattern. A VAT number is checked with VIES. An Admin's or Member's key may create; a Viewer's may not.",
        "tags": ["Clients"],
        "parameters": [
          { "$ref": "#/components/parameters/IdempotencyKey" }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/ClientCreate" }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The Client created — or, for a retry under the same `Idempotency-Key`, the Client the first request created.",
            "headers": {
              "Idempotent-Replayed": { "$ref": "#/components/headers/IdempotentReplayed" }
            },
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Client" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthenticated" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "422": { "$ref": "#/components/responses/InvalidRequest" }
        }
      }
    },
    "/v1/clients/{client}": {
      "parameters": [
        {
          "name": "client",
          "in": "path",
          "required": true,
          "description": "The Client's `id`.",
          "schema": { "type": "integer" }
        }
      ],
      "get": {
        "operationId": "getClient",
        "summary": "Retrieve a Client",
        "description": "One Client of the Business. Any Role may read it. A Client of another Business is not found.",
        "tags": ["Clients"],
        "responses": {
          "200": {
            "description": "The Client.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Client" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthenticated" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      },
      "patch": {
        "operationId": "updateClient",
        "summary": "Update a Client",
        "description": "Changes the fields sent and nothing else, held to the same rules as the Client form in Essentio: a blank `name` takes the `legal_name`, a Client left with neither is refused, a blank `account_code` keeps the code the Client has, and `currency` and `payment_terms` may be changed but not cleared. A changed VAT number or country is checked with VIES again. An Admin's or Member's key may update; a Viewer's may not.",
        "tags": ["Clients"],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/ClientUpdate" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The Client as it now stands.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Client" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthenticated" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "422": { "$ref": "#/components/responses/InvalidRequest" }
        }
      }
    },
    "/v1/reports/income": {
      "get": {
        "operationId": "getIncomeReport",
        "summary": "Income",
        "description": "Income for a period: the net amounts of the Reportable documents issued in it, paid or not, a Credit Note subtracting and a standalone Proforma adding only its paid share, with the VAT beside it (a Proforma carries none). Stated in the Business's reporting currency; documents in other currencies are counted in `other_currency_documents`, never summed in. The figure the dashboard's Income tab shows for the same period. Any Role may read it.",
        "tags": ["Reports"],
        "parameters": [
          { "name": "from", "in": "query", "required": true, "description": "The period's first day, YYYY-MM-DD.", "schema": { "type": "string", "format": "date" } },
          { "name": "to", "in": "query", "required": true, "description": "The period's last day, YYYY-MM-DD, not before `from`.", "schema": { "type": "string", "format": "date" } }
        ],
        "responses": {
          "200": {
            "description": "The period's Income.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/IncomeReport" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthenticated" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "422": { "$ref": "#/components/responses/InvalidPeriod" }
        }
      }
    },
    "/v1/reports/vat": {
      "get": {
        "operationId": "getReportedVatReport",
        "summary": "Reported VAT",
        "description": "Reported VAT for a period: the VAT of the Invoices and Credit Notes issued in it, in full, paid or not, a Credit Note subtracting, split by VAT category and rate. A Proforma carries none. Stated in the Business's reporting currency; documents in other currencies are counted in `other_currency_documents`, never summed in. The figure the dashboard's VAT tab shows for the same month. Any Role may read it.",
        "tags": ["Reports"],
        "parameters": [
          { "name": "from", "in": "query", "required": true, "description": "The period's first day, YYYY-MM-DD.", "schema": { "type": "string", "format": "date" } },
          { "name": "to", "in": "query", "required": true, "description": "The period's last day, YYYY-MM-DD, not before `from`.", "schema": { "type": "string", "format": "date" } }
        ],
        "responses": {
          "200": {
            "description": "The period's Reported VAT.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/VatReport" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthenticated" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "422": { "$ref": "#/components/responses/InvalidPeriod" }
        }
      }
    },
    "/v1/reports/overdue": {
      "get": {
        "operationId": "getOverdueReport",
        "summary": "Overdue",
        "description": "What Clients owe late, as of today: the Overdue documents — issued Invoices and Proformas never converted, past their due date, neither paid nor cancelled — in the Business's reporting currency, their Amount due, and the same by Client, largest first. Overdue documents in other currencies are counted in `other_currency_documents`, never summed in. The dashboard's Overdue figures. Any Role may read it.",
        "tags": ["Reports"],
        "responses": {
          "200": {
            "description": "What is Overdue today.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/OverdueReport" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthenticated" },
          "403": { "$ref": "#/components/responses/Forbidden" }
        }
      }
    },
    "/v1/reports/statement": {
      "get": {
        "operationId": "getStatement",
        "summary": "Statement",
        "description": "A Client's Statement for a period: the balance brought forward from the day before it began, every document (on its issue date) and every Payment (on its payment date) dated in the period, whichever document a Payment settles, and the balance on the period's last day — one block per currency, never summed across currencies. A Credit Note is a credit. The Statement of Account the Business prints for the same period. Any Role may read it.",
        "tags": ["Reports"],
        "parameters": [
          { "name": "client_id", "in": "query", "required": true, "description": "The Client's id.", "schema": { "type": "integer" } },
          { "name": "from", "in": "query", "required": true, "description": "The period's first day, YYYY-MM-DD.", "schema": { "type": "string", "format": "date" } },
          { "name": "to", "in": "query", "required": true, "description": "The period's last day, YYYY-MM-DD, not before `from`.", "schema": { "type": "string", "format": "date" } }
        ],
        "responses": {
          "200": {
            "description": "The Client's Statement.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Statement" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthenticated" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": {
            "description": "The Business has no Client with this id.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          },
          "422": { "$ref": "#/components/responses/InvalidPeriod" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "An API key an Owner or Admin created in the Business's Settings → API keys, sent as `Authorization: Bearer <key>`. The key decides the Business and the Role; no request names a Business."
      }
    },
    "parameters": {
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": false,
        "description": "Any string of 1 to 255 printable characters with no space that names this request, such as a UUID or your order id. The request is performed once: sent again with the same key and the same body within 24 hours, it answers what the first one answered and creates nothing. Sent with another body, it is refused (`idempotency`). A request that was refused or failed keeps no key, so it can be corrected and sent again under it. A key belongs to the API key that sent it.",
        "schema": { "type": "string", "minLength": 1, "maxLength": 255, "pattern": "^[\\x21-\\x7E]+$" }
      },
      "Limit": {
        "name": "limit",
        "in": "query",
        "required": false,
        "description": "How many records a page holds: 1 to 100, 25 when not sent.",
        "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 25 }
      },
      "Cursor": {
        "name": "cursor",
        "in": "query",
        "required": false,
        "description": "The `next_cursor` of the page before. Not sent: the first page. A cursor this API did not give is refused.",
        "schema": { "type": "string" }
      }
    },
    "headers": {
      "IdempotentReplayed": {
        "description": "`true` when this is the answer to an earlier request with the same `Idempotency-Key`, sent again; absent otherwise.",
        "schema": { "type": "string", "enum": ["true"] }
      }
    },
    "responses": {
      "Unauthenticated": {
        "description": "No API key, an unknown one, or a revoked one.",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" }
          }
        }
      },
      "Forbidden": {
        "description": "The key's Role may not do this. A permission failure, never a Refusal.",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" }
          }
        }
      },
      "NotFound": {
        "description": "Nothing of the Business's has this id.",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" }
          }
        }
      },
      "InvalidRequest": {
        "description": "The request cannot be answered as sent: `error.errors` names each field (or parameter, or header) the rules refused, in the words Essentio's own form uses. Or its `Idempotency-Key` was sent with another request (`idempotency`).",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" }
          }
        }
      },
      "InvalidPeriod": {
        "description": "`from` or `to` is missing, is not a day written as YYYY-MM-DD, or `to` is before `from`.",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" }
          }
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": ["error"],
        "properties": {
          "error": {
            "type": "object",
            "required": ["type", "message"],
            "properties": {
              "type": {
                "description": "`authentication`: the request carries no valid credential (401). `permission`: the credential's Role may not do this (403). `not_found`: no such endpoint or record (404). `invalid_request`: any other request the API cannot answer as sent. `server_error`: a fault on Essentio's side (5xx). `idempotency`: an `Idempotency-Key` sent again with another request than the first one under it; nothing was performed (422).",
                "type": "string",
                "enum": ["authentication", "permission", "not_found", "invalid_request", "server_error", "idempotency"]
              },
              "message": {
                "description": "What went wrong, in words for a person.",
                "type": "string"
              },
              "errors": {
                "description": "Sent with a 422 whose fields the rules refused: each field at fault, with what is wrong with it.",
                "type": "object",
                "additionalProperties": {
                  "type": "array",
                  "items": { "type": "string" }
                }
              }
            }
          }
        }
      },
      "ClientList": {
        "type": "object",
        "required": ["data", "next_cursor"],
        "properties": {
          "data": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/Client" }
          },
          "next_cursor": { "$ref": "#/components/schemas/NextCursor" }
        }
      },
      "NextCursor": {
        "type": ["string", "null"],
        "description": "Where the next page starts: pass it back as `cursor`. Null on the last page. Opaque: its content may change, so a program never builds or reads one."
      },
      "Client": {
        "type": "object",
        "required": [
          "id", "name", "legal_name", "contact_person", "email", "phone", "address", "city",
          "postal_code", "country", "vat_number", "vat_verified", "account_code", "currency",
          "payment_terms", "is_active", "created_at", "updated_at",
          "credit_limit", "notes", "preferred_bank_account_ids"
        ],
        "properties": {
          "id": { "type": "integer" },
          "name": { "type": "string", "description": "The name the Business knows the Client by." },
          "legal_name": { "type": ["string", "null"] },
          "contact_person": { "type": ["string", "null"] },
          "email": { "type": ["string", "null"] },
          "phone": { "type": ["string", "null"] },
          "address": { "type": ["string", "null"] },
          "city": { "type": ["string", "null"] },
          "postal_code": { "type": ["string", "null"] },
          "country": { "type": "string", "description": "ISO 3166-1 alpha-2.", "pattern": "^[A-Z]{2}$" },
          "vat_number": { "type": ["string", "null"] },
          "vat_verified": { "type": "boolean", "description": "Whether VIES confirmed the VAT number." },
          "account_code": { "type": ["string", "null"], "description": "The Client's account code in the Business's books." },
          "currency": { "type": "string", "description": "ISO 4217.", "pattern": "^[A-Z]{3}$" },
          "payment_terms": { "type": "integer", "minimum": 0, "description": "Days a document is due after it is issued." },
          "is_active": { "type": "boolean" },
          "created_at": { "type": "string", "format": "date-time" },
          "updated_at": { "type": "string", "format": "date-time" },
          "credit_limit": { "oneOf": [{ "$ref": "#/components/schemas/Amount" }, { "type": "null" }], "description": "How much the Business lets the Client owe, in the Client's currency, as an exact decimal string (`1500.00`); null when none is set." },
          "notes": { "type": ["string", "null"], "description": "The Business's own notes about the Client." },
          "preferred_bank_account_ids": {
            "type": "array",
            "items": { "type": "integer" },
            "description": "The Business's bank accounts this Client pays into, by `id`, ascending; empty when none is preferred. The same ids a document's `bank_account_ids` takes."
          }
        }
      },
      "ClientCreate": {
        "type": "object",
        "description": "A new Client. `email` and `country` are required, and a `name` or a `legal_name`: a blank `name` takes the `legal_name`. A field not sent, or sent empty, is left blank, except those that take a default.",
        "required": ["email", "country"],
        "properties": {
          "name": { "type": ["string", "null"], "maxLength": 255, "description": "Required unless `legal_name` is sent; blank, it takes the `legal_name`." },
          "legal_name": { "type": ["string", "null"], "maxLength": 255 },
          "contact_person": { "type": ["string", "null"], "maxLength": 255 },
          "email": { "type": "string", "format": "email", "maxLength": 255 },
          "phone": { "type": ["string", "null"], "maxLength": 50 },
          "address": { "type": ["string", "null"] },
          "city": { "type": ["string", "null"], "maxLength": 100 },
          "postal_code": { "type": ["string", "null"], "maxLength": 20 },
          "country": { "type": "string", "description": "ISO 3166-1 alpha-2.", "minLength": 2, "maxLength": 2 },
          "vat_number": { "type": ["string", "null"], "description": "Checked with VIES when it is created; `vat_verified` says whether VIES confirmed it." },
          "account_code": { "type": ["string", "null"], "maxLength": 100, "description": "The Client's account code in the Business's books. Not sent or blank: the next code of the Business's pattern. A code another Client holds is refused." },
          "currency": { "type": ["string", "null"], "description": "ISO 4217, one Essentio computes documents in. Not sent or blank: the Business's currency." },
          "payment_terms": { "type": ["integer", "null"], "minimum": 0, "description": "Days a document is due after it is issued. Not sent or blank: 30." },
          "credit_limit": { "type": ["string", "null"], "description": "How much the Business lets the Client owe, in the Client's currency: a decimal string such as `1500.00`, at least 0, stored to two decimals. Send a string, never a float. Null clears it." },
          "notes": { "type": ["string", "null"], "description": "The Business's own notes about the Client. Null clears them." },
          "preferred_bank_account_ids": {
            "type": ["array", "null"],
            "items": { "type": "integer" },
            "description": "The Business's bank accounts this Client pays into, by `id`, each one of the Business's own (another Business's is refused at its index, `preferred_bank_account_ids.0`). A list replaces them, `[]` clears them; null or not sent keeps them."
          }
        }
      },
      "ClientUpdate": {
        "type": "object",
        "description": "The fields to change; a field not sent keeps its value. Each is held to what `ClientCreate` holds it to.",
        "properties": {
          "name": { "type": ["string", "null"], "maxLength": 255, "description": "Blank, it takes the `legal_name`; send the `legal_name` with it." },
          "legal_name": { "type": ["string", "null"], "maxLength": 255 },
          "contact_person": { "type": ["string", "null"], "maxLength": 255 },
          "email": { "type": "string", "format": "email", "maxLength": 255 },
          "phone": { "type": ["string", "null"], "maxLength": 50 },
          "address": { "type": ["string", "null"] },
          "city": { "type": ["string", "null"], "maxLength": 100 },
          "postal_code": { "type": ["string", "null"], "maxLength": 20 },
          "country": { "type": "string", "minLength": 2, "maxLength": 2 },
          "vat_number": { "type": ["string", "null"], "description": "Cleared, the Client's VAT number is removed and nothing is asked of VIES." },
          "account_code": { "type": ["string", "null"], "maxLength": 100, "description": "Blank keeps the code the Client has." },
          "currency": { "type": "string", "description": "May be changed, not cleared." },
          "payment_terms": { "type": "integer", "minimum": 0, "description": "May be changed, not cleared." },
          "is_active": { "type": "boolean" },
          "credit_limit": { "type": ["string", "null"], "description": "How much the Business lets the Client owe, in the Client's currency: a decimal string such as `1500.00`, at least 0, stored to two decimals. Send a string, never a float. Null clears it." },
          "notes": { "type": ["string", "null"], "description": "The Business's own notes about the Client. Null clears them." },
          "preferred_bank_account_ids": {
            "type": ["array", "null"],
            "items": { "type": "integer" },
            "description": "The Business's bank accounts this Client pays into, by `id`, each one of the Business's own (another Business's is refused at its index, `preferred_bank_account_ids.0`). A list replaces them, `[]` clears them; null or not sent keeps them."
          }
        }
      },
      "Amount": {
        "description": "An exact decimal amount, as a string at its currency's scale (`1190.00`, `-57.00`): never a float.",
        "type": "string",
        "pattern": "^-?[0-9]+(\\.[0-9]+)?$"
      },
      "IncomeReport": {
        "type": "object",
        "required": ["currency", "from", "to", "income", "vat", "other_currency_documents"],
        "properties": {
          "currency": { "type": "string", "description": "The Business's reporting currency, ISO 4217.", "pattern": "^[A-Z]{3}$" },
          "from": { "type": "string", "format": "date" },
          "to": { "type": "string", "format": "date" },
          "income": { "$ref": "#/components/schemas/Amount", "description": "Net of VAT." },
          "vat": { "$ref": "#/components/schemas/Amount", "description": "The VAT of the same documents, Proformas left out." },
          "other_currency_documents": { "type": "integer", "minimum": 0, "description": "Reportable documents issued in the period in another currency, left out of both figures." }
        }
      },
      "VatReport": {
        "type": "object",
        "required": ["currency", "from", "to", "net", "vat", "bands", "other_currency_documents"],
        "properties": {
          "currency": { "type": "string", "description": "The Business's reporting currency, ISO 4217.", "pattern": "^[A-Z]{3}$" },
          "from": { "type": "string", "format": "date" },
          "to": { "type": "string", "format": "date" },
          "net": { "$ref": "#/components/schemas/Amount", "description": "The net amount the VAT is on." },
          "vat": { "$ref": "#/components/schemas/Amount" },
          "bands": {
            "description": "The same figures split by VAT category and rate, highest rate first; each 0% category is a band of its own.",
            "type": "array",
            "items": { "$ref": "#/components/schemas/VatBand" }
          },
          "other_currency_documents": { "type": "integer", "minimum": 0, "description": "Invoices and Credit Notes issued in the period in another currency, left out of every figure." }
        }
      },
      "VatBand": {
        "type": "object",
        "required": ["category", "rate", "net", "vat"],
        "properties": {
          "category": { "type": "string", "description": "The EN16931 VAT category code: `S` standard, `Z` zero rated, `E` exempt, `AE` reverse charge, `K` intra-EU, `G` export, `O` outside scope." },
          "rate": { "type": "string", "description": "The rate in percent, to two decimals: `5.50`.", "pattern": "^[0-9]+\\.[0-9]{2}$" },
          "net": { "$ref": "#/components/schemas/Amount" },
          "vat": { "$ref": "#/components/schemas/Amount" }
        }
      },
      "OverdueReport": {
        "type": "object",
        "required": ["currency", "as_of", "count", "amount_due", "clients", "other_currency_documents"],
        "properties": {
          "currency": { "type": "string", "description": "The Business's reporting currency, ISO 4217.", "pattern": "^[A-Z]{3}$" },
          "as_of": { "type": "string", "format": "date", "description": "The day, in UTC, the documents were judged Overdue on." },
          "count": { "type": "integer", "minimum": 0, "description": "Overdue documents in the reporting currency." },
          "amount_due": { "$ref": "#/components/schemas/Amount", "description": "Their Amount due: Total less what has been paid." },
          "clients": {
            "description": "The same by Client, largest Amount due first.",
            "type": "array",
            "items": { "$ref": "#/components/schemas/OverdueClient" }
          },
          "other_currency_documents": { "type": "integer", "minimum": 0, "description": "Overdue documents in another currency, left out of `count` and `amount_due`." }
        }
      },
      "OverdueClient": {
        "type": "object",
        "required": ["client_id", "name", "documents", "amount_due"],
        "properties": {
          "client_id": { "type": "integer" },
          "name": { "type": "string" },
          "documents": { "type": "integer", "minimum": 1, "description": "The Client's Overdue documents." },
          "amount_due": { "$ref": "#/components/schemas/Amount" }
        }
      },
      "Statement": {
        "type": "object",
        "required": ["client_id", "from", "to", "currencies"],
        "properties": {
          "client_id": { "type": "integer" },
          "from": { "type": "string", "format": "date" },
          "to": { "type": "string", "format": "date" },
          "currencies": {
            "description": "One block per currency with an entry in the period or a balance brought into it, the currency with most documents first.",
            "type": "array",
            "items": { "$ref": "#/components/schemas/StatementCurrency" }
          }
        }
      },
      "StatementCurrency": {
        "type": "object",
        "required": ["currency", "opening_balance", "closing_balance", "lines"],
        "properties": {
          "currency": { "type": "string", "description": "ISO 4217.", "pattern": "^[A-Z]{3}$" },
          "opening_balance": { "$ref": "#/components/schemas/Amount", "description": "What the Client owed the day before the period began; negative when the Business owes the Client." },
          "closing_balance": { "$ref": "#/components/schemas/Amount", "description": "What the Client owed on the period's last day; negative when the Business owes the Client." },
          "lines": {
            "description": "Every entry dated in the period, by date.",
            "type": "array",
            "items": { "$ref": "#/components/schemas/StatementLine" }
          }
        }
      },
      "StatementLine": {
        "type": "object",
        "required": ["date", "kind", "document_type", "number", "description", "debit", "credit", "balance", "prepaid"],
        "properties": {
          "date": { "type": "string", "format": "date", "description": "A document's issue date, or a Payment's payment date." },
          "kind": { "type": "string", "enum": ["document", "payment"] },
          "document_type": { "type": ["string", "null"], "description": "`invoice`, `proforma` or `credit_note` on a document's line; null on a Payment's." },
          "number": { "type": "string", "description": "The document's number, or the Payment's reference (`-` when it has none)." },
          "description": { "type": "string", "description": "A document's one Line, or how many it has; a Payment's method." },
          "debit": { "oneOf": [{ "$ref": "#/components/schemas/Amount" }, { "type": "null" }], "description": "What the entry adds to the balance: a document other than a Credit Note." },
          "credit": { "oneOf": [{ "$ref": "#/components/schemas/Amount" }, { "type": "null" }], "description": "What the entry takes off the balance: a Payment or a Credit Note." },
          "balance": { "$ref": "#/components/schemas/Amount", "description": "The running balance after the entry." },
          "prepaid": { "type": "boolean", "description": "A Payment made on the Proforma the Invoice was converted from." }
        }
      }
    }
  }
}
