{
  "openapi": "3.1.0",
  "info": {
    "title": "Neno Public API",
    "version": "1.0.5",
    "description": "Public REST API for the Neno platform. Workspace-scoped, authenticated with\na bearer API key (`neno_api_live_…` / `neno_api_test_…`). The workspace is derived from\nthe key. A matching `X-Workspace-Id` header is ignored. A different value\nreturns `400 invalid_request` with `param: X-Workspace-Id`.\n\nScope (minimal launch set):\n  * Invoices and draft invoices. A draft has `status=draft` and is available\n    through the `/invoices` status filter.\n  * Quotes with their own `quote_*` status lifecycle and `/quotes` resource.\n  * Tasks — list/poll (the only notification path; notifications themselves\n    are outbound email/push, not a queryable resource), responses, files.\n  * Bills and expenses. Upload bills, then poll them while Neno processes\n    their documents. `/expenses` is a documented alias of `/bills`.\n\nMoney: all monetary amounts are minor-unit strings paired with a separate\n`currencyCode` (ISO-4217).\n\nCompatibility contract: we may add endpoints, response fields, and enum\nvalues without notice. Integrations must ignore unknown fields (tolerant\nreader) and must not assume enums are exhaustive.\n"
  },
  "servers": [
    {
      "url": "https://api.neno.co/public/v1",
      "description": "Live"
    },
    {
      "url": "https://api.neno.build/public/v1",
      "description": "Sandbox (test keys, isolated data plane)"
    }
  ],
  "security": [
    {
      "ApiKeyAuth": []
    }
  ],
  "tags": [
    {
      "name": "Invoices",
      "description": "Invoices and draft invoices."
    },
    {
      "name": "Customers",
      "description": "Invoice customers."
    },
    {
      "name": "Payable Accounts",
      "description": "Accounts that can receive payment for invoices."
    },
    {
      "name": "Quotes",
      "description": "Quotes with their own lifecycle."
    },
    {
      "name": "Tasks",
      "description": "Task polling (notification surface), responses, and file uploads."
    },
    {
      "name": "Bills & Expenses",
      "description": "Upload, list and retrieve bills and expenses."
    }
  ],
  "paths": {
    "/customers": {
      "get": {
        "tags": [
          "Customers"
        ],
        "operationId": "listCustomers",
        "summary": "List customers",
        "parameters": [
          {
            "name": "search",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "email",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "email"
            }
          },
          {
            "name": "vatNumber",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "registrationNumber",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of customers.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Page"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Customer"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      },
      "post": {
        "tags": [
          "Customers"
        ],
        "operationId": "createCustomer",
        "summary": "Create a customer",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CustomerWrite"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Customer"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/customers/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/CustomerId"
        }
      ],
      "get": {
        "tags": [
          "Customers"
        ],
        "operationId": "getCustomer",
        "summary": "Retrieve a customer",
        "responses": {
          "200": {
            "description": "The customer.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Customer"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "tags": [
          "Customers"
        ],
        "operationId": "updateCustomer",
        "summary": "Update a customer",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CustomerUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Customer"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/invoices": {
      "get": {
        "tags": [
          "Invoices"
        ],
        "operationId": "listInvoices",
        "summary": "List invoices (and drafts)",
        "description": "Filterable list of `type=invoice` documents. Pass `status=draft` for\ndraft invoices. `/draft-invoices` is a documented alias of this filter.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/InvoiceStatus"
          },
          {
            "name": "customerId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "issuedAfter",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "issuedBefore",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of invoices.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Page"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/PublicInvoiceRead"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "tags": [
          "Invoices"
        ],
        "operationId": "createInvoice",
        "summary": "Create an invoice (optionally finalize + send in one call)",
        "description": "Creates an invoice. By default it lands as a `draft`. For good DevEx a\nsingle call can also finalize and send it: set `finalize: true` to assign\nthe number, and `send: true` to email the customer (implies finalize).\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InvoiceCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created. `status` reflects whether it was finalized/sent.",
            "headers": {
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InvoiceDocument"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyRequestInProgress"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyKeyReused"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/invoices/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/DocumentId"
        }
      ],
      "get": {
        "tags": [
          "Invoices"
        ],
        "operationId": "getInvoice",
        "summary": "Retrieve an invoice",
        "responses": {
          "200": {
            "description": "The invoice.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicInvoiceRead"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "patch": {
        "tags": [
          "Invoices"
        ],
        "operationId": "updateInvoice",
        "summary": "Update a draft invoice",
        "description": "Only documents in `draft` status may be edited.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DocumentUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated.",
            "headers": {
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InvoiceDocument"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyRequestInProgress"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyKeyReused"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/invoices/{id}/finalize": {
      "parameters": [
        {
          "$ref": "#/components/parameters/DocumentId"
        }
      ],
      "post": {
        "tags": [
          "Invoices"
        ],
        "operationId": "finalizeInvoice",
        "summary": "Finalize a draft (assign number)",
        "description": "Transitions a draft to `finalized` and assigns `invoiceNumber`.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "Finalized.",
            "headers": {
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InvoiceDocument"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyRequestInProgress"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyKeyReused"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/invoices/{id}/send": {
      "parameters": [
        {
          "$ref": "#/components/parameters/DocumentId"
        }
      ],
      "post": {
        "tags": [
          "Invoices"
        ],
        "operationId": "sendInvoice",
        "summary": "Send an invoice to the customer",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SendRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Sent.",
            "headers": {
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InvoiceDocument"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyRequestInProgress"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyKeyReused"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/invoices/{id}/pdf": {
      "parameters": [
        {
          "$ref": "#/components/parameters/DocumentId"
        }
      ],
      "get": {
        "tags": [
          "Invoices"
        ],
        "operationId": "downloadInvoicePdf",
        "summary": "Download the invoice PDF",
        "responses": {
          "200": {
            "description": "The rendered invoice PDF.",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/draft-invoices": {
      "get": {
        "tags": [
          "Invoices"
        ],
        "operationId": "listDraftInvoices",
        "summary": "List draft invoices",
        "description": "Documented alias of `GET /invoices?status=draft`.",
        "parameters": [
          {
            "name": "customerId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "issuedAfter",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "issuedBefore",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of draft invoices.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Page"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/PublicInvoiceRead"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/payable-accounts": {
      "get": {
        "tags": [
          "Payable Accounts"
        ],
        "operationId": "listPayableAccounts",
        "summary": "List accounts that can receive invoice payments",
        "description": "Every returned `id` can be supplied as `payableAccountId` when creating an invoice.",
        "responses": {
          "200": {
            "description": "The workspace accounts that can receive invoice payments.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PayableAccountsResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/quotes": {
      "get": {
        "tags": [
          "Quotes"
        ],
        "operationId": "listQuotes",
        "summary": "List quotes",
        "parameters": [
          {
            "$ref": "#/components/parameters/QuoteStatus"
          },
          {
            "name": "customerId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of quotes.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Page"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/QuoteDocument"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "tags": [
          "Quotes"
        ],
        "operationId": "createQuote",
        "summary": "Create a quote",
        "description": "Creates a quote in `quote_draft` status.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DocumentCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created (quote_draft).",
            "headers": {
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QuoteDocument"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyRequestInProgress"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyKeyReused"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/quotes/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/DocumentId"
        }
      ],
      "get": {
        "tags": [
          "Quotes"
        ],
        "operationId": "getQuote",
        "summary": "Retrieve a quote",
        "responses": {
          "200": {
            "description": "The quote.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QuoteDocument"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "patch": {
        "tags": [
          "Quotes"
        ],
        "operationId": "updateQuote",
        "summary": "Update a draft quote",
        "description": "Editable while in `quote_draft`.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DocumentUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated.",
            "headers": {
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QuoteDocument"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyRequestInProgress"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyKeyReused"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/quotes/{id}/send": {
      "parameters": [
        {
          "$ref": "#/components/parameters/DocumentId"
        }
      ],
      "post": {
        "tags": [
          "Quotes"
        ],
        "operationId": "sendQuote",
        "summary": "Send a quote to the customer",
        "description": "Transitions `quote_draft` to `quote_sent`.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SendRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Sent.",
            "headers": {
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QuoteDocument"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyRequestInProgress"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyKeyReused"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/quotes/{id}/status": {
      "parameters": [
        {
          "$ref": "#/components/parameters/DocumentId"
        }
      ],
      "post": {
        "tags": [
          "Quotes"
        ],
        "operationId": "updateQuoteStatus",
        "summary": "Record a quote decision (accept / decline / expire)",
        "description": "Records the customer decision. Accept or decline only from `quote_sent`.\nExpire from `quote_sent` or `quote_accepted`.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "status": {
                    "type": "string",
                    "enum": [
                      "quote_accepted",
                      "quote_declined",
                      "quote_expired"
                    ]
                  },
                  "reason": {
                    "type": "string"
                  }
                },
                "required": [
                  "status"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated quote.",
            "headers": {
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QuoteDocument"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyRequestInProgress"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyKeyReused"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/quotes/{id}/convert": {
      "parameters": [
        {
          "$ref": "#/components/parameters/DocumentId"
        }
      ],
      "post": {
        "tags": [
          "Quotes"
        ],
        "operationId": "convertQuoteToInvoice",
        "summary": "Convert an accepted quote into an invoice",
        "description": "Requires the quote to be in `quote_accepted`. Creates a NEW invoice draft\n(its own provisional number) copying customer/issuer/payment/items/taxes,\nflips the quote to `quote_converted`, and sets its `convertedInvoiceId`.\nRejects attempts to convert the quote more than once.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "201": {
            "description": "The newly created invoice draft.",
            "headers": {
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InvoiceDocument"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyRequestInProgress"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyKeyReused"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/tasks": {
      "get": {
        "tags": [
          "Tasks"
        ],
        "operationId": "listTasks",
        "summary": "List tasks",
        "description": "Lists tasks (e.g. filter `?status=open`). Note: task notifications are\ndelivered out-of-band (email + push) — there is no notification/unread\nresource, so this list is the read path.\n",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/TaskStatus"
            }
          },
          {
            "name": "source",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/TaskSource"
            }
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of tasks.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Page"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Task"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/tasks/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/TaskId"
        }
      ],
      "get": {
        "tags": [
          "Tasks"
        ],
        "operationId": "getTask",
        "summary": "Retrieve a task",
        "responses": {
          "200": {
            "description": "The task.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Task"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/tasks/{id}/response": {
      "parameters": [
        {
          "$ref": "#/components/parameters/TaskId"
        }
      ],
      "patch": {
        "tags": [
          "Tasks"
        ],
        "operationId": "respondToTask",
        "summary": "Submit a task response",
        "description": "For `boolean` tasks send `{ acknowledged: true }`; for `text_input`\ntasks send `{ text }` (1–10000 chars).\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TaskResponseData"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated task.",
            "headers": {
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Task"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyRequestInProgress"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyKeyReused"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/tasks/{id}/files": {
      "parameters": [
        {
          "$ref": "#/components/parameters/TaskId"
        }
      ],
      "post": {
        "tags": [
          "Tasks"
        ],
        "operationId": "uploadTaskFiles",
        "summary": "Upload files to a task",
        "description": "Multipart upload. Limits (enforced server-side): ≤10 files, ≤8 MB each,\ncontent types: application/pdf, image/jpeg, image/png, image/gif,\nimage/webp, image/heic, image/heif. Bytes proxy through the API.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "files": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "binary"
                    }
                  }
                },
                "required": [
                  "files"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Files attached; returns the updated task.",
            "headers": {
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Task"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyRequestInProgress"
          },
          "413": {
            "description": "File too large (>8 MB).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "Unsupported content type.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyKeyReused"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/tasks/{id}/files/{fileId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/TaskId"
        },
        {
          "name": "fileId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "tags": [
          "Tasks"
        ],
        "operationId": "downloadTaskFile",
        "summary": "Download a task file",
        "responses": {
          "200": {
            "description": "The file bytes.",
            "content": {
              "application/octet-stream": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "tags": [
          "Tasks"
        ],
        "operationId": "deleteTaskFile",
        "summary": "Delete a task file",
        "responses": {
          "204": {
            "description": "Deleted."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/tasks/{id}/messages": {
      "parameters": [
        {
          "$ref": "#/components/parameters/TaskId"
        }
      ],
      "post": {
        "tags": [
          "Tasks"
        ],
        "operationId": "postTaskMessage",
        "summary": "Post a message on a task",
        "description": "Body may be empty only if a file is attached.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "body": {
                    "type": "string"
                  },
                  "taskFileId": {
                    "type": "string",
                    "format": "uuid"
                  }
                },
                "required": [
                  "body"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created message.",
            "headers": {
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaskMessage"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyRequestInProgress"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyKeyReused"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/bills": {
      "get": {
        "tags": [
          "Bills & Expenses"
        ],
        "operationId": "listBills",
        "summary": "List bills / expenses",
        "description": "`/expenses` is an alias of this endpoint. Optionally narrow by `docType`.\n\nRequires the `bills:read` scope. Returns all bills in the workspace.\n",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/BillCustomerStatus"
            }
          },
          {
            "name": "processingStatus",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/BillProcessingStatus"
            }
          },
          {
            "name": "docType",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/BillDocType"
            }
          },
          {
            "name": "supplierSearch",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "dueAfter",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "dueBefore",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of bills.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Page"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Bill"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "tags": [
          "Bills & Expenses"
        ],
        "operationId": "createBill",
        "summary": "Upload a bill or expense",
        "description": "Upload one PDF, JPEG, PNG, HEIC or HEIF file up to 10 MB. The file's\nmagic bytes must match its type. Neno detects whether it is an invoice,\nreceipt or credit note. Do not send a document type. HEIC and HEIF\nuploads are stored and downloaded as JPEG files.\n\nRequires the `bills:write` scope. Duplicate file content in the same\nworkspace returns the existing bill with `duplicate: true`.\n\nNew bills start with `processingStatus=PROCESSING`. Poll\n`GET /bills/{id}` until `processingStatus` is `REVIEWED` or `FAILED`.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/BillUpload"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "A matching bill already exists. `duplicate` is `true`.",
            "headers": {
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillDuplicate"
                }
              }
            }
          },
          "201": {
            "description": "Uploaded and queued for processing.",
            "headers": {
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillCreated"
                }
              }
            }
          },
          "400": {
            "description": "Invalid multipart form.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyRequestInProgress"
          },
          "413": {
            "description": "File exceeds the 10 MB limit.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "Unsupported file type or file bytes do not match the declared type.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyKeyReused"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/bills/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/BillId"
        }
      ],
      "get": {
        "tags": [
          "Bills & Expenses"
        ],
        "operationId": "getBill",
        "summary": "Retrieve a bill or expense",
        "description": "Requires the `bills:read` scope.",
        "responses": {
          "200": {
            "description": "The bill, including its VAT, lines and line-level ledger accounts when available.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillDetail"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/bills/{id}/document": {
      "parameters": [
        {
          "$ref": "#/components/parameters/BillId"
        }
      ],
      "get": {
        "tags": [
          "Bills & Expenses"
        ],
        "operationId": "downloadBillDocument",
        "summary": "Download the original bill file",
        "description": "Requires the `bills:read` scope. HEIC and HEIF uploads are stored and downloaded as JPEG files.",
        "responses": {
          "200": {
            "description": "The uploaded document file.",
            "headers": {
              "Content-Disposition": {
                "description": "Attachment filename for the downloaded document.",
                "schema": {
                  "type": "string",
                  "example": "attachment; filename=\"invoice.pdf\""
                }
              }
            },
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "image/jpeg": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "image/png": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/expenses": {
      "get": {
        "tags": [
          "Bills & Expenses"
        ],
        "operationId": "listExpenses",
        "summary": "List expenses (alias of /bills)",
        "description": "Documented alias of `GET /bills`. It has the same parameters and response\nshape. Requires the `bills:read` scope. See `GET /bills`.\n",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/BillCustomerStatus"
            }
          },
          {
            "name": "processingStatus",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/BillProcessingStatus"
            }
          },
          {
            "name": "docType",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/BillDocType"
            }
          },
          {
            "name": "supplierSearch",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "dueAfter",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "dueBefore",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of bills / expenses.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Page"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Bill"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "tags": [
          "Bills & Expenses"
        ],
        "operationId": "createExpense",
        "summary": "Upload an expense (alias of /bills)",
        "description": "Documented alias of `POST /bills`. It requires `bills:write` and has the same form, responses and duplicate detection.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/BillUpload"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "A matching bill already exists. `duplicate` is `true`.",
            "headers": {
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillDuplicate"
                }
              }
            }
          },
          "201": {
            "description": "Uploaded and queued for processing.",
            "headers": {
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillCreated"
                }
              }
            }
          },
          "400": {
            "description": "Invalid multipart form.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyRequestInProgress"
          },
          "413": {
            "description": "File exceeds the 10 MB limit.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "Unsupported file type or file bytes do not match the declared type.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyKeyReused"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/expenses/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/BillId"
        }
      ],
      "get": {
        "tags": [
          "Bills & Expenses"
        ],
        "operationId": "getExpense",
        "summary": "Retrieve an expense (alias of /bills/{id})",
        "description": "Documented alias of `GET /bills/{id}`. Requires the `bills:read` scope.",
        "responses": {
          "200": {
            "description": "The bill or expense.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillDetail"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/expenses/{id}/document": {
      "parameters": [
        {
          "$ref": "#/components/parameters/BillId"
        }
      ],
      "get": {
        "tags": [
          "Bills & Expenses"
        ],
        "operationId": "downloadExpenseDocument",
        "summary": "Download the original expense file",
        "description": "Documented alias of `GET /bills/{id}/document`. Requires the `bills:read` scope. HEIC and HEIF uploads are stored and downloaded as JPEG files.",
        "responses": {
          "200": {
            "description": "The uploaded document file.",
            "headers": {
              "Content-Disposition": {
                "description": "Attachment filename for the downloaded document.",
                "schema": {
                  "type": "string",
                  "example": "attachment; filename=\"invoice.pdf\""
                }
              }
            },
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "image/jpeg": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "image/png": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "neno_api_live_… / neno_api_test_…",
        "description": "Workspace-scoped API key sent as `Authorization: Bearer neno_api_live_…` or `neno_api_test_…`."
      }
    },
    "parameters": {
      "Limit": {
        "name": "limit",
        "in": "query",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 100,
          "default": 25
        }
      },
      "Cursor": {
        "name": "cursor",
        "in": "query",
        "description": "UUID cursor from a previous page's `nextCursor`.",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": true,
        "description": "Client-generated key that identifies one write for 24 hours. Reuse it only when retrying the same method, path, query, and body.",
        "schema": {
          "type": "string",
          "minLength": 1,
          "maxLength": 255
        }
      },
      "DocumentId": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "Public document UUID. This is distinct from the customer-facing share token.\n",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "BillId": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "Bill UUID.",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "TaskId": {
        "name": "id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "CustomerId": {
        "name": "id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "InvoiceStatus": {
        "name": "status",
        "in": "query",
        "schema": {
          "$ref": "#/components/schemas/InvoiceStatus"
        }
      },
      "QuoteStatus": {
        "name": "status",
        "in": "query",
        "schema": {
          "$ref": "#/components/schemas/QuoteStatus"
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Missing, invalid, revoked, or expired API key.",
        "headers": {
          "WWW-Authenticate": {
            "schema": {
              "type": "string"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/AuthenticationError"
            }
          }
        }
      },
      "Forbidden": {
        "description": "The API key lacks a required scope or the creator lacks a required permission.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/PermissionError"
            }
          }
        }
      },
      "NotFound": {
        "description": "Resource not found (or not in this workspace).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "BadRequest": {
        "description": "Malformed request.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "IdempotencyRequestInProgress": {
        "description": "A matching request with this Idempotency-Key is still in progress. Retry after one second.",
        "headers": {
          "Retry-After": {
            "schema": {
              "type": "integer",
              "example": 1
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/IdempotencyError"
            }
          }
        }
      },
      "IdempotencyKeyReused": {
        "description": "This Idempotency-Key was already used for a different request.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/IdempotencyError"
            }
          }
        }
      },
      "Conflict": {
        "description": "State conflict.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "ValidationError": {
        "description": "Request failed validation.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Rate limit exceeded.",
        "headers": {
          "Retry-After": {
            "schema": {
              "type": "integer"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "schemas": {
      "AuthenticationError": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Error"
          },
          {
            "type": "object",
            "properties": {
              "error": {
                "properties": {
                  "type": {
                    "type": "string",
                    "enum": [
                      "authentication_error"
                    ]
                  },
                  "code": {
                    "type": "string",
                    "enum": [
                      "missing_api_key",
                      "invalid_api_key"
                    ]
                  }
                }
              }
            }
          }
        ]
      },
      "PermissionError": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Error"
          },
          {
            "type": "object",
            "properties": {
              "error": {
                "properties": {
                  "type": {
                    "type": "string",
                    "enum": [
                      "permission_error"
                    ]
                  },
                  "code": {
                    "type": "string",
                    "enum": [
                      "insufficient_scope",
                      "insufficient_permissions"
                    ]
                  }
                }
              }
            }
          }
        ]
      },
      "InvoiceStatus": {
        "type": "string",
        "description": "Invoice status values. Non-exhaustive.",
        "enum": [
          "draft",
          "pending_review",
          "approved",
          "finalized",
          "sent",
          "paid",
          "cancelled"
        ]
      },
      "QuoteStatus": {
        "type": "string",
        "description": "Quote status values. Non-exhaustive.",
        "enum": [
          "quote_draft",
          "quote_sent",
          "quote_accepted",
          "quote_declined",
          "quote_expired",
          "quote_converted"
        ]
      },
      "PayableAccount": {
        "type": "object",
        "description": "An account that can receive payment for an invoice.",
        "properties": {
          "id": {
            "type": "string",
            "description": "Supply this value as `payableAccountId` when creating an invoice."
          },
          "source": {
            "type": "string",
            "enum": [
              "manual",
              "open_banking"
            ]
          },
          "accountHolderName": {
            "type": "string",
            "nullable": true
          },
          "iban": {
            "type": "string",
            "description": "Full IBAN printed on the invoice payment block."
          },
          "bic": {
            "type": "string",
            "nullable": true
          },
          "bankName": {
            "type": "string",
            "nullable": true
          },
          "currencyCode": {
            "type": "string",
            "nullable": true,
            "description": "ISO-4217."
          },
          "isPrimary": {
            "type": "boolean",
            "description": "Whether invoice creation selects this account when `payableAccountId` is omitted."
          }
        },
        "required": [
          "id",
          "source",
          "accountHolderName",
          "iban",
          "bic",
          "bankName",
          "currencyCode",
          "isPrimary"
        ]
      },
      "PayableAccountsResponse": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PayableAccount"
            }
          }
        },
        "required": [
          "data"
        ]
      },
      "LineItem": {
        "type": "object",
        "description": "All monetary and numeric values are strings (minor units for amounts).",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "lineNo": {
            "type": "integer"
          },
          "description": {
            "type": "string"
          },
          "details": {
            "type": "string",
            "nullable": true
          },
          "unit": {
            "type": "string",
            "nullable": true
          },
          "quantity": {
            "type": "string"
          },
          "unitPriceMinor": {
            "type": "string"
          },
          "taxRatePercent": {
            "type": "string"
          },
          "discountPercent": {
            "type": "string",
            "nullable": true
          },
          "lineDiscountMinor": {
            "type": "string",
            "nullable": true
          },
          "lineGrossMinor": {
            "type": "string"
          },
          "lineSubtotalMinor": {
            "type": "string"
          },
          "lineTaxMinor": {
            "type": "string"
          },
          "lineTotalMinor": {
            "type": "string"
          }
        },
        "required": [
          "description",
          "quantity",
          "unitPriceMinor",
          "lineTotalMinor"
        ]
      },
      "TaxLine": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "ratePercent": {
            "type": "string"
          },
          "amountMinor": {
            "type": "string"
          },
          "discountMinor": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "OutboundDocument": {
        "type": "object",
        "description": "Public shape shared by invoices and quotes.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Public document UUID. This is distinct from the customer share token."
          },
          "type": {
            "type": "string",
            "enum": [
              "invoice",
              "quote",
              "credit_note"
            ]
          },
          "invoiceNumber": {
            "type": "string",
            "nullable": true,
            "description": "Assigned on finalize/finalization."
          },
          "language": {
            "type": "string",
            "nullable": true
          },
          "templateStyle": {
            "type": "string",
            "nullable": true
          },
          "customerName": {
            "type": "string"
          },
          "customerContactPerson": {
            "type": "string",
            "nullable": true
          },
          "customerAddressLine1": {
            "type": "string",
            "nullable": true
          },
          "customerAddressLine2": {
            "type": "string",
            "nullable": true
          },
          "customerCity": {
            "type": "string",
            "nullable": true
          },
          "customerPostalCode": {
            "type": "string",
            "nullable": true
          },
          "customerCountryCode": {
            "type": "string",
            "nullable": true
          },
          "customerVatNumber": {
            "type": "string",
            "nullable": true
          },
          "currencyCode": {
            "type": "string",
            "description": "ISO-4217",
            "example": "EUR"
          },
          "netAmountMinor": {
            "type": "string"
          },
          "taxTotalMinor": {
            "type": "string"
          },
          "totalMinor": {
            "type": "string"
          },
          "discountPercent": {
            "type": "string",
            "nullable": true
          },
          "discountMinor": {
            "type": "string",
            "nullable": true
          },
          "issueDate": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "dueDate": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "paidAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "poNumber": {
            "type": "string",
            "nullable": true
          },
          "notes": {
            "type": "string",
            "nullable": true
          },
          "terms": {
            "type": "string",
            "nullable": true
          },
          "logoUrl": {
            "type": "string",
            "nullable": true
          },
          "lineItems": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LineItem"
            }
          },
          "taxes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TaxLine"
            }
          }
        },
        "required": [
          "id",
          "type",
          "customerName",
          "currencyCode",
          "totalMinor"
        ]
      },
      "InvoiceDocument": {
        "allOf": [
          {
            "$ref": "#/components/schemas/OutboundDocument"
          },
          {
            "type": "object",
            "properties": {
              "status": {
                "$ref": "#/components/schemas/InvoiceStatus"
              },
              "creditedInvoiceId": {
                "type": "string",
                "nullable": true,
                "description": "Public handle of the invoice this credit note credits (credit\nnotes only). Same id-space as `id` — pass it directly to\n`GET /invoices/{id}`. (The internal FK is never exposed.)\n"
              },
              "creditedInvoiceNumber": {
                "type": "string",
                "nullable": true,
                "description": "Human-readable number of the credited invoice, for display."
              }
            },
            "required": [
              "status"
            ]
          }
        ]
      },
      "PublicInvoiceReadLineItem": {
        "type": "object",
        "description": "A line item on a public invoice read. Internal line identifiers are omitted.",
        "properties": {
          "lineNo": {
            "type": "integer"
          },
          "description": {
            "type": "string"
          },
          "details": {
            "type": "string",
            "nullable": true
          },
          "unit": {
            "type": "string",
            "nullable": true
          },
          "quantity": {
            "type": "string"
          },
          "unitPriceMinor": {
            "type": "string"
          },
          "taxRatePercent": {
            "type": "string",
            "nullable": true,
            "description": "Null on a reverse-charge document",
            "whose lines carry no VAT rate.": null
          },
          "discountPercent": {
            "type": "string",
            "nullable": true
          },
          "lineDiscountMinor": {
            "type": "string"
          },
          "lineGrossMinor": {
            "type": "string"
          },
          "lineSubtotalMinor": {
            "type": "string"
          },
          "lineTaxMinor": {
            "type": "string"
          },
          "lineTotalMinor": {
            "type": "string"
          }
        },
        "required": [
          "lineNo",
          "description",
          "quantity",
          "unitPriceMinor",
          "lineTotalMinor"
        ]
      },
      "PublicInvoiceReadTaxLine": {
        "type": "object",
        "description": "A tax line on a public invoice read. Internal tax identifiers are omitted.",
        "properties": {
          "name": {
            "type": "string"
          },
          "ratePercent": {
            "type": "string"
          },
          "amountMinor": {
            "type": "string"
          },
          "discountMinor": {
            "type": "string"
          }
        },
        "required": [
          "name",
          "ratePercent",
          "amountMinor",
          "discountMinor"
        ]
      },
      "PublicInvoiceRead": {
        "type": "object",
        "description": "Read-only public shape for an invoice. Credit notes are not exposed by these endpoints.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Invoice UUID, distinct from the customer share token."
          },
          "type": {
            "type": "string",
            "enum": [
              "invoice"
            ]
          },
          "status": {
            "$ref": "#/components/schemas/InvoiceStatus"
          },
          "invoiceNumber": {
            "type": "string",
            "nullable": true
          },
          "language": {
            "type": "string",
            "nullable": true
          },
          "templateStyle": {
            "type": "string",
            "nullable": true
          },
          "customerName": {
            "type": "string"
          },
          "customerContactPerson": {
            "type": "string",
            "nullable": true
          },
          "customerAddressLine1": {
            "type": "string",
            "nullable": true
          },
          "customerAddressLine2": {
            "type": "string",
            "nullable": true
          },
          "customerCity": {
            "type": "string",
            "nullable": true
          },
          "customerPostalCode": {
            "type": "string",
            "nullable": true
          },
          "customerCountryCode": {
            "type": "string",
            "nullable": true
          },
          "customerVatNumber": {
            "type": "string",
            "nullable": true
          },
          "currencyCode": {
            "type": "string"
          },
          "netAmountMinor": {
            "type": "string"
          },
          "taxTotalMinor": {
            "type": "string"
          },
          "totalMinor": {
            "type": "string"
          },
          "discountPercent": {
            "type": "string",
            "nullable": true
          },
          "discountMinor": {
            "type": "string"
          },
          "reverseCharge": {
            "type": "boolean",
            "description": "Reverse charge (BTW verlegd): the customer accounts for the VAT, and the document charges none."
          },
          "issueDate": {
            "type": "string",
            "format": "date"
          },
          "dueDate": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "paidAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "poNumber": {
            "type": "string",
            "nullable": true
          },
          "notes": {
            "type": "string",
            "nullable": true
          },
          "terms": {
            "type": "string",
            "nullable": true
          },
          "logoUrl": {
            "type": "string",
            "nullable": true
          },
          "lineItems": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PublicInvoiceReadLineItem"
            }
          },
          "taxes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PublicInvoiceReadTaxLine"
            }
          }
        },
        "required": [
          "id",
          "type",
          "status",
          "customerName",
          "currencyCode",
          "netAmountMinor",
          "taxTotalMinor",
          "totalMinor",
          "issueDate",
          "lineItems",
          "taxes"
        ]
      },
      "QuoteDocument": {
        "allOf": [
          {
            "$ref": "#/components/schemas/OutboundDocument"
          },
          {
            "type": "object",
            "properties": {
              "status": {
                "$ref": "#/components/schemas/QuoteStatus"
              },
              "validUntil": {
                "type": "string",
                "format": "date",
                "nullable": true,
                "description": "Quote expiry."
              },
              "convertedInvoiceId": {
                "type": "string",
                "nullable": true,
                "description": "Public handle of the invoice this quote was converted into, if\nany. Same id-space as `id` — pass it directly to\n`GET /invoices/{id}`. (The internal FK is never exposed.)\n"
              },
              "convertedInvoiceNumber": {
                "type": "string",
                "nullable": true,
                "description": "Human-readable number of the resulting invoice, for display."
              }
            },
            "required": [
              "status"
            ]
          }
        ]
      },
      "CustomerAddress": {
        "type": "object",
        "properties": {
          "line1": {
            "type": "string"
          },
          "line2": {
            "type": "string"
          },
          "city": {
            "type": "string"
          },
          "postalCode": {
            "type": "string"
          },
          "countryCode": {
            "type": "string",
            "minLength": 2,
            "maxLength": 2
          }
        }
      },
      "CustomerWrite": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "email": {
            "type": "string",
            "format": "email",
            "nullable": true
          },
          "contactPerson": {
            "type": "string",
            "nullable": true
          },
          "address": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CustomerAddress"
              }
            ],
            "nullable": true
          },
          "vatNumber": {
            "type": "string",
            "nullable": true
          },
          "registrationNumber": {
            "type": "string",
            "nullable": true
          }
        },
        "required": [
          "name"
        ]
      },
      "CustomerUpdate": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "email": {
            "type": "string",
            "format": "email",
            "nullable": true
          },
          "contactPerson": {
            "type": "string",
            "nullable": true
          },
          "address": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CustomerAddress"
              }
            ],
            "nullable": true
          },
          "vatNumber": {
            "type": "string",
            "nullable": true
          },
          "registrationNumber": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "Customer": {
        "allOf": [
          {
            "$ref": "#/components/schemas/CustomerWrite"
          },
          {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "createdAt": {
                "type": "string",
                "format": "date-time"
              },
              "updatedAt": {
                "type": "string",
                "format": "date-time"
              }
            },
            "required": [
              "id",
              "name",
              "createdAt",
              "updatedAt"
            ]
          }
        ]
      },
      "LineItemInput": {
        "type": "object",
        "properties": {
          "description": {
            "type": "string"
          },
          "details": {
            "type": "string"
          },
          "unit": {
            "type": "string"
          },
          "quantity": {
            "type": "string",
            "description": "Decimal as string."
          },
          "unitPriceMinor": {
            "type": "string",
            "description": "Minor units as string."
          },
          "taxRatePercent": {
            "type": "string"
          },
          "discountPercent": {
            "type": "string"
          }
        },
        "required": [
          "description",
          "quantity",
          "unitPriceMinor"
        ]
      },
      "DocumentCreate": {
        "type": "object",
        "properties": {
          "customerId": {
            "type": "string",
            "format": "uuid"
          },
          "currencyCode": {
            "type": "string",
            "example": "EUR"
          },
          "lineItems": {
            "type": "array",
            "minItems": 1,
            "items": {
              "$ref": "#/components/schemas/LineItemInput"
            }
          },
          "issueDate": {
            "type": "string",
            "format": "date"
          },
          "dueDate": {
            "type": "string",
            "format": "date"
          },
          "validUntil": {
            "type": "string",
            "format": "date",
            "description": "Quotes only."
          },
          "poNumber": {
            "type": "string"
          },
          "notes": {
            "type": "string"
          },
          "terms": {
            "type": "string"
          }
        },
        "required": [
          "customerId",
          "lineItems"
        ]
      },
      "InvoiceCreate": {
        "allOf": [
          {
            "$ref": "#/components/schemas/DocumentCreate"
          },
          {
            "type": "object",
            "properties": {
              "finalize": {
                "type": "boolean",
                "default": false,
                "description": "If true, finalize on create (assign `invoiceNumber`)."
              },
              "send": {
                "type": "boolean",
                "default": false,
                "description": "If true, email the customer on create (implies finalize)."
              },
              "sendTo": {
                "type": "array",
                "items": {
                  "type": "string",
                  "format": "email"
                },
                "description": "Recipients when `send` is true; defaults to the customer."
              },
              "sendMessage": {
                "type": "string",
                "description": "Optional message body when `send` is true."
              }
            }
          }
        ]
      },
      "DocumentUpdate": {
        "type": "object",
        "description": "All fields optional; only supplied fields change. Drafts only.",
        "properties": {
          "customerId": {
            "type": "string",
            "format": "uuid"
          },
          "currencyCode": {
            "type": "string"
          },
          "lineItems": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LineItemInput"
            }
          },
          "issueDate": {
            "type": "string",
            "format": "date"
          },
          "dueDate": {
            "type": "string",
            "format": "date"
          },
          "validUntil": {
            "type": "string",
            "format": "date"
          },
          "poNumber": {
            "type": "string"
          },
          "notes": {
            "type": "string"
          },
          "terms": {
            "type": "string"
          }
        }
      },
      "SendRequest": {
        "type": "object",
        "properties": {
          "to": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "email"
            }
          },
          "message": {
            "type": "string"
          }
        }
      },
      "TaskType": {
        "type": "string",
        "enum": [
          "file_upload",
          "boolean",
          "text_input"
        ]
      },
      "TaskStatus": {
        "type": "string",
        "description": "Computed task status. Non-exhaustive.",
        "enum": [
          "open",
          "pending",
          "approved",
          "rejected",
          "completed",
          "replied",
          "awaiting_response"
        ]
      },
      "TaskSource": {
        "type": "string",
        "enum": [
          "accountant",
          "vat"
        ]
      },
      "TaskFile": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "taskId": {
            "type": "string",
            "format": "uuid"
          },
          "filename": {
            "type": "string"
          },
          "contentType": {
            "type": "string"
          },
          "byteSize": {
            "type": "integer"
          },
          "authorRole": {
            "type": "string",
            "enum": [
              "accountant",
              "user"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "filename",
          "contentType"
        ]
      },
      "TaskMessage": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "taskId": {
            "type": "string",
            "format": "uuid"
          },
          "authorRole": {
            "type": "string",
            "enum": [
              "accountant",
              "user"
            ]
          },
          "body": {
            "type": "string"
          },
          "taskFileId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "body"
        ]
      },
      "Task": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "source": {
            "$ref": "#/components/schemas/TaskSource"
          },
          "key": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "type": {
            "$ref": "#/components/schemas/TaskType"
          },
          "status": {
            "allOf": [
              {
                "$ref": "#/components/schemas/TaskStatus"
              }
            ],
            "description": "Computed from status updates (denormalized as `latestStatus`)."
          },
          "responseData": {
            "$ref": "#/components/schemas/TaskResponseData"
          },
          "files": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TaskFile"
            }
          },
          "submittedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "completedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "type",
          "status",
          "createdAt"
        ]
      },
      "TaskResponseData": {
        "description": "`{ acknowledged: true }` for boolean tasks; `{ text }` for text_input tasks.",
        "oneOf": [
          {
            "type": "object",
            "properties": {
              "acknowledged": {
                "type": "boolean",
                "enum": [
                  true
                ]
              }
            },
            "required": [
              "acknowledged"
            ]
          },
          {
            "type": "object",
            "properties": {
              "text": {
                "type": "string",
                "minLength": 1,
                "maxLength": 10000
              }
            },
            "required": [
              "text"
            ]
          }
        ]
      },
      "BillCustomerStatus": {
        "type": "string",
        "description": "Customer-facing bill status.",
        "enum": [
          "NEEDS_YOUR_ATTENTION",
          "WAITING_FOR_ACCOUNTANT",
          "BOOKED",
          "DUPLICATE"
        ]
      },
      "BillProcessingStatus": {
        "type": "string",
        "description": "Processing state for the uploaded document.",
        "enum": [
          "PROCESSING",
          "NEEDS_REVIEW",
          "REVIEWED",
          "FAILED"
        ],
        "example": "REVIEWED"
      },
      "BillDocType": {
        "type": "string",
        "enum": [
          "INVOICE",
          "RECEIPT",
          "CREDIT_NOTE",
          "UNKNOWN"
        ]
      },
      "Bill": {
        "type": "object",
        "description": "A bill or expense document (one model). Money is string minor units.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "vendor": {
            "type": [
              "string",
              "null"
            ],
            "example": "Coolblue"
          },
          "supplierName": {
            "type": [
              "string",
              "null"
            ],
            "description": "Normalized supplier name. Null until `processingStatus` is `REVIEWED`.",
            "example": "Coolblue B.V."
          },
          "documentNumber": {
            "type": [
              "string",
              "null"
            ],
            "description": "Supplier document number. Null until `processingStatus` is `REVIEWED`.",
            "example": "CB-2026-884120"
          },
          "status": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "NEEDS_YOUR_ATTENTION",
              "WAITING_FOR_ACCOUNTANT",
              "BOOKED",
              "DUPLICATE",
              null
            ],
            "description": "Customer-facing status. Null while Neno processes the bill."
          },
          "processingStatus": {
            "allOf": [
              {
                "$ref": "#/components/schemas/BillProcessingStatus"
              }
            ],
            "example": "REVIEWED"
          },
          "docType": {
            "$ref": "#/components/schemas/BillDocType"
          },
          "currencyCode": {
            "type": [
              "string",
              "null"
            ],
            "description": "ISO-4217 currency code. Null when review established amounts without a currency.",
            "example": "EUR"
          },
          "netAmountMinor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Net amount in minor units. Null until `processingStatus` is `REVIEWED`.",
            "example": "82562"
          },
          "taxTotalMinor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Tax amount in minor units. Null until `processingStatus` is `REVIEWED`.",
            "example": "17338"
          },
          "tipTotalMinor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Tip amount in minor units. Null until `processingStatus` is `REVIEWED`."
          },
          "totalMinor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Total amount in minor units. Null until `processingStatus` is `REVIEWED`.",
            "example": "99900"
          },
          "documentDate": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "dueDate": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "paidAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "example": null
          },
          "bookedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "example": null,
            "description": "When the bill's live general-ledger recognition was booked; null when none is live."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "status",
          "processingStatus",
          "currencyCode",
          "totalMinor",
          "createdAt"
        ]
      },
      "BillUploadResult": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Bill"
          },
          {
            "type": "object",
            "properties": {
              "duplicate": {
                "type": "boolean",
                "description": "True when this upload matched an existing bill's file content."
              }
            },
            "required": [
              "duplicate"
            ]
          }
        ]
      },
      "BillCreated": {
        "allOf": [
          {
            "$ref": "#/components/schemas/BillUploadResult"
          },
          {
            "type": "object",
            "properties": {
              "duplicate": {
                "const": false
              },
              "processingStatus": {
                "const": "PROCESSING"
              },
              "status": {
                "type": "null",
                "const": null
              },
              "supplierName": {
                "type": "null",
                "const": null
              },
              "documentNumber": {
                "type": "null",
                "const": null
              },
              "netAmountMinor": {
                "type": "null",
                "const": null
              },
              "taxTotalMinor": {
                "type": "null",
                "const": null
              },
              "tipTotalMinor": {
                "type": "null",
                "const": null
              },
              "totalMinor": {
                "type": "null",
                "const": null
              }
            }
          }
        ]
      },
      "BillDuplicate": {
        "allOf": [
          {
            "$ref": "#/components/schemas/BillUploadResult"
          },
          {
            "type": "object",
            "properties": {
              "duplicate": {
                "const": true
              }
            }
          }
        ]
      },
      "BillUpload": {
        "type": "object",
        "properties": {
          "file": {
            "type": "string",
            "format": "binary",
            "description": "PDF, JPEG, PNG, HEIC or HEIF file, up to 10 MB. HEIC and HEIF files are stored as JPEG."
          },
          "note": {
            "type": "string",
            "maxLength": 1000,
            "example": "Receipt for client meeting travel."
          }
        },
        "required": [
          "file"
        ]
      },
      "BillLineItem": {
        "type": "object",
        "properties": {
          "description": {
            "type": "string",
            "example": "Train tickets for client meeting"
          },
          "quantity": {
            "type": [
              "string",
              "null"
            ],
            "description": "Quantity. Null until `processingStatus` is `REVIEWED`.",
            "example": "1"
          },
          "unitPriceMinor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Unit price in minor units. Null until `processingStatus` is `REVIEWED`.",
            "example": "4840"
          },
          "taxRatePercent": {
            "type": [
              "string",
              "null"
            ],
            "description": "Tax rate as a percentage. Null until `processingStatus` is `REVIEWED`.",
            "example": "9"
          },
          "totalMinor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Line total in minor units. Null until `processingStatus` is `REVIEWED`.",
            "example": "4840"
          },
          "ledgerAccount": {
            "$ref": "#/components/schemas/LedgerAccount"
          }
        },
        "required": [
          "description",
          "totalMinor"
        ]
      },
      "BillTaxLine": {
        "type": "object",
        "properties": {
          "ratePercent": {
            "type": [
              "string",
              "null"
            ],
            "description": "Tax rate as a percentage. Null until `processingStatus` is `REVIEWED`.",
            "example": "21"
          },
          "netAmountMinor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Not populated in v1."
          },
          "taxAmountMinor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Tax amount in minor units. Null until `processingStatus` is `REVIEWED`.",
            "example": "17338"
          }
        },
        "required": [
          "ratePercent",
          "taxAmountMinor"
        ]
      },
      "LedgerAccount": {
        "type": [
          "object",
          "null"
        ],
        "properties": {
          "code": {
            "type": "string",
            "example": "4500"
          },
          "name": {
            "type": "string",
            "example": "Travel expenses"
          }
        },
        "required": [
          "code",
          "name"
        ]
      },
      "BillDetail": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Bill"
          },
          {
            "type": "object",
            "properties": {
              "lineItems": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/BillLineItem"
                }
              },
              "taxes": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/BillTaxLine"
                }
              }
            }
          }
        ]
      },
      "Page": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {}
          },
          "nextCursor": {
            "type": "string",
            "nullable": true,
            "description": "Cursor for the next page; null when there are no more."
          },
          "hasMore": {
            "type": "boolean"
          }
        },
        "required": [
          "data",
          "hasMore"
        ]
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "example": "invalid_request"
              },
              "message": {
                "type": "string"
              },
              "code": {
                "type": "string"
              },
              "param": {
                "type": "string"
              }
            },
            "required": [
              "type",
              "message"
            ]
          }
        },
        "required": [
          "error"
        ]
      },
      "IdempotencyError": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Error"
          },
          {
            "type": "object",
            "properties": {
              "error": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "enum": [
                      "idempotency_error"
                    ]
                  },
                  "code": {
                    "type": "string",
                    "enum": [
                      "idempotency_key_reused",
                      "idempotency_request_in_progress",
                      "idempotency_response_too_large"
                    ]
                  }
                }
              }
            }
          }
        ]
      }
    },
    "headers": {
      "IdempotentReplayed": {
        "description": "Present with value `true` when the response was replayed for an Idempotency-Key retry.",
        "schema": {
          "type": "string",
          "enum": [
            "true"
          ]
        }
      }
    }
  }
}